1
0
mirror of https://github.com/EDCD/EDMarketConnector.git synced 2025-04-12 15:27:14 +03:00

[2051] Update Supporting Files

Not touching any of the big ones, but some clarification updates to many of the supporting files.
This commit is contained in:
David Sangrey 2023-11-17 11:33:26 -05:00
parent a169fb5a18
commit b2d5e13465
No known key found for this signature in database
GPG Key ID: 3AEADBB0186884BC
7 changed files with 100 additions and 76 deletions

View File

@ -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 @@ if __name__ == "__main__":
print('Not docked!')
continue
elif not data.get('lastStarport'):
if not data.get('lastStarport'):
print('No starport!')
continue

View File

@ -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 tempfile
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 @@ SAFE_TRANSLATE = str.maketrans({x: '_' for x in "!@#$%^&*()./\\\r\n[]-+='\";:?<>
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 @@ class LoggingHandler(server.BaseHTTPRequestHandler):
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 @@ class LoggingHandler(server.BaseHTTPRequestHandler):
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:

View File

@ -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 @@ import tkinter as tk
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 @@ class JournalLock:
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')

View File

@ -10,10 +10,11 @@ OSX and Windows.
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 @@ elif sys.platform == 'win32':
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 @@ class Frame(sys.platform == 'darwin' and tk.Frame or ttk.Frame): # type: ignore
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 @@ class Label(tk.Label):
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 @@ class Entry(sys.platform == 'darwin' and tk.Entry or ttk.Entry): # type: ignore
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 @@ class Button(sys.platform == 'darwin' and tk.Button or ttk.Button): # type: ign
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 @@ class ColoredButton(sys.platform == 'darwin' and tk.Label or tk.Button): # type
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 @@ class Checkbutton(sys.platform == 'darwin' and tk.Checkbutton or ttk.Checkbutton
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)

View File

@ -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 @@ import tkinter as tk
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 @@ if sys.platform == 'win32':
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 @@ elif sys.platform == 'linux':
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 @@ class _Theme(object):
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 @@ class _Theme(object):
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('<Button-1>', command)
widget.bind('<Enter>', lambda e: self._enter(e, image))
widget.bind('<Leave>', 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 @@ class _Theme(object):
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 @@ class _Theme(object):
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 @@ class _Theme(object):
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 @@ class _Theme(object):
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 @@ class _Theme(object):
# 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 @@ class _Theme(object):
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

View File

@ -1,7 +1,15 @@
"""A requests.session with a TimeoutAdapter."""
import requests
from requests.adapters import HTTPAdapter
"""
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

View File

@ -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 @@ class EDMCVersion(object):
self.sv: semantic_version.base.Version = sv
class Updater(object):
class Updater:
"""
Handle checking for updates.
@ -63,16 +70,16 @@ class Updater(object):
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 @@ class Updater(object):
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 @@ class Updater(object):
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 @@ class Updater(object):
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 @@ class Updater(object):
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 @@ class Updater(object):
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