From b2d5e134655f857ee99be5ffb4f3538d663ee403 Mon Sep 17 00:00:00 2001 From: David Sangrey Date: Fri, 17 Nov 2023 11:33:26 -0500 Subject: [PATCH] [2051] Update Supporting Files Not touching any of the big ones, but some clarification updates to many of the supporting files. --- collate.py | 4 ++-- debug_webserver.py | 15 ++++++------- journal_lock.py | 18 +++++++++++----- myNotebook.py | 19 +++++++++-------- theme.py | 53 ++++++++++++++++++++++++---------------------- timeout_session.py | 24 +++++++++++++-------- update.py | 41 ++++++++++++++++++++--------------- 7 files changed, 99 insertions(+), 75 deletions(-) diff --git a/collate.py b/collate.py index 480075279..caf5994f7 100755 --- a/collate.py +++ b/collate.py @@ -79,7 +79,7 @@ def addcommodities(data) -> None: # noqa: CCR001 commodities[key] = new - if not len(commodities) > size_pre: + if len(commodities) <= size_pre: return if isfile(commodityfile): @@ -227,7 +227,7 @@ def addships(data) -> None: # noqa: CCR001 print('Not docked!') continue - elif not data.get('lastStarport'): + if not data.get('lastStarport'): print('No starport!') continue diff --git a/debug_webserver.py b/debug_webserver.py index 48f473a4d..87f7eaed0 100644 --- a/debug_webserver.py +++ b/debug_webserver.py @@ -1,4 +1,6 @@ """Simple HTTP listener to be used with debugging various EDMC sends.""" +from __future__ import annotations + import gzip import json import pathlib @@ -6,7 +8,7 @@ import threading import zlib from http import server -from typing import Any, Callable, Literal, Tuple, Union +from typing import Any, Callable, Literal from urllib.parse import parse_qs from config import appname @@ -22,9 +24,6 @@ class LoggingHandler(server.BaseHTTPRequestHandler): """HTTP Handler implementation that logs to EDMCs logger and writes data to files on disk.""" - def __init__(self, request, client_address: Tuple[str, int], server) -> None: - super().__init__(request, client_address, server) - def log_message(self, format: str, *args: Any) -> None: """Override default handler logger with EDMC logger.""" logger.info(format % args) @@ -45,7 +44,7 @@ def do_POST(self) -> None: # noqa: N802 # I cant change it elif len(target_path) == 1 and target_path[0] == '/': target_path = 'WEB_ROOT' - response: Union[Callable[[str], str], str, None] = DEFAULT_RESPONSES.get(target_path) + response: Callable[[str], str] | str | None = DEFAULT_RESPONSES.get(target_path) if callable(response): response = response(to_save) @@ -69,11 +68,11 @@ def do_POST(self) -> None: # noqa: N802 # I cant change it target_file = output_data_path / (safe_file_name(target_path) + '.log') if target_file.parent != output_data_path: logger.warning(f"REFUSING TO WRITE FILE THAT ISN'T IN THE RIGHT PLACE! {target_file=}") - logger.warning(f'DATA FOLLOWS\n{data}') # type: ignore # mypy thinks data is a byte string here + logger.warning(f'DATA FOLLOWS\n{data}') return - with output_lock, target_file.open('a') as f: - f.write(to_save + "\n\n") + with output_lock, target_file.open('a') as file: + file.write(to_save + "\n\n") @staticmethod def get_printable(data: bytes, compression: Literal['deflate'] | Literal['gzip'] | str | None = None) -> str: diff --git a/journal_lock.py b/journal_lock.py index 7945ef914..b6f816b98 100644 --- a/journal_lock.py +++ b/journal_lock.py @@ -1,4 +1,11 @@ -"""Implements locking of Journal directory.""" +""" +journal_lock.py - Locking of the Journal Directory. + +Copyright (c) EDCD, All Rights Reserved +Licensed under the GNU General Public License. +See LICENSE file. +""" +from __future__ import annotations import pathlib import sys @@ -6,7 +13,7 @@ from enum import Enum from os import getpid as os_getpid from tkinter import ttk -from typing import TYPE_CHECKING, Callable, Optional +from typing import TYPE_CHECKING, Callable from config import config from EDMCLogging import get_main_logger @@ -34,9 +41,9 @@ class JournalLock: def __init__(self) -> None: """Initialise where the journal directory and lock file are.""" self.journal_dir: str | None = config.get_str('journaldir') or config.default_journal_dir - self.journal_dir_path: Optional[pathlib.Path] = None + self.journal_dir_path: pathlib.Path | None = None self.set_path_from_journaldir() - self.journal_dir_lockfile_name: Optional[pathlib.Path] = None + self.journal_dir_lockfile_name: pathlib.Path | None = None # We never test truthiness of this, so let it be defined when first assigned. Avoids type hint issues. # self.journal_dir_lockfile: Optional[IO] = None self.locked = False @@ -58,7 +65,8 @@ def open_journal_dir_lockfile(self) -> bool: self.journal_dir_lockfile_name = self.journal_dir_path / 'edmc-journal-lock.txt' # type: ignore logger.trace_if('journal-lock', f'journal_dir_lockfile_name = {self.journal_dir_lockfile_name!r}') try: - self.journal_dir_lockfile = open(self.journal_dir_lockfile_name, mode='w+', encoding='utf-8') + with open(self.journal_dir_lockfile_name, mode='w+', encoding='utf-8') as lockfile: + self.journal_dir_lockfile = lockfile # Linux CIFS read-only mount throws: OSError(30, 'Read-only file system') # Linux no-write-perm directory throws: PermissionError(13, 'Permission denied') diff --git a/myNotebook.py b/myNotebook.py index 30f95274a..6fa357746 100644 --- a/myNotebook.py +++ b/myNotebook.py @@ -10,10 +10,11 @@ Entire file may be imported by plugins. """ +from __future__ import annotations + import sys import tkinter as tk from tkinter import ttk -from typing import Optional # Can't do this with styles on OSX - http://www.tkdocs.com/tutorial/styles.html#whydifficult if sys.platform == 'darwin': @@ -29,7 +30,7 @@ class Notebook(ttk.Notebook): """Custom ttk.Notebook class to fix some display issues.""" - def __init__(self, master: Optional[ttk.Frame] = None, **kw): + def __init__(self, master: ttk.Frame | None = None, **kw): ttk.Notebook.__init__(self, master, **kw) style = ttk.Style() @@ -75,9 +76,9 @@ def __init__(self, master: ttk.Notebook | None = None, **kw): class Label(tk.Label): """Custom tk.Label class to fix some display issues.""" - def __init__(self, master: Optional[ttk.Frame] = None, **kw): + def __init__(self, master: ttk.Frame | None = None, **kw): # This format chosen over `sys.platform in (...)` as mypy and friends dont understand that - if sys.platform == 'darwin' or sys.platform == 'win32': + if sys.platform in ('darwin', 'win32'): kw['foreground'] = kw.pop('foreground', PAGEFG) kw['background'] = kw.pop('background', PAGEBG) else: @@ -89,7 +90,7 @@ def __init__(self, master: Optional[ttk.Frame] = None, **kw): class Entry(sys.platform == 'darwin' and tk.Entry or ttk.Entry): # type: ignore """Custom t(t)k.Entry class to fix some display issues.""" - def __init__(self, master: Optional[ttk.Frame] = None, **kw): + def __init__(self, master: ttk.Frame | None = None, **kw): if sys.platform == 'darwin': kw['highlightbackground'] = kw.pop('highlightbackground', PAGEBG) tk.Entry.__init__(self, master, **kw) @@ -100,7 +101,7 @@ def __init__(self, master: Optional[ttk.Frame] = None, **kw): class Button(sys.platform == 'darwin' and tk.Button or ttk.Button): # type: ignore """Custom t(t)k.Button class to fix some display issues.""" - def __init__(self, master: Optional[ttk.Frame] = None, **kw): + def __init__(self, master: ttk.Frame | None = None, **kw): if sys.platform == 'darwin': kw['highlightbackground'] = kw.pop('highlightbackground', PAGEBG) tk.Button.__init__(self, master, **kw) @@ -113,7 +114,7 @@ def __init__(self, master: Optional[ttk.Frame] = None, **kw): class ColoredButton(sys.platform == 'darwin' and tk.Label or tk.Button): # type: ignore """Custom t(t)k.ColoredButton class to fix some display issues.""" - def __init__(self, master: Optional[ttk.Frame] = None, **kw): + def __init__(self, master: ttk.Frame | None = None, **kw): if sys.platform == 'darwin': # Can't set Button background on OSX, so use a Label instead kw['relief'] = kw.pop('relief', tk.RAISED) @@ -131,7 +132,7 @@ def _press(self, event): class Checkbutton(sys.platform == 'darwin' and tk.Checkbutton or ttk.Checkbutton): # type: ignore """Custom t(t)k.Checkbutton class to fix some display issues.""" - def __init__(self, master: Optional[ttk.Frame] = None, **kw): + def __init__(self, master: ttk.Frame | None = None, **kw): if sys.platform == 'darwin': kw['foreground'] = kw.pop('foreground', PAGEFG) kw['background'] = kw.pop('background', PAGEBG) @@ -145,7 +146,7 @@ def __init__(self, master: Optional[ttk.Frame] = None, **kw): class Radiobutton(sys.platform == 'darwin' and tk.Radiobutton or ttk.Radiobutton): # type: ignore """Custom t(t)k.Radiobutton class to fix some display issues.""" - def __init__(self, master: Optional[ttk.Frame] = None, **kw): + def __init__(self, master: ttk.Frame | None = None, **kw): if sys.platform == 'darwin': kw['foreground'] = kw.pop('foreground', PAGEFG) kw['background'] = kw.pop('background', PAGEBG) diff --git a/theme.py b/theme.py index b5d51484d..bfb76c55d 100644 --- a/theme.py +++ b/theme.py @@ -1,9 +1,14 @@ """ -Theme support. +theme.py - Theme support. + +Copyright (c) EDCD, All Rights Reserved +Licensed under the GNU General Public License. +See LICENSE file. Because of various ttk limitations this app is an unholy mix of Tk and ttk widgets. So can't use ttk's theme support. So have to change colors manually. """ +from __future__ import annotations import os import sys @@ -11,7 +16,7 @@ from os.path import join from tkinter import font as tk_font from tkinter import ttk -from typing import TYPE_CHECKING, Callable, Dict, List, Optional, Set, Tuple +from typing import TYPE_CHECKING, Callable from config import config from EDMCLogging import get_main_logger @@ -36,7 +41,7 @@ def _(x: str) -> str: ... AddFontResourceEx.restypes = [LPCWSTR, DWORD, LPCVOID] # type: ignore FR_PRIVATE = 0x10 FR_NOT_ENUM = 0x20 - AddFontResourceEx(join(config.respath, u'EUROCAPS.TTF'), FR_PRIVATE, 0) + AddFontResourceEx(join(config.respath, 'EUROCAPS.TTF'), FR_PRIVATE, 0) elif sys.platform == 'linux': # pyright: reportUnboundVariable=false @@ -121,7 +126,7 @@ class MotifWmHints(Structure): dpy = None -class _Theme(object): +class _Theme: # Enum ? Remember these are, probably, based on 'value' of a tk # RadioButton set. Looking in prefs.py, they *appear* to be hard-coded @@ -132,18 +137,18 @@ class _Theme(object): def __init__(self) -> None: self.active: int | None = None # Starts out with no theme - self.minwidth: Optional[int] = None - self.widgets: Dict[tk.Widget | tk.BitmapImage, Set] = {} - self.widgets_pair: List = [] - self.defaults: Dict = {} - self.current: Dict = {} + self.minwidth: int | None = None + self.widgets: dict[tk.Widget | tk.BitmapImage, set] = {} + self.widgets_pair: list = [] + self.defaults: dict = {} + self.current: dict = {} self.default_ui_scale: float | None = None # None == not yet known self.startup_ui_scale: int | None = None def register(self, widget: tk.Widget | tk.BitmapImage) -> None: # noqa: CCR001, C901 # Note widget and children for later application of a theme. Note if # the widget has explicit fg or bg attributes. - assert isinstance(widget, tk.Widget) or isinstance(widget, tk.BitmapImage), widget + assert isinstance(widget, (tk.BitmapImage, tk.Widget)), widget if not self.defaults: # Can't initialise this til window is created # Windows, MacOS self.defaults = { @@ -169,14 +174,14 @@ def register(self, widget: tk.Widget | tk.BitmapImage) -> None: # noqa: CCR001, attribs.add('fg') if widget['background'] not in ['', self.defaults['bitmapbg']]: attribs.add('bg') - elif isinstance(widget, tk.Entry) or isinstance(widget, ttk.Entry): + elif isinstance(widget, (tk.Entry, ttk.Entry)): if widget['foreground'] not in ['', self.defaults['entryfg']]: attribs.add('fg') if widget['background'] not in ['', self.defaults['entrybg']]: attribs.add('bg') if 'font' in widget.keys() and str(widget['font']) not in ['', self.defaults['entryfont']]: attribs.add('font') - elif isinstance(widget, tk.Frame) or isinstance(widget, ttk.Frame) or isinstance(widget, tk.Canvas): + elif isinstance(widget, (tk.Canvas, tk.Frame, ttk.Frame)): if ( ('background' in widget.keys() or isinstance(widget, tk.Canvas)) and widget['background'] not in ['', self.defaults['frame']] @@ -200,21 +205,21 @@ def register(self, widget: tk.Widget | tk.BitmapImage) -> None: # noqa: CCR001, attribs.add('font') self.widgets[widget] = attribs - if isinstance(widget, tk.Frame) or isinstance(widget, ttk.Frame): + if isinstance(widget, (tk.Frame, ttk.Frame)): for child in widget.winfo_children(): self.register(child) - def register_alternate(self, pair: Tuple, gridopts: Dict) -> None: + def register_alternate(self, pair: tuple, gridopts: dict) -> None: self.widgets_pair.append((pair, gridopts)) def button_bind( - self, widget: tk.Widget, command: Callable, image: Optional[tk.BitmapImage] = None + self, widget: tk.Widget, command: Callable, image: tk.BitmapImage | None = None ) -> None: widget.bind('', command) widget.bind('', lambda e: self._enter(e, image)) widget.bind('', lambda e: self._leave(e, image)) - def _enter(self, event: tk.Event, image: Optional[tk.BitmapImage]) -> None: + def _enter(self, event: tk.Event, image: tk.BitmapImage | None) -> None: widget = event.widget if widget and widget['state'] != tk.DISABLED: try: @@ -231,7 +236,7 @@ def _enter(self, event: tk.Event, image: Optional[tk.BitmapImage]) -> None: except Exception: logger.exception(f'Failure configuring image: {image=}') - def _leave(self, event: tk.Event, image: Optional[tk.BitmapImage]) -> None: + def _leave(self, event: tk.Event, image: tk.BitmapImage | None) -> None: widget = event.widget if widget and widget['state'] != tk.DISABLED: try: @@ -299,13 +304,13 @@ def update(self, widget: tk.Widget) -> None: Also, register it for future updates. :param widget: Target widget. """ - assert isinstance(widget, tk.Widget) or isinstance(widget, tk.BitmapImage), widget + assert isinstance(widget, (tk.BitmapImage, tk.Widget)), widget if not self.current: return # No need to call this for widgets created in plugin_app() self.register(widget) self._update_widget(widget) - if isinstance(widget, tk.Frame) or isinstance(widget, ttk.Frame): + if isinstance(widget, (tk.Frame, ttk.Frame)): for child in widget.winfo_children(): self._update_widget(child) @@ -314,7 +319,7 @@ def _update_widget(self, widget: tk.Widget | tk.BitmapImage) -> None: # noqa: C if widget not in self.widgets: if isinstance(widget, tk.Widget): w_class = widget.winfo_class() - w_keys: List[str] = widget.keys() + w_keys: list[str] = widget.keys() else: # There is no tk.BitmapImage.winfo_class() @@ -325,7 +330,7 @@ def _update_widget(self, widget: tk.Widget | tk.BitmapImage) -> None: # noqa: C assert_str = f'{w_class} {widget} "{"text" in w_keys and widget["text"]}"' raise AssertionError(assert_str) - attribs: Set = self.widgets.get(widget, set()) + attribs: set = self.widgets.get(widget, set()) try: if isinstance(widget, tk.BitmapImage): @@ -355,7 +360,7 @@ def _update_widget(self, widget: tk.Widget | tk.BitmapImage) -> None: # noqa: C # e.g. tk.Button, tk.Label, tk.Menu if 'fg' not in attribs: widget['foreground'] = self.current['foreground'] - widget['activeforeground'] = self.current['activeforeground'], + widget['activeforeground'] = self.current['activeforeground'] widget['disabledforeground'] = self.current['disabledforeground'] if 'bg' not in attribs: @@ -419,9 +424,7 @@ def apply(self, root: tk.Tk) -> None: # noqa: CCR001, C901 if self.active == theme: return # Don't need to mess with the window manager - - else: - self.active = theme + self.active = theme if sys.platform == 'darwin': from AppKit import NSAppearance, NSApplication, NSMiniaturizableWindowMask, NSResizableWindowMask diff --git a/timeout_session.py b/timeout_session.py index 8a4656ce7..2d0d395f1 100644 --- a/timeout_session.py +++ b/timeout_session.py @@ -1,7 +1,15 @@ -"""A requests.session with a TimeoutAdapter.""" +""" +timeout_session.py - requests session with timeout adapter. + +Copyright (c) EDCD, All Rights Reserved +Licensed under the GNU General Public License. +See LICENSE file. +""" +from __future__ import annotations + import requests +from requests import Session from requests.adapters import HTTPAdapter - from config import user_agent REQUEST_TIMEOUT = 10 # reasonable timeout that all HTTP requests should use @@ -35,11 +43,9 @@ def new_session( :param session: the Session object to attach the Adapter to, defaults to a new session :return: The created Session """ - if session is None: - session = requests.Session() + with Session() as session: session.headers['User-Agent'] = user_agent - - adapter = TimeoutAdapter(timeout) - session.mount("http://", adapter) - session.mount("https://", adapter) - return session + adapter = TimeoutAdapter(timeout) + session.mount("http://", adapter) + session.mount("https://", adapter) + return session diff --git a/update.py b/update.py index 272d217dd..f88daa38c 100644 --- a/update.py +++ b/update.py @@ -1,25 +1,32 @@ -"""Checking for updates to this application.""" +""" +update.py - Checking for Program Updates. + +Copyright (c) EDCD, All Rights Reserved +Licensed under the GNU General Public License. +See LICENSE file. +""" +from __future__ import annotations + import os import sys import threading from os.path import dirname, join from traceback import print_exc -from typing import TYPE_CHECKING, Optional +from typing import TYPE_CHECKING from xml.etree import ElementTree - import requests import semantic_version +from config import appname, appversion_nobuild, config, update_feed +from EDMCLogging import get_main_logger if TYPE_CHECKING: import tkinter as tk -from config import appname, appversion_nobuild, config, update_feed -from EDMCLogging import get_main_logger logger = get_main_logger() -class EDMCVersion(object): +class EDMCVersion: """ Hold all the information about an EDMC version. @@ -39,7 +46,7 @@ def __init__(self, version: str, title: str, sv: semantic_version.base.Version): self.sv: semantic_version.base.Version = sv -class Updater(object): +class Updater: """ Handle checking for updates. @@ -63,16 +70,16 @@ def use_internal(self) -> bool: return False - def __init__(self, tkroot: Optional['tk.Tk'] = None, provider: str = 'internal'): + def __init__(self, tkroot: tk.Tk | None = None, provider: str = 'internal'): """ Initialise an Updater instance. :param tkroot: reference to the root window of the GUI :param provider: 'internal' or other string if not """ - self.root: Optional['tk.Tk'] = tkroot + self.root: tk.Tk | None = tkroot self.provider: str = provider - self.thread: Optional[threading.Thread] = None + self.thread: threading.Thread | None = None if self.use_internal(): return @@ -81,7 +88,7 @@ def __init__(self, tkroot: Optional['tk.Tk'] = None, provider: str = 'internal') import ctypes try: - self.updater: Optional[ctypes.CDLL] = ctypes.cdll.WinSparkle + self.updater: ctypes.CDLL | None = ctypes.cdll.WinSparkle # Set the appcast URL self.updater.win_sparkle_set_appcast_url(update_feed.encode()) @@ -150,7 +157,7 @@ def check_for_updates(self) -> None: elif sys.platform == 'darwin' and self.updater: self.updater.checkForUpdates_(None) - def check_appcast(self) -> Optional[EDMCVersion]: + def check_appcast(self) -> EDMCVersion | None: """ Manually (no Sparkle or WinSparkle) check the update_feed appcast file. @@ -161,7 +168,7 @@ def check_appcast(self) -> Optional[EDMCVersion]: newversion = None items = {} try: - r = requests.get(update_feed, timeout=10) + request = requests.get(update_feed, timeout=10) except requests.RequestException as ex: logger.exception(f'Error retrieving update_feed file: {ex}') @@ -169,7 +176,7 @@ def check_appcast(self) -> Optional[EDMCVersion]: return None try: - feed = ElementTree.fromstring(r.text) + feed = ElementTree.fromstring(request.text) except SyntaxError as ex: logger.exception(f'Syntax error in update_feed file: {ex}') @@ -196,12 +203,12 @@ def check_appcast(self) -> Optional[EDMCVersion]: continue # This will change A.B.C.D to A.B.C+D - sv = semantic_version.Version.coerce(ver) + semver = semantic_version.Version.coerce(ver) - items[sv] = EDMCVersion( + items[semver] = EDMCVersion( version=str(ver), # sv might have mangled version title=item.find('title').text, # type: ignore - sv=sv + sv=semver ) # Look for any remaining version greater than appversion