From 3290cd63991be85aaee632929b36cd947aca3071 Mon Sep 17 00:00:00 2001 From: David Sangrey Date: Sun, 9 Jun 2024 19:15:46 -0400 Subject: [PATCH 01/34] [2256] Allow Variable Num of Variables --- EDMarketConnector.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/EDMarketConnector.py b/EDMarketConnector.py index cd76ccc07..1b138dd86 100755 --- a/EDMarketConnector.py +++ b/EDMarketConnector.py @@ -454,7 +454,7 @@ def __init__(self, master: tk.Tk): # noqa: C901, CCR001 # TODO - can possibly f if sys.platform == 'win32': from simplesystray import SysTrayIcon - def open_window(systray: 'SysTrayIcon') -> None: + def open_window(systray: 'SysTrayIcon', *args) -> None: self.w.deiconify() menu_options = (("Open", None, open_window),) From d4b7173cf4a36a90d3fa3c1092b13fc3edc2c85f Mon Sep 17 00:00:00 2001 From: David Sangrey Date: Sun, 9 Jun 2024 20:50:30 -0400 Subject: [PATCH 02/34] [525] Update Reddit and HTML Changelog --- .gitignore | 3 +- requirements-dev.txt | 2 +- scripts/build_changelog.py | 107 +++++++++++++++++++++++++++++++++++++ 3 files changed, 110 insertions(+), 2 deletions(-) create mode 100644 scripts/build_changelog.py diff --git a/.gitignore b/.gitignore index 778820c66..1df2f6660 100644 --- a/.gitignore +++ b/.gitignore @@ -9,8 +9,9 @@ build dist.win32/ dist.* -# Ignore generated ChangeLog.html file +# Ignore generated ChangeLog files ChangeLog.html +/scripts/script_output # Ignore files dump diff --git a/requirements-dev.txt b/requirements-dev.txt index 7c8e1ef5f..05c5ad291 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -31,7 +31,7 @@ autopep8==2.2.0 pre-commit==3.7.1 # HTML changelogs -grip==4.6.2 +mistune==3.0.2 # Packaging # We only need py2exe on windows. diff --git a/scripts/build_changelog.py b/scripts/build_changelog.py new file mode 100644 index 000000000..ce266fd78 --- /dev/null +++ b/scripts/build_changelog.py @@ -0,0 +1,107 @@ +#!/usr/bin/env python3 +# flake8: noqa +""" +build_changelog.py - Read the latest changelog file and format for the Forums and Reddit. + +Copyright (c) EDCD, All Rights Reserved +Licensed under the GNU General Public License. +See LICENSE file. +""" +import pathlib +import re +from os import chdir +import mistune + + +def get_changelog() -> str: + """Pull the last full changelog details in MD.""" + with open("../CHANGELOG.md", encoding="utf-8") as changelog_file: + content = changelog_file.read() + changelog_list: list = content.split("---", maxsplit=2) + changelog = changelog_list[2] + changelog_list = changelog.split("===", maxsplit=2) + changelog_list[0] = changelog_list[0].rstrip() + changelog_list[0] = changelog_list[0].lstrip() + changelog_list[0] += "\n===" + changelog_list[1] = changelog_list[1].rstrip() + changelog_list[1] = "\n".join(changelog_list[1].split("\n")[:-2]) + changelog = changelog_list[0] + changelog_list[1] + changelog = changelog.rstrip() + return changelog + + +def build_html(md_changelog) -> None: + html_out = mistune.html(md_changelog) + html_out = re.sub("h1", "h2", html_out) + html_out += "\n
" + with open("script_output/html_changelog.txt", "w", encoding="utf-8") as html_file: + html_file.write(html_out) + + +def build_reddit( + md_changelog: str, vt_signed: str, vt_unsigned: str, version: str, gh_link: str +) -> None: + reddit_start = """# What Is Elite Dangerous Market Connector? + +Elite Dangerous Market Connector ("EDMC") is a third-party application for use with Frontier Developments' game "Elite Dangerous". Its purpose is to facilitate supplying certain game data to, and in some cases retrieving it from, a number of websites and other tools. + +To achieve this it utilizes the Journal Files written by the game when played on a PC. It also makes use of Frontier's Companion API ("Frontier's CAPI"), accessible once you've authorized this application. + +EDMC has a plugin system that many other developers have made use of to extend its functionality. + +Find out more on the [EDMC Wiki](https://github.com/EDCD/EDMarketConnector/wiki). + +~~----------------------------------------------------~~ + +You can also view the Elite: Dangerous Forum thread [HERE](https://forums.frontier.co.uk/threads/elite-dangerous-market-connector-edmc.618708/). + +~~----------------------------------------------------~~ + +**As has become routine now, various anti-virus software are reporting a false positive on our installer and/or files it contains. We've pre-emptively uploaded the installer to** [VirusTotal]( +""" + reddit_mid_1 = """) **if you want to check what it's saying. Please see our** [Troubleshooting/AV-false-positives FAQ](https://github.com/EDCD/EDMarketConnector/wiki/Troubleshooting#installer-and-or-executables-flagged-as-malicious-viruses) **for further information.** + +[Unsigned Installer](""" + + reddit_mid_2 = """) if you don't want to use the code-signed option. + +~~----------------------------------------------------~~ +""" + versionsearch = f"Release {version}" + updated = f"# [Release {version}]({gh_link})" + md_changelog = re.sub("===\n", "", md_changelog) + md_changelog = re.sub(versionsearch, updated, md_changelog) + reddit_end = f""" + +**Linux** + +If you're running on Linux, try the [Flatpak](https://flathub.org/apps/io.edcd.EDMarketConnector) build of EDMC! (Update to {version} coming soon.)""" + + reddit_out = ( + reddit_start + + vt_signed + + reddit_mid_1 + + vt_unsigned + + reddit_mid_2 + + md_changelog + + reddit_end + ) + with open( + "script_output/reddit_changelog.txt", "w", encoding="utf-8" + ) as reddit_file: + reddit_file.write(reddit_out) + + +def main() -> None: + vt_signed = input("Please enter the VirusTotal URL for the Signed Installer: ") + vt_unsigned = input("Please enter the VirusTotal URL for the Unsigned Installer: ") + version = input("Please enter the version of EDMC: ") + gh_link = input(f"Please enter the GitHub link for {version}: ") + md_changelog = get_changelog() + build_reddit(md_changelog, vt_signed, vt_unsigned, version, gh_link) + build_html(md_changelog) + + +if __name__ == "__main__": + chdir(pathlib.Path(__file__).parent) + main() From 340c9818fcecc60af66f14f405342d50f79c8cbf Mon Sep 17 00:00:00 2001 From: David Sangrey Date: Sun, 9 Jun 2024 21:22:24 -0400 Subject: [PATCH 03/34] [525] Add FDEV Forum Post --- scripts/build_changelog.py | 65 ++++++++++++++++++++++++++++++++++---- 1 file changed, 58 insertions(+), 7 deletions(-) diff --git a/scripts/build_changelog.py b/scripts/build_changelog.py index ce266fd78..c4772a83a 100644 --- a/scripts/build_changelog.py +++ b/scripts/build_changelog.py @@ -13,7 +13,7 @@ import mistune -def get_changelog() -> str: +def get_changelog() -> tuple[str, str]: """Pull the last full changelog details in MD.""" with open("../CHANGELOG.md", encoding="utf-8") as changelog_file: content = changelog_file.read() @@ -27,15 +27,65 @@ def get_changelog() -> str: changelog_list[1] = "\n".join(changelog_list[1].split("\n")[:-2]) changelog = changelog_list[0] + changelog_list[1] changelog = changelog.rstrip() - return changelog + version = changelog.split("\n")[0] + version = version.split(" ")[1] + return changelog, version -def build_html(md_changelog) -> None: +def build_html(md_changelog) -> str: html_out = mistune.html(md_changelog) html_out = re.sub("h1", "h2", html_out) html_out += "\n
" with open("script_output/html_changelog.txt", "w", encoding="utf-8") as html_file: html_file.write(html_out) + return html_out + + +def build_fdev( + vt_signed: str, + vt_unsigned: str, + version: str, + gh_link: str, + html: str, +) -> None: + fdev_out = ( + "[HEADING=2][URL='" + + gh_link + + "'][SIZE=7]Release " + + version + + "[/SIZE][/URL][/HEADING]\n[URL='" + + vt_signed + ) + fdev_out += ( + "']Pre-emptive upload to VirusTotal[/URL]. ([URL='" + + vt_unsigned + + "']Unsigned Installer[/URL])\n\n" + ) + + if version.startswith("Pre-Release") or version.startswith("Beta"): + fdev_out += f'This is a release candidate for {version}. It has been pushed to the "Beta" track for updates!' + fdev_out += ( + '\n\nFor more information on the "Beta" update track, please read ' + "[URL='https://github.com/EDCD/EDMarketConnector/wiki/Participating-in-Open-Betas-of-EDMC']" + "This Wiki Article[/URL]. Questions and comments are welcome!\n\n" + ) + changelog_trim = html.split("\n", maxsplit=1) + md_log = changelog_trim[1] + md_log = re.sub("

", "", md_log) + md_log = re.sub("

", "", md_log) + md_log = re.sub("", "\n[HEADING=3]", md_log) + md_log = re.sub("", "[/HEADING]", md_log) + md_log = re.sub("
    ", "[LIST]", md_log) + md_log = re.sub("
  • ", "[*]", md_log) + md_log = re.sub("
  • ", "", md_log) + md_log = re.sub("", "[ICODE]", md_log) + md_log = re.sub("", "[/ICODE]", md_log) + md_log = re.sub("
\n", "[/LIST]", md_log) + fdev_out += md_log + + with open("script_output/fdev_changelog.txt", "w", encoding="utf-8") as fdev_file: + fdev_file.write(fdev_out) + return def build_reddit( @@ -93,13 +143,14 @@ def build_reddit( def main() -> None: + md_changelog, version = get_changelog() + print(f"Detected version {version} in the changelog. Continuing...") + gh_link = input(f"Please enter the GitHub link for {version}: ") vt_signed = input("Please enter the VirusTotal URL for the Signed Installer: ") vt_unsigned = input("Please enter the VirusTotal URL for the Unsigned Installer: ") - version = input("Please enter the version of EDMC: ") - gh_link = input(f"Please enter the GitHub link for {version}: ") - md_changelog = get_changelog() build_reddit(md_changelog, vt_signed, vt_unsigned, version, gh_link) - build_html(md_changelog) + html = build_html(md_changelog) + build_fdev(vt_signed, vt_unsigned, version, gh_link, html) if __name__ == "__main__": From 33f8e9c83707431ca5e4cd1f16120d0220ad1d1f Mon Sep 17 00:00:00 2001 From: David Sangrey Date: Sun, 9 Jun 2024 21:51:56 -0400 Subject: [PATCH 04/34] [525] Cleanup Script --- scripts/build_changelog.py | 129 +++++++++++++++++++------------------ 1 file changed, 68 insertions(+), 61 deletions(-) diff --git a/scripts/build_changelog.py b/scripts/build_changelog.py index c4772a83a..f0c019943 100644 --- a/scripts/build_changelog.py +++ b/scripts/build_changelog.py @@ -7,6 +7,7 @@ Licensed under the GNU General Public License. See LICENSE file. """ + import pathlib import re from os import chdir @@ -15,82 +16,87 @@ def get_changelog() -> tuple[str, str]: """Pull the last full changelog details in MD.""" - with open("../CHANGELOG.md", encoding="utf-8") as changelog_file: - content = changelog_file.read() - changelog_list: list = content.split("---", maxsplit=2) - changelog = changelog_list[2] - changelog_list = changelog.split("===", maxsplit=2) - changelog_list[0] = changelog_list[0].rstrip() - changelog_list[0] = changelog_list[0].lstrip() - changelog_list[0] += "\n===" - changelog_list[1] = changelog_list[1].rstrip() - changelog_list[1] = "\n".join(changelog_list[1].split("\n")[:-2]) - changelog = changelog_list[0] + changelog_list[1] - changelog = changelog.rstrip() - version = changelog.split("\n")[0] - version = version.split(" ")[1] - return changelog, version - - -def build_html(md_changelog) -> str: - html_out = mistune.html(md_changelog) - html_out = re.sub("h1", "h2", html_out) - html_out += "\n
" + try: + with open("../CHANGELOG.md", encoding="utf-8") as changelog_file: + content = changelog_file.read() + except FileNotFoundError as exc: + raise FileNotFoundError("Changelog file not found.") from exc + + changelog_list = content.split("---", maxsplit=2) + if len(changelog_list) < 3: + raise ValueError("Changelog format is incorrect.") + + changelog = changelog_list[2].split("===", maxsplit=2) + if len(changelog) < 2: + raise ValueError("Changelog format is incorrect.") + + changelog[0] = changelog[0].strip() + changelog[1] = "\n".join(changelog[1].strip().split("\n")[:-2]) + version = changelog[0] + version = version.split(" ")[1] + changelog = changelog[1].strip() + + return changelog, version + + +def build_html(md_changelog: str, version: str) -> str: + """Convert markdown changelog to HTML.""" + html_out = f"

Release {version}

\n" + html_out += mistune.html(md_changelog) + html_out = re.sub(r"h1", "h2", html_out) + "\n
" + with open("script_output/html_changelog.txt", "w", encoding="utf-8") as html_file: html_file.write(html_out) + return html_out +def format_fdev(md_log: str) -> str: + """Format changelog for FDEV forums.""" + md_log = re.sub(r"

|

", "", md_log) + md_log = re.sub(r"", "\n[HEADING=3]", md_log) + md_log = re.sub(r"", "[/HEADING]", md_log) + md_log = re.sub(r"
    ", "[LIST]", md_log) + md_log = re.sub(r"
  • ", "[*]", md_log) + md_log = re.sub(r"
  • ", "", md_log) + md_log = re.sub(r"", "[ICODE]", md_log) + md_log = re.sub(r"", "[/ICODE]", md_log) + md_log = re.sub(r"
\n", "[/LIST]", md_log) + md_log = re.sub(r"
", "", md_log) + md_log = re.sub(r"Changes and Enhancements", "What's Changed", md_log) + return md_log + + def build_fdev( - vt_signed: str, - vt_unsigned: str, - version: str, - gh_link: str, - html: str, + vt_signed: str, vt_unsigned: str, version: str, gh_link: str, html: str ) -> None: + """Build changelog for FDEV forums.""" fdev_out = ( - "[HEADING=2][URL='" - + gh_link - + "'][SIZE=7]Release " - + version - + "[/SIZE][/URL][/HEADING]\n[URL='" - + vt_signed - ) - fdev_out += ( - "']Pre-emptive upload to VirusTotal[/URL]. ([URL='" - + vt_unsigned - + "']Unsigned Installer[/URL])\n\n" + f"[HEADING=2][URL='{gh_link}'][SIZE=7]Release {version}[/SIZE][/URL][/HEADING]\n" + f"[URL='{vt_signed}']Pre-emptive upload to VirusTotal[/URL]. " + f"([URL='{vt_unsigned}']Unsigned Installer[/URL])\n\n" ) - if version.startswith("Pre-Release") or version.startswith("Beta"): - fdev_out += f'This is a release candidate for {version}. It has been pushed to the "Beta" track for updates!' + if version.startswith(("Pre-Release", "Beta")): fdev_out += ( - '\n\nFor more information on the "Beta" update track, please read ' + f'This is a release candidate for {version}. It has been pushed to the "Beta" track for updates!\n\n' + 'For more information on the "Beta" update track, please read ' "[URL='https://github.com/EDCD/EDMarketConnector/wiki/Participating-in-Open-Betas-of-EDMC']" "This Wiki Article[/URL]. Questions and comments are welcome!\n\n" ) - changelog_trim = html.split("\n", maxsplit=1) - md_log = changelog_trim[1] - md_log = re.sub("

", "", md_log) - md_log = re.sub("

", "", md_log) - md_log = re.sub("", "\n[HEADING=3]", md_log) - md_log = re.sub("", "[/HEADING]", md_log) - md_log = re.sub("
    ", "[LIST]", md_log) - md_log = re.sub("
  • ", "[*]", md_log) - md_log = re.sub("
  • ", "", md_log) - md_log = re.sub("", "[ICODE]", md_log) - md_log = re.sub("", "[/ICODE]", md_log) - md_log = re.sub("
\n", "[/LIST]", md_log) + + md_log = html.split("\n", maxsplit=1)[1] + md_log = format_fdev(md_log) fdev_out += md_log with open("script_output/fdev_changelog.txt", "w", encoding="utf-8") as fdev_file: fdev_file.write(fdev_out) - return def build_reddit( md_changelog: str, vt_signed: str, vt_unsigned: str, version: str, gh_link: str ) -> None: + """Build changelog for Reddit.""" reddit_start = """# What Is Elite Dangerous Market Connector? Elite Dangerous Market Connector ("EDMC") is a third-party application for use with Frontier Developments' game "Elite Dangerous". Its purpose is to facilitate supplying certain game data to, and in some cases retrieving it from, a number of websites and other tools. @@ -107,9 +113,8 @@ def build_reddit( ~~----------------------------------------------------~~ -**As has become routine now, various anti-virus software are reporting a false positive on our installer and/or files it contains. We've pre-emptively uploaded the installer to** [VirusTotal]( -""" - reddit_mid_1 = """) **if you want to check what it's saying. Please see our** [Troubleshooting/AV-false-positives FAQ](https://github.com/EDCD/EDMarketConnector/wiki/Troubleshooting#installer-and-or-executables-flagged-as-malicious-viruses) **for further information.** +**As has become routine now, various anti-virus software are reporting a false positive on our installer and/or files it contains. We've pre-emptively uploaded the installer to** [VirusTotal](""" + reddit_mid_1 = """) **if you want to check what it's saying. Please see our** [Troubleshooting/AV-false-positives FAQ](https://github.com/EDCD/EDMarketConnector/wiki/Troubleshooting#installer-and-or-executables-flagged-as-malicious-viruses) **for further information.** [Unsigned Installer](""" @@ -117,10 +122,9 @@ def build_reddit( ~~----------------------------------------------------~~ """ - versionsearch = f"Release {version}" - updated = f"# [Release {version}]({gh_link})" - md_changelog = re.sub("===\n", "", md_changelog) - md_changelog = re.sub(versionsearch, updated, md_changelog) + updated = f"# [Release {version}]({gh_link})\n\n" + md_changelog = re.sub(r"===\n", "", md_changelog) + md_changelog = re.sub(f"Release {version}", updated, md_changelog) reddit_end = f""" **Linux** @@ -133,9 +137,11 @@ def build_reddit( + reddit_mid_1 + vt_unsigned + reddit_mid_2 + + updated + md_changelog + reddit_end ) + with open( "script_output/reddit_changelog.txt", "w", encoding="utf-8" ) as reddit_file: @@ -143,13 +149,14 @@ def build_reddit( def main() -> None: + """Run the Changelog Generator""" md_changelog, version = get_changelog() print(f"Detected version {version} in the changelog. Continuing...") gh_link = input(f"Please enter the GitHub link for {version}: ") vt_signed = input("Please enter the VirusTotal URL for the Signed Installer: ") vt_unsigned = input("Please enter the VirusTotal URL for the Unsigned Installer: ") build_reddit(md_changelog, vt_signed, vt_unsigned, version, gh_link) - html = build_html(md_changelog) + html = build_html(md_changelog, version) build_fdev(vt_signed, vt_unsigned, version, gh_link, html) From 74af852fca8786ebaa463990b29a54f2b44468f6 Mon Sep 17 00:00:00 2001 From: David Sangrey Date: Mon, 10 Jun 2024 09:43:00 -0400 Subject: [PATCH 05/34] [2215] Move Logging Directory --- EDMCLogging.py | 5 +---- EDMarketConnector.py | 10 ++++++---- debug_webserver.py | 6 ++---- prefs.py | 6 ++---- 4 files changed, 11 insertions(+), 16 deletions(-) diff --git a/EDMCLogging.py b/EDMCLogging.py index b6d4b3534..c09de41e5 100644 --- a/EDMCLogging.py +++ b/EDMCLogging.py @@ -41,7 +41,6 @@ import logging.handlers import os import pathlib -import tempfile import warnings from contextlib import suppress from fnmatch import fnmatch @@ -51,7 +50,6 @@ from time import gmtime from traceback import print_exc from typing import TYPE_CHECKING, cast - import config as config_mod from config import appcmdname, appname, config @@ -184,8 +182,7 @@ def __init__(self, logger_name: str, loglevel: int | str = _default_loglevel): # We want the files in %TEMP%\{appname}\ as {logger_name}-debug.log and # rotated versions. # This is {logger_name} so that EDMC.py logs to a different file. - logfile_rotating = pathlib.Path(tempfile.gettempdir()) - logfile_rotating /= f'{appname}' + logfile_rotating = pathlib.Path(config.app_dir_path / 'logs') logfile_rotating.mkdir(exist_ok=True) logfile_rotating /= f'{logger_name}-debug.log' diff --git a/EDMarketConnector.py b/EDMarketConnector.py index cd76ccc07..609cc2e98 100755 --- a/EDMarketConnector.py +++ b/EDMarketConnector.py @@ -20,7 +20,6 @@ import sys import threading import webbrowser -import tempfile from os import chdir, environ, path from time import localtime, strftime, time from typing import TYPE_CHECKING, Any, Literal @@ -42,16 +41,19 @@ # not frozen. chdir(pathlib.Path(__file__).parent) - # config will now cause an appname logger to be set up, so we need the # console redirect before this if __name__ == '__main__': # Keep this as the very first code run to be as sure as possible of no # output until after this redirect is done, if needed. if getattr(sys, 'frozen', False): + from config import config # By default py2exe tries to write log to dirname(sys.executable) which fails when installed # unbuffered not allowed for text in python3, so use `1 for line buffering - log_file_path = path.join(tempfile.gettempdir(), f'{appname}.log') + log_file_path = pathlib.Path(config.app_dir_path / 'logs') + log_file_path.mkdir(exist_ok=True) + log_file_path /= f'{appname}.log' + sys.stdout = sys.stderr = open(log_file_path, mode='wt', buffering=1) # Do NOT use WITH here. # TODO: Test: Make *sure* this redirect is working, else py2exe is going to cause an exit popup @@ -619,7 +621,7 @@ def open_window(systray: 'SysTrayIcon') -> None: self.help_menu.add_command(command=lambda: self.updater.check_for_updates()) # Check for Updates... # About E:D Market Connector self.help_menu.add_command(command=lambda: not self.HelpAbout.showing and self.HelpAbout(self.w)) - logfile_loc = pathlib.Path(tempfile.gettempdir()) / appname + logfile_loc = pathlib.Path(config.app_dir_path / 'logs') self.help_menu.add_command(command=lambda: prefs.open_folder(logfile_loc)) # Open Log Folder self.help_menu.add_command(command=lambda: prefs.help_open_system_profiler(self)) # Open Log Folde diff --git a/debug_webserver.py b/debug_webserver.py index 87f7eaed0..eeedce977 100644 --- a/debug_webserver.py +++ b/debug_webserver.py @@ -4,20 +4,18 @@ import gzip import json import pathlib -import tempfile import threading import zlib from http import server from typing import Any, Callable, Literal from urllib.parse import parse_qs - -from config import appname +import config from EDMCLogging import get_main_logger logger = get_main_logger() output_lock = threading.Lock() -output_data_path = pathlib.Path(tempfile.gettempdir()) / f'{appname}' / 'http_debug' +output_data_path = pathlib.Path(config.app_dir_path / 'logs' / 'http_debug') SAFE_TRANSLATE = str.maketrans({x: '_' for x in "!@#$%^&*()./\\\r\n[]-+='\";:?<>,~`"}) diff --git a/prefs.py b/prefs.py index 9f2062f40..f07a41db8 100644 --- a/prefs.py +++ b/prefs.py @@ -7,7 +7,6 @@ import pathlib import subprocess import sys -import tempfile import tkinter as tk import warnings from os import system @@ -20,7 +19,6 @@ import plug from config import appversion_nobuild, config from EDMCLogging import edmclogger, get_main_logger -from constants import appname from hotkey import hotkeymgr from l10n import translations as tr from monitor import monitor @@ -43,7 +41,7 @@ def help_open_log_folder() -> None: """Open the folder logs are stored in.""" warnings.warn('prefs.help_open_log_folder is deprecated, use open_log_folder instead. ' 'This function will be removed in 6.0 or later', DeprecationWarning, stacklevel=2) - open_folder(pathlib.Path(tempfile.gettempdir()) / appname) + open_folder(pathlib.Path(config.app_dir_path / 'logs')) def open_folder(file: pathlib.Path) -> None: @@ -323,7 +321,7 @@ def __init__(self, parent: tk.Tk, callback: Optional[Callable]): self.geometry(f"+{position.left}+{position.top}") # Set Log Directory - self.logfile_loc = pathlib.Path(tempfile.gettempdir()) / appname + self.logfile_loc = pathlib.Path(config.app_dir_path / 'logs') # Set minimum size to prevent content cut-off self.update_idletasks() # Update "requested size" from geometry manager From c198108700df9aa6b9fa0438219e505e71c1afd5 Mon Sep 17 00:00:00 2001 From: David Sangrey Date: Mon, 10 Jun 2024 12:55:58 -0400 Subject: [PATCH 06/34] [748] Establish EDMC Shutdown Mechanism --- EDMarketConnector.py | 12 +++++++++++- prefs.py | 18 +++++++++++------- 2 files changed, 22 insertions(+), 8 deletions(-) diff --git a/EDMarketConnector.py b/EDMarketConnector.py index cd76ccc07..2582f570e 100755 --- a/EDMarketConnector.py +++ b/EDMarketConnector.py @@ -845,9 +845,19 @@ def postprefs(self, dologin: bool = True, **postargs): ) update_msg = update_msg.replace('\\n', '\n') update_msg = update_msg.replace('\\r', '\r') - stable_popup = tk.messagebox.askyesno(title=title, message=update_msg, parent=postargs.get('Parent')) + stable_popup = tk.messagebox.askyesno(title=title, message=update_msg) if stable_popup: webbrowser.open("https://github.com/edCD/eDMarketConnector/releases/latest") + if postargs.get('Restart_Req'): + restart_msg = tr.tl('A restart of EDMC is required. EDMC will now shut down.') + restart_box = tk.messagebox.Message( + title=tr.tl('Restart Required'), + message=restart_msg, + type=tk.messagebox.OK + ) + restart_box.show() + if restart_box: + app.onexit() def set_labels(self): """Set main window labels, e.g. after language change.""" diff --git a/prefs.py b/prefs.py index 9f2062f40..dffe71b94 100644 --- a/prefs.py +++ b/prefs.py @@ -239,6 +239,7 @@ def __init__(self, parent: tk.Tk, callback: Optional[Callable]): self.parent = parent self.callback = callback + self.req_restart = False # LANG: File > Settings (macOS) self.title(tr.tl('Settings')) @@ -1274,19 +1275,22 @@ def apply(self) -> None: config.set('dark_highlight', self.theme_colors[1]) theme.apply(self.parent) - # Send to the Post Config if we updated the update branch - post_flags = { - 'Update': True if self.curr_update_track != self.update_paths.get() else False, - 'Track': self.update_paths.get(), - 'Parent': self - } # Notify if self.callback: - self.callback(**post_flags) + self.callback() plug.notify_prefs_changed(monitor.cmdr, monitor.is_beta) self._destroy() + # Send to the Post Config if we updated the update branch or need to restart + post_flags = { + 'Update': True if self.curr_update_track != self.update_paths.get() else False, + 'Track': self.update_paths.get(), + 'Parent': self, + 'Restart_Req': True if self.req_restart else False + } + if self.callback: + self.callback(**post_flags) def _destroy(self) -> None: """widget.destroy wrapper that does some extra cleanup.""" From fe8818d187be86c85358c35d81062425b850dca1 Mon Sep 17 00:00:00 2001 From: David Sangrey Date: Mon, 10 Jun 2024 17:21:06 -0400 Subject: [PATCH 07/34] [2114] Apply PathLib Handover --- EDMC.py | 11 +++++--- EDMCLogging.py | 7 ++--- EDMCSystemProfiler.py | 17 ++++++------ EDMarketConnector.py | 16 ++++++------ build.py | 19 +++++++------- collate.py | 15 +++++------ commodity.py | 4 +-- companion.py | 5 ++-- dashboard.py | 9 ++++--- docs/examples/plugintest/load.py | 14 +++++----- installer.py | 12 ++++----- l10n.py | 44 ++++++++++++++++---------------- loadout.py | 6 ++--- monitor.py | 36 +++++++++++++------------- plug.py | 22 ++++++++++------ prefs.py | 18 ++++++------- theme.py | 5 ++-- ttkHyperlinkLabel.py | 3 +-- 18 files changed, 137 insertions(+), 126 deletions(-) diff --git a/EDMC.py b/EDMC.py index 6b72c7d1c..55236b9cc 100755 --- a/EDMC.py +++ b/EDMC.py @@ -14,6 +14,7 @@ import os import queue import sys +from pathlib import Path from time import sleep, time from typing import TYPE_CHECKING, Any @@ -212,22 +213,24 @@ def main(): # noqa: C901, CCR001 # system, chances are its the current locale, and not utf-8. Otherwise if it was copied, its probably # utf8. Either way, try the system FIRST because reading something like cp1251 in UTF-8 results in garbage # but the reverse results in an exception. - json_file = os.path.abspath(args.j) + json_file = Path(args.j).resolve() try: with open(json_file) as file_handle: data = json.load(file_handle) except UnicodeDecodeError: with open(json_file, encoding='utf-8') as file_handle: data = json.load(file_handle) - config.set('querytime', int(os.path.getmtime(args.j))) + file_path = Path(args.j) + modification_time = file_path.stat().st_mtime + config.set('querytime', int(modification_time)) else: # Get state from latest Journal file logger.debug('Getting state from latest journal file') try: - monitor.currentdir = config.get_str('journaldir', default=config.default_journal_dir) + monitor.currentdir = Path(config.get_str('journaldir', default=config.default_journal_dir)) if not monitor.currentdir: - monitor.currentdir = config.default_journal_dir + monitor.currentdir = Path(config.default_journal_dir) logger.debug(f'logdir = "{monitor.currentdir}"') logfile = monitor.journal_newest_filename(monitor.currentdir) diff --git a/EDMCLogging.py b/EDMCLogging.py index b6d4b3534..09a01860d 100644 --- a/EDMCLogging.py +++ b/EDMCLogging.py @@ -26,12 +26,13 @@ To utilise logging in a 'found' (third-party) plugin, include this: - import os + from pathlib import Path import logging - plugin_name = os.path.basename(os.path.dirname(__file__)) + # Retrieve the name of the plugin folder + plugin_name = Path(__file__).resolve().parent.name + # Set up logger with hierarchical name including appname and plugin_name # plugin_name here *must* be the name of the folder the plugin resides in - # See, plug.py:load_plugins() logger = logging.getLogger(f'{appname}.{plugin_name}') """ from __future__ import annotations diff --git a/EDMCSystemProfiler.py b/EDMCSystemProfiler.py index 92502cc8b..8b1dcbe56 100644 --- a/EDMCSystemProfiler.py +++ b/EDMCSystemProfiler.py @@ -11,7 +11,7 @@ import webbrowser import platform import sys -from os import chdir, environ, path +from os import chdir, environ import pathlib import logging from journal_lock import JournalLock @@ -19,10 +19,10 @@ if getattr(sys, "frozen", False): # Under py2exe sys.path[0] is the executable name if sys.platform == "win32": - chdir(path.dirname(sys.path[0])) + chdir(pathlib.Path(sys.path[0]).parent) # Allow executable to be invoked from any cwd - environ["TCL_LIBRARY"] = path.join(path.dirname(sys.path[0]), "lib", "tcl") - environ["TK_LIBRARY"] = path.join(path.dirname(sys.path[0]), "lib", "tk") + environ['TCL_LIBRARY'] = str(pathlib.Path(sys.path[0]).parent / 'lib' / 'tcl') + environ['TK_LIBRARY'] = str(pathlib.Path(sys.path[0]).parent / 'lib' / 'tk') else: # We still want to *try* to have CWD be where the main script is, even if @@ -44,11 +44,12 @@ def get_sys_report(config: config.AbstractConfig) -> str: plt = platform.uname() locale.setlocale(locale.LC_ALL, "") lcl = locale.getlocale() - monitor.currentdir = config.get_str( + monitor.currentdir = pathlib.Path(config.get_str( "journaldir", default=config.default_journal_dir + ) ) if not monitor.currentdir: - monitor.currentdir = config.default_journal_dir + monitor.currentdir = pathlib.Path(config.default_journal_dir) try: logfile = monitor.journal_newest_filename(monitor.currentdir) if logfile is None: @@ -115,12 +116,12 @@ def main() -> None: root.withdraw() # Hide the window initially to calculate the dimensions try: icon_image = tk.PhotoImage( - file=path.join(cur_config.respath_path, "io.edcd.EDMarketConnector.png") + file=pathlib.Path(cur_config.respath_path) / "io.edcd.EDMarketConnector.png" ) root.iconphoto(True, icon_image) except tk.TclError: - root.iconbitmap(path.join(cur_config.respath_path, "EDMarketConnector.ico")) + root.iconbitmap(pathlib.Path(cur_config.respath_path) / "EDMarketConnector.ico") sys_report = get_sys_report(cur_config) diff --git a/EDMarketConnector.py b/EDMarketConnector.py index cd76ccc07..f67a4fe11 100755 --- a/EDMarketConnector.py +++ b/EDMarketConnector.py @@ -21,7 +21,7 @@ import threading import webbrowser import tempfile -from os import chdir, environ, path +from os import chdir, environ from time import localtime, strftime, time from typing import TYPE_CHECKING, Any, Literal from constants import applongname, appname, protocolhandler_redirect @@ -32,10 +32,10 @@ if getattr(sys, 'frozen', False): # Under py2exe sys.path[0] is the executable name if sys.platform == 'win32': - chdir(path.dirname(sys.path[0])) + os.chdir(pathlib.Path(sys.path[0]).parent) # Allow executable to be invoked from any cwd - environ['TCL_LIBRARY'] = path.join(path.dirname(sys.path[0]), 'lib', 'tcl') - environ['TK_LIBRARY'] = path.join(path.dirname(sys.path[0]), 'lib', 'tk') + environ['TCL_LIBRARY'] = str(pathlib.Path(sys.path[0]).parent / 'lib' / 'tcl') + environ['TK_LIBRARY'] = str(pathlib.Path(sys.path[0]).parent / 'lib' / 'tk') else: # We still want to *try* to have CWD be where the main script is, even if @@ -51,7 +51,7 @@ if getattr(sys, 'frozen', False): # By default py2exe tries to write log to dirname(sys.executable) which fails when installed # unbuffered not allowed for text in python3, so use `1 for line buffering - log_file_path = path.join(tempfile.gettempdir(), f'{appname}.log') + log_file_path = pathlib.Path(tempfile.gettempdir()) / f'{appname}.log' sys.stdout = sys.stderr = open(log_file_path, mode='wt', buffering=1) # Do NOT use WITH here. # TODO: Test: Make *sure* this redirect is working, else py2exe is going to cause an exit popup @@ -468,8 +468,8 @@ def open_window(systray: 'SysTrayIcon') -> None: self.w.wm_iconbitmap(default='EDMarketConnector.ico') else: - self.w.tk.call('wm', 'iconphoto', self.w, '-default', - tk.PhotoImage(file=path.join(config.respath_path, 'io.edcd.EDMarketConnector.png'))) + image_path = pathlib.Path(config.respath_path) / 'io.edcd.EDMarketConnector.png' + self.w.tk.call('wm', 'iconphoto', self.w, '-default', image=tk.PhotoImage(file=image_path)) # TODO: Export to files and merge from them in future ? self.theme_icon = tk.PhotoImage( @@ -1639,7 +1639,7 @@ def shipyard_url(self, shipname: str) -> str | None: # Avoid file length limits if possible provider = config.get_str('shipyard_provider', default='EDSY') target = plug.invoke(provider, 'EDSY', 'shipyard_url', loadout, monitor.is_beta) - file_name = path.join(config.app_dir_path, "last_shipyard.html") + file_name = pathlib.Path(config.app_dir_path) / "last_shipyard.html" with open(file_name, 'w') as f: f.write(SHIPYARD_HTML_TEMPLATE.format( diff --git a/build.py b/build.py index c5236c5a1..224de56f6 100644 --- a/build.py +++ b/build.py @@ -10,7 +10,6 @@ import sys import pathlib from string import Template -from os.path import join, isdir import py2exe from config import ( appcmdname, @@ -37,7 +36,7 @@ def iss_build(template_path: str, output_file: str) -> None: new_file.write(newfile) -def system_check(dist_dir: str) -> str: +def system_check(dist_dir: pathlib.Path) -> str: """Check if the system is able to build.""" if sys.version_info < (3, 11): sys.exit(f"Unexpected Python version {sys.version}") @@ -55,17 +54,17 @@ def system_check(dist_dir: str) -> str: print(f"Git short hash: {git_shorthash}") - if dist_dir and len(dist_dir) > 1 and isdir(dist_dir): + if dist_dir and pathlib.Path.is_dir(dist_dir): shutil.rmtree(dist_dir) return gitversion_file def generate_data_files( app_name: str, gitversion_file: str, plugins: list[str] -) -> list[tuple[str, list[str]]]: +) -> list[tuple[object, object]]: """Create the required datafiles to build.""" l10n_dir = "L10n" - fdevids_dir = "FDevIDs" + fdevids_dir = pathlib.Path("FDevIDs") data_files = [ ( "", @@ -88,13 +87,13 @@ def generate_data_files( ), ( l10n_dir, - [join(l10n_dir, x) for x in os.listdir(l10n_dir) if x.endswith(".strings")], + [pathlib.Path(l10n_dir) / x for x in os.listdir(l10n_dir) if x.endswith(".strings")] ), ( fdevids_dir, [ - join(fdevids_dir, "commodity.csv"), - join(fdevids_dir, "rare_commodity.csv"), + pathlib.Path(fdevids_dir / "commodity.csv"), + pathlib.Path(fdevids_dir / "rare_commodity.csv"), ], ), ("plugins", plugins), @@ -104,7 +103,7 @@ def generate_data_files( def build() -> None: """Build EDMarketConnector using Py2Exe.""" - dist_dir: str = "dist.win32" + dist_dir: pathlib.Path = pathlib.Path("dist.win32") gitversion_filename: str = system_check(dist_dir) # Constants @@ -142,7 +141,7 @@ def build() -> None: } # Function to generate DATA_FILES list - data_files: list[tuple[str, list[str]]] = generate_data_files( + data_files: list[tuple[object, object]] = generate_data_files( appname, gitversion_filename, plugins ) diff --git a/collate.py b/collate.py index 380cb8ba4..0a4130525 100755 --- a/collate.py +++ b/collate.py @@ -17,7 +17,6 @@ import os import pathlib import sys -from os.path import isfile from traceback import print_exc import companion @@ -35,7 +34,7 @@ def __make_backup(file_name: pathlib.Path, suffix: str = '.bak') -> None: """ backup_name = file_name.parent / (file_name.name + suffix) - if isfile(backup_name): + if pathlib.Path.is_file(backup_name): os.unlink(backup_name) os.rename(file_name, backup_name) @@ -58,7 +57,7 @@ def addcommodities(data) -> None: # noqa: CCR001 commodities = {} # slurp existing - if isfile(commodityfile): + if pathlib.Path.is_file(commodityfile): with open(commodityfile) as csvfile: reader = csv.DictReader(csvfile) for row in reader: @@ -86,7 +85,7 @@ def addcommodities(data) -> None: # noqa: CCR001 if len(commodities) <= size_pre: return - if isfile(commodityfile): + if pathlib.Path.is_file(commodityfile): __make_backup(commodityfile) with open(commodityfile, 'w', newline='\n') as csvfile: @@ -109,7 +108,7 @@ def addmodules(data): # noqa: C901, CCR001 fields = ('id', 'symbol', 'category', 'name', 'mount', 'guidance', 'ship', 'class', 'rating', 'entitlement') # slurp existing - if isfile(outfile): + if pathlib.Path.is_file(outfile): with open(outfile) as csvfile: reader = csv.DictReader(csvfile, restval='') for row in reader: @@ -147,7 +146,7 @@ def addmodules(data): # noqa: C901, CCR001 if not len(modules) > size_pre: return - if isfile(outfile): + if pathlib.Path.is_file(outfile): __make_backup(outfile) with open(outfile, 'w', newline='\n') as csvfile: @@ -170,7 +169,7 @@ def addships(data) -> None: # noqa: CCR001 fields = ('id', 'symbol', 'name') # slurp existing - if isfile(shipfile): + if pathlib.Path.is_file(shipfile): with open(shipfile) as csvfile: reader = csv.DictReader(csvfile, restval='') for row in reader: @@ -200,7 +199,7 @@ def addships(data) -> None: # noqa: CCR001 if not len(ships) > size_pre: return - if isfile(shipfile): + if pathlib.Path.is_file(shipfile): __make_backup(shipfile) with open(shipfile, 'w', newline='\n') as csvfile: diff --git a/commodity.py b/commodity.py index 6b167fa15..52c0f8b80 100644 --- a/commodity.py +++ b/commodity.py @@ -2,7 +2,7 @@ # -*- coding: utf-8 -*- import time -from os.path import join +from pathlib import Path from config import config from edmc_data import commodity_bracketmap as bracketmap @@ -29,7 +29,7 @@ def export(data, kind=COMMODITY_DEFAULT, filename=None) -> None: filename_time = time.strftime('%Y-%m-%dT%H.%M.%S', time.localtime(querytime)) filename_kind = 'csv' filename = f'{filename_system}.{filename_starport}.{filename_time}.{filename_kind}' - filename = join(config.get_str('outdir'), filename) + filename = Path(config.get_str('outdir')) / filename if kind == COMMODITY_CSV: sep = ';' # BUG: for fixing later after cleanup diff --git a/companion.py b/companion.py index 775da2951..d91fed226 100644 --- a/companion.py +++ b/companion.py @@ -27,6 +27,7 @@ import urllib.parse import webbrowser from email.utils import parsedate +from pathlib import Path from queue import Queue from typing import TYPE_CHECKING, Any, Mapping, TypeVar import requests @@ -1135,7 +1136,7 @@ def dump(self, r: requests.Response) -> None: def dump_capi_data(self, data: CAPIData) -> None: """Dump CAPI data to file for examination.""" - if os.path.isdir('dump'): + if Path('dump').is_dir(): file_name: str = "" if data.source_endpoint == self.FRONTIER_CAPI_PATH_FLEETCARRIER: file_name += f"FleetCarrier.{data['name']['callsign']}" @@ -1203,7 +1204,7 @@ def fixup(data: CAPIData) -> CAPIData: # noqa: C901, CCR001 # Can't be usefully if not commodity_map: # Lazily populate for f in ('commodity.csv', 'rare_commodity.csv'): - if not os.path.isfile(config.app_dir_path / 'FDevIDs/' / f): + if not (Path(config.app_dir_path) / 'FDevIDs' / f).is_file(): logger.warning(f'FDevID file {f} not found! Generating output without these commodity name rewrites.') continue with open(config.app_dir_path / 'FDevIDs' / f, 'r') as csvfile: diff --git a/dashboard.py b/dashboard.py index 4776319c5..a57264c02 100644 --- a/dashboard.py +++ b/dashboard.py @@ -12,7 +12,7 @@ import time import tkinter as tk from calendar import timegm -from os.path import getsize, isdir, isfile, join +from pathlib import Path from typing import Any, cast from watchdog.observers.api import BaseObserver from config import config @@ -57,7 +57,7 @@ def start(self, root: tk.Tk, started: int) -> bool: logdir = config.get_str('journaldir', default=config.default_journal_dir) logdir = logdir or config.default_journal_dir - if not isdir(logdir): + if not Path.is_dir(Path(logdir)): logger.info(f"No logdir, or it isn't a directory: {logdir=}") self.stop() return False @@ -164,7 +164,8 @@ def on_modified(self, event) -> None: :param event: Watchdog event. """ - if event.is_directory or (isfile(event.src_path) and getsize(event.src_path)): + modpath = Path(event.src_path) + if event.is_directory or (modpath.is_file() and modpath.stat().st_size): # Can get on_modified events when the file is emptied self.process(event.src_path if not event.is_directory else None) @@ -177,7 +178,7 @@ def process(self, logfile: str | None = None) -> None: if config.shutting_down: return try: - status_json_path = join(self.currentdir, 'Status.json') + status_json_path = Path(self.currentdir) / 'Status.json' with open(status_json_path, 'rb') as h: data = h.read().strip() if data: # Can be empty if polling while the file is being re-written diff --git a/docs/examples/plugintest/load.py b/docs/examples/plugintest/load.py index 4aca16897..b494f4363 100644 --- a/docs/examples/plugintest/load.py +++ b/docs/examples/plugintest/load.py @@ -3,10 +3,10 @@ """Plugin that tests that modules we bundle for plugins are present and working.""" import logging -import os import shutil import sqlite3 import zipfile +from pathlib import Path import semantic_version from SubA import SubA @@ -14,7 +14,7 @@ from config import appname, appversion, config # This could also be returned from plugin_start3() -plugin_name = os.path.basename(os.path.dirname(__file__)) +plugin_name = Path(__file__).resolve().parent.name # Logger per found plugin, so the folder name is included in # the logging format. @@ -49,17 +49,17 @@ class PluginTest: def __init__(self, directory: str): logger.debug(f'directory = "{directory}') - dbfile = os.path.join(directory, this.DBFILE) + dbfile = Path(directory) / this.DBFILE # Test 'import zipfile' - with zipfile.ZipFile(dbfile + '.zip', 'w') as zip: - if os.path.exists(dbfile): + with zipfile.ZipFile(str(dbfile) + '.zip', 'w') as zip: + if dbfile.exists(): zip.write(dbfile) zip.close() # Testing 'import shutil' - if os.path.exists(dbfile): - shutil.copyfile(dbfile, dbfile + '.bak') + if dbfile.exists(): + shutil.copyfile(dbfile, str(dbfile) + '.bak') # Testing 'import sqlite3' self.sqlconn = sqlite3.connect(dbfile) diff --git a/installer.py b/installer.py index 754b33735..1ee79ddb2 100644 --- a/installer.py +++ b/installer.py @@ -5,23 +5,23 @@ Licensed under the GNU General Public License. See LICENSE file. """ -import os import subprocess +from pathlib import Path from build import build -def run_inno_setup_installer(iss_path: str) -> None: +def run_inno_setup_installer(iss_path: Path) -> None: """Run the Inno installer, building the installation exe.""" # Get the path to the Inno Setup compiler (iscc.exe) (Currently set to default path) - inno_setup_compiler_path: str = "C:\\Program Files (x86)\\Inno Setup 6\\ISCC.exe" + inno_setup_compiler_path = Path("C:\\Program Files (x86)\\Inno Setup 6\\ISCC.exe") # Check if the Inno Setup compiler executable exists - if not os.path.isfile(inno_setup_compiler_path): + if not inno_setup_compiler_path.exists(): print(f"Error: Inno Setup compiler not found at '{inno_setup_compiler_path}'.") return # Check if the provided .iss file exists - if not os.path.isfile(iss_file_path): + if not iss_file_path.exists(): print(f"Error: The provided .iss file '{iss_path}' not found.") return @@ -40,6 +40,6 @@ def run_inno_setup_installer(iss_path: str) -> None: if __name__ == "__main__": build() # Add the ISS Template File - iss_file_path: str = "./EDMC_Installer_Config.iss" + iss_file_path = Path("./EDMC_Installer_Config.iss") # Build the ISS file run_inno_setup_installer(iss_file_path) diff --git a/l10n.py b/l10n.py index 8613244fa..857b15962 100755 --- a/l10n.py +++ b/l10n.py @@ -17,10 +17,9 @@ import sys import warnings from contextlib import suppress -from os import listdir, sep, makedirs -from os.path import basename, dirname, isdir, join, abspath, exists +from os import listdir, sep from typing import TYPE_CHECKING, Iterable, TextIO, cast - +import pathlib from config import config from EDMCLogging import get_main_logger @@ -35,7 +34,7 @@ # Language name LANGUAGE_ID = '!Language' -LOCALISATION_DIR = 'L10n' +LOCALISATION_DIR: pathlib.Path = pathlib.Path('L10n') if sys.platform == 'win32': import ctypes @@ -119,10 +118,10 @@ def install(self, lang: str | None = None) -> None: # noqa: CCR001 self.translations = {None: self.contents(cast(str, lang))} for plugin in listdir(config.plugin_dir_path): - plugin_path = join(config.plugin_dir_path, plugin, LOCALISATION_DIR) - if isdir(plugin_path): + plugin_path = config.plugin_dir_path / plugin / LOCALISATION_DIR + if pathlib.Path.is_dir(plugin_path): try: - self.translations[plugin] = self.contents(cast(str, lang), str(plugin_path)) + self.translations[plugin] = self.contents(cast(str, lang), plugin_path) except UnicodeDecodeError as e: logger.warning(f'Malformed file {lang}.strings in plugin {plugin}: {e}') @@ -133,7 +132,7 @@ def install(self, lang: str | None = None) -> None: # noqa: CCR001 # DEPRECATED: Migrate to translations.translate or tr.tl. Will remove in 6.0 or later. builtins.__dict__['_'] = self.translate - def contents(self, lang: str, plugin_path: str | None = None) -> dict[str, str]: + def contents(self, lang: str, plugin_path: pathlib.Path | None = None) -> dict[str, str]: """Load all the translations from a translation file.""" assert lang in self.available() translations = {} @@ -173,12 +172,12 @@ def translate(self, x: str, context: str | None = None, lang: str | None = None) :return: The translated string """ plugin_name: str | None = None - plugin_path: str | None = None + plugin_path: pathlib.Path | None = None if context: # TODO: There is probably a better way to go about this now. plugin_name = context[len(config.plugin_dir)+1:].split(sep)[0] - plugin_path = join(config.plugin_dir_path, plugin_name, LOCALISATION_DIR) + plugin_path = pathlib.Path(config.plugin_dir_path / plugin_name / LOCALISATION_DIR) if lang: contents: dict[str, str] = self.contents(lang=lang, plugin_path=plugin_path) @@ -225,17 +224,17 @@ def available_names(self) -> dict[str | None, str]: return names - def respath(self) -> str: + def respath(self) -> pathlib.Path: """Path to localisation files.""" if getattr(sys, 'frozen', False): - return abspath(join(dirname(sys.executable), LOCALISATION_DIR)) + return pathlib.Path(sys.executable).parent.joinpath(LOCALISATION_DIR).resolve() if __file__: - return abspath(join(dirname(__file__), LOCALISATION_DIR)) + return pathlib.Path(__file__).parent.joinpath(LOCALISATION_DIR).resolve() - return abspath(LOCALISATION_DIR) + return pathlib.Path(LOCALISATION_DIR).resolve() - def file(self, lang: str, plugin_path: str | None = None) -> TextIO | None: + def file(self, lang: str, plugin_path: pathlib.Path | None = None) -> TextIO | None: """ Open the given lang file for reading. @@ -244,8 +243,8 @@ def file(self, lang: str, plugin_path: str | None = None) -> TextIO | None: :return: the opened file (Note: This should be closed when done) """ if plugin_path: - file_path = join(plugin_path, f'{lang}.strings') - if not exists(file_path): + file_path = plugin_path / f"{lang}.strings" + if not file_path.exists(): return None try: @@ -253,7 +252,7 @@ def file(self, lang: str, plugin_path: str | None = None) -> TextIO | None: except OSError: logger.exception(f'could not open {file_path}') - res_path = join(self.respath(), f'{lang}.strings') + res_path = self.respath() / f'{lang}.strings' return open(res_path, encoding='utf-8') @@ -382,9 +381,10 @@ def __init__(self): if __name__ == "__main__": regexp = re.compile(r'''_\([ur]?(['"])(((? N regexp = re.compile(re.escape(ship) + r'\.\d\d\d\d-\d\d-\d\dT\d\d\.\d\d\.\d\d\.txt') oldfiles = sorted([x for x in listdir(config.get_str('outdir')) if regexp.match(x)]) if oldfiles: - with open(join(config.get_str('outdir'), oldfiles[-1]), 'rU') as h: + with open(Path(config.get_str('outdir')) / Path(oldfiles[-1]), 'rU') as h: if h.read() == string: return # same as last time - don't write @@ -55,7 +55,7 @@ def export(data: companion.CAPIData, requested_filename: str | None = None) -> N output_directory = config.get_str('outdir') ship_time = time.strftime('%Y-%m-%dT%H.%M.%S', time.localtime(query_time)) - file_path = join(output_directory, f"{ship}.{ship_time}.txt") + file_path = output_directory / Path(f"{ship}.{ship_time}.txt") with open(file_path, 'wt') as h: h.write(string) diff --git a/monitor.py b/monitor.py index 9d33f5eb5..aca6af32c 100644 --- a/monitor.py +++ b/monitor.py @@ -8,7 +8,7 @@ from __future__ import annotations import json -import pathlib +from pathlib import Path import queue import re import sys @@ -16,7 +16,6 @@ from calendar import timegm from collections import defaultdict from os import SEEK_END, SEEK_SET, listdir -from os.path import basename, expanduser, getctime, isdir, join from time import gmtime, localtime, mktime, sleep, strftime, strptime, time from typing import TYPE_CHECKING, Any, BinaryIO, MutableMapping import semantic_version @@ -81,7 +80,7 @@ def __init__(self) -> None: # TODO(A_D): A bunch of these should be switched to default values (eg '' for strings) and no longer be Optional FileSystemEventHandler.__init__(self) # futureproofing - not need for current version of watchdog self.root: 'tkinter.Tk' = None # type: ignore # Don't use Optional[] - mypy thinks no methods - self.currentdir: str | None = None # The actual logdir that we're monitoring + self.currentdir: Path | None = None # The actual logdir that we're monitoring self.logfile: str | None = None self.observer: BaseObserver | None = None self.observed = None # a watchdog ObservedWatch, or None if polling @@ -202,9 +201,9 @@ def start(self, root: 'tkinter.Tk') -> bool: # noqa: CCR001 if journal_dir == '' or journal_dir is None: journal_dir = config.default_journal_dir - logdir = expanduser(journal_dir) + logdir = Path(journal_dir).expanduser() - if not logdir or not isdir(logdir): + if not logdir or not Path.is_dir(logdir): logger.error(f'Journal Directory is invalid: "{logdir}"') self.stop() return False @@ -277,9 +276,10 @@ def journal_newest_filename(self, journals_dir) -> str | None: # Odyssey Update 11 has, e.g. Journal.2022-03-15T152503.01.log # Horizons Update 11 equivalent: Journal.220315152335.01.log # So we can no longer use a naive sort. - journals_dir_path = pathlib.Path(journals_dir) - journal_files = (journals_dir_path / pathlib.Path(x) for x in journal_files) - return str(max(journal_files, key=getctime)) + journals_dir_path = Path(journals_dir) + journal_files = (journals_dir_path / Path(x) for x in journal_files) + latest_file = max(journal_files, key=lambda f: Path(f).stat().st_ctime) + return str(latest_file) return None @@ -348,7 +348,7 @@ def running(self) -> bool: def on_created(self, event: 'FileSystemEvent') -> None: """Watchdog callback when, e.g. client (re)started.""" - if not event.is_directory and self._RE_LOGFILE.search(basename(event.src_path)): + if not event.is_directory and self._RE_LOGFILE.search(Path(event.src_path).name): self.logfile = event.src_path @@ -1056,7 +1056,7 @@ def parse_entry(self, line: bytes) -> MutableMapping[str, Any]: # noqa: C901, C self.state['Cargo'] = defaultdict(int) # From 3.3 full Cargo event (after the first one) is written to a separate file if 'Inventory' not in entry: - with open(join(self.currentdir, 'Cargo.json'), 'rb') as h: # type: ignore + with open(self.currentdir / 'Cargo.json', 'rb') as h: # type: ignore entry = json.load(h) self.state['CargoJSON'] = entry @@ -1083,7 +1083,7 @@ def parse_entry(self, line: bytes) -> MutableMapping[str, Any]: # noqa: C901, C # Always attempt loading of this, but if it fails we'll hope this was # a startup/boarding version and thus `entry` contains # the data anyway. - currentdir_path = pathlib.Path(str(self.currentdir)) + currentdir_path = Path(str(self.currentdir)) shiplocker_filename = currentdir_path / 'ShipLocker.json' shiplocker_max_attempts = 5 shiplocker_fail_sleep = 0.01 @@ -1152,7 +1152,7 @@ def parse_entry(self, line: bytes) -> MutableMapping[str, Any]: # noqa: C901, C # TODO: v31 doc says this is`backpack.json` ... but Howard Chalkley # said it's `Backpack.json` - backpack_file = pathlib.Path(str(self.currentdir)) / 'Backpack.json' + backpack_file = Path(str(self.currentdir)) / 'Backpack.json' backpack_data = None if not backpack_file.exists(): @@ -1528,7 +1528,7 @@ def parse_entry(self, line: bytes) -> MutableMapping[str, Any]: # noqa: C901, C entry = fcmaterials elif event_type == 'moduleinfo': - with open(join(self.currentdir, 'ModulesInfo.json'), 'rb') as mf: # type: ignore + with open(self.currentdir / 'ModulesInfo.json', 'rb') as mf: # type: ignore try: entry = json.load(mf) @@ -2242,14 +2242,14 @@ def export_ship(self, filename=None) -> None: # noqa: C901, CCR001 oldfiles = sorted((x for x in listdir(config.get_str('outdir')) if regexp.match(x))) if oldfiles: try: - with open(join(config.get_str('outdir'), oldfiles[-1]), encoding='utf-8') as h: + with open(config.get_str('outdir') / Path(oldfiles[-1]), encoding='utf-8') as h: if h.read() == string: return # same as last time - don't write except UnicodeError: logger.exception("UnicodeError reading old ship loadout with utf-8 encoding, trying without...") try: - with open(join(config.get_str('outdir'), oldfiles[-1])) as h: + with open(config.get_str('outdir') / Path(oldfiles[-1])) as h: if h.read() == string: return # same as last time - don't write @@ -2268,7 +2268,7 @@ def export_ship(self, filename=None) -> None: # noqa: C901, CCR001 # Write ts = strftime('%Y-%m-%dT%H.%M.%S', localtime(time())) - filename = join(config.get_str('outdir'), f'{ship}.{ts}.txt') + filename = config.get_str('outdir') / Path(f'{ship}.{ts}.txt') try: with open(filename, 'wt', encoding='utf-8') as h: @@ -2355,7 +2355,7 @@ def _parse_navroute_file(self) -> dict[str, Any] | None: try: - with open(join(self.currentdir, 'NavRoute.json')) as f: + with open(self.currentdir / 'NavRoute.json') as f: raw = f.read() except Exception as e: @@ -2381,7 +2381,7 @@ def _parse_fcmaterials_file(self) -> dict[str, Any] | None: try: - with open(join(self.currentdir, 'FCMaterials.json')) as f: + with open(self.currentdir / 'FCMaterials.json') as f: raw = f.read() except Exception as e: diff --git a/plug.py b/plug.py index 9c203f252..7c4072423 100644 --- a/plug.py +++ b/plug.py @@ -14,6 +14,7 @@ import os import sys import tkinter as tk +from pathlib import Path from tkinter import ttk from typing import Any, Mapping, MutableMapping @@ -47,7 +48,7 @@ def __init__(self) -> None: class Plugin: """An EDMC plugin.""" - def __init__(self, name: str, loadfile: str | None, plugin_logger: logging.Logger | None): # noqa: CCR001 + def __init__(self, name: str, loadfile: Path | None, plugin_logger: logging.Logger | None): # noqa: CCR001 """ Load a single plugin. @@ -73,7 +74,7 @@ def __init__(self, name: str, loadfile: str | None, plugin_logger: logging.Logge sys.modules[module.__name__] = module spec.loader.exec_module(module) if getattr(module, 'plugin_start3', None): - newname = module.plugin_start3(os.path.dirname(loadfile)) + newname = module.plugin_start3(Path(loadfile).resolve().parent) self.name = str(newname) if newname else self.name self.module = module elif getattr(module, 'plugin_start', None): @@ -171,7 +172,9 @@ def _load_internal_plugins(): for name in sorted(os.listdir(config.internal_plugin_dir_path)): if name.endswith('.py') and name[0] not in ('.', '_'): try: - plugin = Plugin(name[:-3], os.path.join(config.internal_plugin_dir_path, name), logger) + plugin_name = name[:-3] + plugin_path = config.internal_plugin_dir_path / name + plugin = Plugin(plugin_name, plugin_path, logger) plugin.folder = None internal.append(plugin) except Exception: @@ -186,9 +189,12 @@ def _load_found_plugins(): # The intent here is to e.g. have EDMC-Overlay load before any plugins # that depend on it. - for name in sorted(os.listdir(config.plugin_dir_path), key=lambda n: ( - not os.path.isfile(os.path.join(config.plugin_dir_path, n, '__init__.py')), n.lower())): - if not os.path.isdir(os.path.join(config.plugin_dir_path, name)) or name[0] in ('.', '_'): + plugin_files = sorted(Path(config.plugin_dir_path).iterdir(), key=lambda p: ( + not (p / '__init__.py').is_file(), p.name.lower())) + + for plugin_file in plugin_files: + name = plugin_file.name + if not (Path(config.plugin_dir_path) / name).is_dir() or name.startswith(('.', '_')): pass elif name.endswith('.disabled'): name, discard = name.rsplit('.', 1) @@ -196,12 +202,12 @@ def _load_found_plugins(): else: try: # Add plugin's folder to load path in case plugin has internal package dependencies - sys.path.append(os.path.join(config.plugin_dir_path, name)) + sys.path.append(str(Path(config.plugin_dir_path) / name)) import EDMCLogging # Create a logger for this 'found' plugin. Must be before the load.py is loaded. plugin_logger = EDMCLogging.get_plugin_logger(name) - found.append(Plugin(name, os.path.join(config.plugin_dir_path, name, 'load.py'), plugin_logger)) + found.append(Plugin(name, config.plugin_dir_path / name / 'load.py', plugin_logger)) except Exception: PLUGINS_broken.append(Plugin(name, None, logger)) logger.exception(f'Failure loading found Plugin "{name}"') diff --git a/prefs.py b/prefs.py index 9f2062f40..673d1e702 100644 --- a/prefs.py +++ b/prefs.py @@ -4,14 +4,14 @@ import contextlib import logging -import pathlib +from os.path import expandvars +from pathlib import Path import subprocess import sys import tempfile import tkinter as tk import warnings from os import system -from os.path import expanduser, expandvars, join, normpath from tkinter import colorchooser as tkColorChooser # type: ignore # noqa: N812 from tkinter import ttk from types import TracebackType @@ -43,10 +43,10 @@ def help_open_log_folder() -> None: """Open the folder logs are stored in.""" warnings.warn('prefs.help_open_log_folder is deprecated, use open_log_folder instead. ' 'This function will be removed in 6.0 or later', DeprecationWarning, stacklevel=2) - open_folder(pathlib.Path(tempfile.gettempdir()) / appname) + open_folder(Path(tempfile.gettempdir()) / appname) -def open_folder(file: pathlib.Path) -> None: +def open_folder(file: Path) -> None: """Open the given file in the OS file explorer.""" if sys.platform.startswith('win'): # On Windows, use the "start" command to open the folder @@ -58,7 +58,7 @@ def open_folder(file: pathlib.Path) -> None: def help_open_system_profiler(parent) -> None: """Open the EDMC System Profiler.""" - profiler_path = pathlib.Path(config.respath_path) + profiler_path = Path(config.respath_path) try: if getattr(sys, 'frozen', False): profiler_path /= 'EDMCSystemProfiler.exe' @@ -323,7 +323,7 @@ def __init__(self, parent: tk.Tk, callback: Optional[Callable]): self.geometry(f"+{position.left}+{position.top}") # Set Log Directory - self.logfile_loc = pathlib.Path(tempfile.gettempdir()) / appname + self.logfile_loc = Path(tempfile.gettempdir()) / appname # Set minimum size to prevent content cut-off self.update_idletasks() # Update "requested size" from geometry manager @@ -1066,7 +1066,7 @@ def filebrowse(self, title, pathvar): import tkinter.filedialog directory = tkinter.filedialog.askdirectory( parent=self, - initialdir=expanduser(pathvar.get()), + initialdir=Path(pathvar.get()).expanduser(), title=title, mustexist=tk.TRUE ) @@ -1088,7 +1088,7 @@ def displaypath(self, pathvar: tk.StringVar, entryfield: tk.Entry) -> None: if sys.platform == 'win32': start = len(config.home.split('\\')) if pathvar.get().lower().startswith(config.home.lower()) else 0 display = [] - components = normpath(pathvar.get()).split('\\') + components = Path(pathvar.get()).resolve().parts buf = ctypes.create_unicode_buffer(MAX_PATH) pidsRes = ctypes.c_int() # noqa: N806 # Windows convention for i in range(start, len(components)): @@ -1233,7 +1233,7 @@ def apply(self) -> None: config.set( 'outdir', - join(config.home_path, self.outdir.get()[2:]) if self.outdir.get().startswith('~') else self.outdir.get() + str(config.home_path / self.outdir.get()[2:]) if self.outdir.get().startswith('~') else self.outdir.get() ) logdir = self.logdir.get() diff --git a/theme.py b/theme.py index 94e99f7a6..41913f42e 100644 --- a/theme.py +++ b/theme.py @@ -12,8 +12,8 @@ import os import sys +from pathlib import Path import tkinter as tk -from os.path import join from tkinter import font as tk_font from tkinter import ttk from typing import Callable @@ -38,7 +38,8 @@ AddFontResourceEx.restypes = [LPCWSTR, DWORD, LPCVOID] # type: ignore FR_PRIVATE = 0x10 FR_NOT_ENUM = 0x20 - AddFontResourceEx(join(config.respath, 'EUROCAPS.TTF'), FR_PRIVATE, 0) + font_path = Path(config.respath) / 'EUROCAPS.TTF' + AddFontResourceEx(str(font_path), FR_PRIVATE, 0) elif sys.platform == 'linux': # pyright: reportUnboundVariable=false diff --git a/ttkHyperlinkLabel.py b/ttkHyperlinkLabel.py index 6266d9fb2..0852e89b1 100644 --- a/ttkHyperlinkLabel.py +++ b/ttkHyperlinkLabel.py @@ -28,7 +28,6 @@ from tkinter import ttk from typing import Any import plug -from os import path from config import config, logger from l10n import translations as tr from monitor import monitor @@ -96,7 +95,7 @@ def open_shipyard(self, url: str): else: # Avoid file length limits if possible target = plug.invoke(url, 'EDSY', 'shipyard_url', loadout, monitor.is_beta) - file_name = path.join(config.app_dir_path, "last_shipyard.html") + file_name = config.app_dir_path / "last_shipyard.html" with open(file_name, 'w') as f: f.write(SHIPYARD_HTML_TEMPLATE.format( From f8b7a8b919f468a97a1ae6a6761f477b1fff3044 Mon Sep 17 00:00:00 2001 From: David Sangrey Date: Mon, 10 Jun 2024 17:49:13 -0400 Subject: [PATCH 08/34] [2114] Cleanup Initial Commit Some of these are already paths. --- EDMCLogging.py | 4 ++-- EDMCSystemProfiler.py | 4 ++-- EDMarketConnector.py | 4 ++-- collate.py | 2 +- companion.py | 2 +- l10n.py | 2 +- plug.py | 6 +++--- prefs.py | 2 +- theme.py | 3 +-- 9 files changed, 14 insertions(+), 15 deletions(-) diff --git a/EDMCLogging.py b/EDMCLogging.py index 09a01860d..eedd9f4e1 100644 --- a/EDMCLogging.py +++ b/EDMCLogging.py @@ -490,8 +490,8 @@ def munge_module_name(cls, frame_info: inspect.Traceback, module_name: str) -> s :return: The munged module_name. """ file_name = pathlib.Path(frame_info.filename).expanduser() - plugin_dir = pathlib.Path(config.plugin_dir_path).expanduser() - internal_plugin_dir = pathlib.Path(config.internal_plugin_dir_path).expanduser() + plugin_dir = config.plugin_dir_path.expanduser() + internal_plugin_dir = config.internal_plugin_dir_path.expanduser() # Find the first parent called 'plugins' plugin_top = file_name while plugin_top and plugin_top.name != '': diff --git a/EDMCSystemProfiler.py b/EDMCSystemProfiler.py index 8b1dcbe56..816c5e8b2 100644 --- a/EDMCSystemProfiler.py +++ b/EDMCSystemProfiler.py @@ -116,12 +116,12 @@ def main() -> None: root.withdraw() # Hide the window initially to calculate the dimensions try: icon_image = tk.PhotoImage( - file=pathlib.Path(cur_config.respath_path) / "io.edcd.EDMarketConnector.png" + file=cur_config.respath_path / "io.edcd.EDMarketConnector.png" ) root.iconphoto(True, icon_image) except tk.TclError: - root.iconbitmap(pathlib.Path(cur_config.respath_path) / "EDMarketConnector.ico") + root.iconbitmap(cur_config.respath_path / "EDMarketConnector.ico") sys_report = get_sys_report(cur_config) diff --git a/EDMarketConnector.py b/EDMarketConnector.py index f67a4fe11..c6234e0c1 100755 --- a/EDMarketConnector.py +++ b/EDMarketConnector.py @@ -468,7 +468,7 @@ def open_window(systray: 'SysTrayIcon') -> None: self.w.wm_iconbitmap(default='EDMarketConnector.ico') else: - image_path = pathlib.Path(config.respath_path) / 'io.edcd.EDMarketConnector.png' + image_path = config.respath_path / 'io.edcd.EDMarketConnector.png' self.w.tk.call('wm', 'iconphoto', self.w, '-default', image=tk.PhotoImage(file=image_path)) # TODO: Export to files and merge from them in future ? @@ -1639,7 +1639,7 @@ def shipyard_url(self, shipname: str) -> str | None: # Avoid file length limits if possible provider = config.get_str('shipyard_provider', default='EDSY') target = plug.invoke(provider, 'EDSY', 'shipyard_url', loadout, monitor.is_beta) - file_name = pathlib.Path(config.app_dir_path) / "last_shipyard.html" + file_name = config.app_dir_path / "last_shipyard.html" with open(file_name, 'w') as f: f.write(SHIPYARD_HTML_TEMPLATE.format( diff --git a/collate.py b/collate.py index 0a4130525..bba94df1b 100755 --- a/collate.py +++ b/collate.py @@ -51,7 +51,7 @@ def addcommodities(data) -> None: # noqa: CCR001 return try: - commodityfile = pathlib.Path(config.app_dir_path / 'FDevIDs' / 'commodity.csv') + commodityfile = config.app_dir_path / 'FDevIDs' / 'commodity.csv' except FileNotFoundError: commodityfile = pathlib.Path('FDevIDs/commodity.csv') commodities = {} diff --git a/companion.py b/companion.py index d91fed226..9d655bc60 100644 --- a/companion.py +++ b/companion.py @@ -1204,7 +1204,7 @@ def fixup(data: CAPIData) -> CAPIData: # noqa: C901, CCR001 # Can't be usefully if not commodity_map: # Lazily populate for f in ('commodity.csv', 'rare_commodity.csv'): - if not (Path(config.app_dir_path) / 'FDevIDs' / f).is_file(): + if not (config.app_dir_path / 'FDevIDs' / f).is_file(): logger.warning(f'FDevID file {f} not found! Generating output without these commodity name rewrites.') continue with open(config.app_dir_path / 'FDevIDs' / f, 'r') as csvfile: diff --git a/l10n.py b/l10n.py index 857b15962..8367d72ce 100755 --- a/l10n.py +++ b/l10n.py @@ -177,7 +177,7 @@ def translate(self, x: str, context: str | None = None, lang: str | None = None) if context: # TODO: There is probably a better way to go about this now. plugin_name = context[len(config.plugin_dir)+1:].split(sep)[0] - plugin_path = pathlib.Path(config.plugin_dir_path / plugin_name / LOCALISATION_DIR) + plugin_path = config.plugin_dir_path / plugin_name / LOCALISATION_DIR if lang: contents: dict[str, str] = self.contents(lang=lang, plugin_path=plugin_path) diff --git a/plug.py b/plug.py index 7c4072423..ee53e76b7 100644 --- a/plug.py +++ b/plug.py @@ -189,12 +189,12 @@ def _load_found_plugins(): # The intent here is to e.g. have EDMC-Overlay load before any plugins # that depend on it. - plugin_files = sorted(Path(config.plugin_dir_path).iterdir(), key=lambda p: ( + plugin_files = sorted(config.plugin_dir_path.iterdir(), key=lambda p: ( not (p / '__init__.py').is_file(), p.name.lower())) for plugin_file in plugin_files: name = plugin_file.name - if not (Path(config.plugin_dir_path) / name).is_dir() or name.startswith(('.', '_')): + if not (config.plugin_dir_path / name).is_dir() or name.startswith(('.', '_')): pass elif name.endswith('.disabled'): name, discard = name.rsplit('.', 1) @@ -202,7 +202,7 @@ def _load_found_plugins(): else: try: # Add plugin's folder to load path in case plugin has internal package dependencies - sys.path.append(str(Path(config.plugin_dir_path) / name)) + sys.path.append(str(config.plugin_dir_path / name)) import EDMCLogging # Create a logger for this 'found' plugin. Must be before the load.py is loaded. diff --git a/prefs.py b/prefs.py index 673d1e702..94fda4d23 100644 --- a/prefs.py +++ b/prefs.py @@ -58,7 +58,7 @@ def open_folder(file: Path) -> None: def help_open_system_profiler(parent) -> None: """Open the EDMC System Profiler.""" - profiler_path = Path(config.respath_path) + profiler_path = config.respath_path try: if getattr(sys, 'frozen', False): profiler_path /= 'EDMCSystemProfiler.exe' diff --git a/theme.py b/theme.py index 41913f42e..c00189129 100644 --- a/theme.py +++ b/theme.py @@ -12,7 +12,6 @@ import os import sys -from pathlib import Path import tkinter as tk from tkinter import font as tk_font from tkinter import ttk @@ -38,7 +37,7 @@ AddFontResourceEx.restypes = [LPCWSTR, DWORD, LPCVOID] # type: ignore FR_PRIVATE = 0x10 FR_NOT_ENUM = 0x20 - font_path = Path(config.respath) / 'EUROCAPS.TTF' + font_path = config.respath_path / 'EUROCAPS.TTF' AddFontResourceEx(str(font_path), FR_PRIVATE, 0) elif sys.platform == 'linux': From da5d317797155c8599b5b5d1e59609dcc440e1da Mon Sep 17 00:00:00 2001 From: David Sangrey Date: Mon, 10 Jun 2024 18:16:11 -0400 Subject: [PATCH 09/34] [2114] Additional Cleanup --- EDMC.py | 2 +- EDMCSystemProfiler.py | 2 +- l10n.py | 6 +++--- loadout.py | 4 ++-- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/EDMC.py b/EDMC.py index 55236b9cc..ce0db95b2 100755 --- a/EDMC.py +++ b/EDMC.py @@ -230,7 +230,7 @@ def main(): # noqa: C901, CCR001 try: monitor.currentdir = Path(config.get_str('journaldir', default=config.default_journal_dir)) if not monitor.currentdir: - monitor.currentdir = Path(config.default_journal_dir) + monitor.currentdir = config.default_journal_dir_path logger.debug(f'logdir = "{monitor.currentdir}"') logfile = monitor.journal_newest_filename(monitor.currentdir) diff --git a/EDMCSystemProfiler.py b/EDMCSystemProfiler.py index 816c5e8b2..77212db7a 100644 --- a/EDMCSystemProfiler.py +++ b/EDMCSystemProfiler.py @@ -49,7 +49,7 @@ def get_sys_report(config: config.AbstractConfig) -> str: ) ) if not monitor.currentdir: - monitor.currentdir = pathlib.Path(config.default_journal_dir) + monitor.currentdir = config.default_journal_dir_path try: logfile = monitor.journal_newest_filename(monitor.currentdir) if logfile is None: diff --git a/l10n.py b/l10n.py index 8367d72ce..ce1ab6a71 100755 --- a/l10n.py +++ b/l10n.py @@ -232,7 +232,7 @@ def respath(self) -> pathlib.Path: if __file__: return pathlib.Path(__file__).parent.joinpath(LOCALISATION_DIR).resolve() - return pathlib.Path(LOCALISATION_DIR).resolve() + return LOCALISATION_DIR.resolve() def file(self, lang: str, plugin_path: pathlib.Path | None = None) -> TextIO | None: """ @@ -396,8 +396,8 @@ def __init__(self): (match.group(4) and (match.group(4)[1:].strip()) + '. ' or '') + f'[{pathlib.Path(f).name}]' ) if seen: - target_path = pathlib.Path(LOCALISATION_DIR / 'en.template.new') - pathlib.Path(target_path).parent.mkdir(parents=True, exist_ok=True) + target_path = LOCALISATION_DIR / 'en.template.new' + target_path.parent.mkdir(parents=True, exist_ok=True) with open(target_path, 'w', encoding='utf-8') as target_file: target_file.write(f'/* Language name */\n"{LANGUAGE_ID}" = "English";\n\n') for thing in sorted(seen, key=str.lower): diff --git a/loadout.py b/loadout.py index f2efc9b48..144439c02 100644 --- a/loadout.py +++ b/loadout.py @@ -53,9 +53,9 @@ def export(data: companion.CAPIData, requested_filename: str | None = None) -> N # Write - output_directory = config.get_str('outdir') + output_directory = Path(config.get_str('outdir')) ship_time = time.strftime('%Y-%m-%dT%H.%M.%S', time.localtime(query_time)) - file_path = output_directory / Path(f"{ship}.{ship_time}.txt") + file_path = output_directory / f"{ship}.{ship_time}.txt" with open(file_path, 'wt') as h: h.write(string) From c60c0483b45149908c14300b1bb9860b2a55d062 Mon Sep 17 00:00:00 2001 From: David Sangrey Date: Sat, 15 Jun 2024 22:13:34 -0400 Subject: [PATCH 10/34] [#2262] Initial SLEF Output --- coriolis-update-files.py | 3 +- inara.json | 267 +++++++++++++++++++++++++++++++++++++++ plugins/inara.py | 53 ++++++++ ships.json | 117 +++++++++++------ 4 files changed, 400 insertions(+), 40 deletions(-) create mode 100644 inara.json diff --git a/coriolis-update-files.py b/coriolis-update-files.py index f31a2e326..6dfcc0c97 100755 --- a/coriolis-update-files.py +++ b/coriolis-update-files.py @@ -51,7 +51,8 @@ def add(modules, name, attributes) -> None: for m in list(data['Ships'].values()): name = coriolis_ship_map.get(m['properties']['name'], str(m['properties']['name'])) assert name in reverse_ship_map, name - ships[name] = {'hullMass': m['properties']['hullMass']} + ships[name] = {'hullMass': m['properties']['hullMass'], + 'reserveFuelCapacity': m['properties']['reserveFuelCapacity']} for i, bulkhead in enumerate(bulkheads): modules['_'.join([reverse_ship_map[name], 'armour', bulkhead])] = {'mass': m['bulkheads'][i]['mass']} diff --git a/inara.json b/inara.json new file mode 100644 index 000000000..1c5cba8a9 --- /dev/null +++ b/inara.json @@ -0,0 +1,267 @@ +{ + "header": { + "appName": "EDMarketConnector", + "appVersion": "5.11.1+85f5c328.DIRTY" + }, + "data": { + "FuelCapacity": { + "Main": 128, + "Reserve": 0.81 + }, + "Ship": "belugaliner", + "ShipName": "HSRV Guiding Light", + "ShipIdent": "NFC-23", + "HullValue": 69924158, + "ModulesValue": 96918434, + "Rebuy": 8342132, + "MaxJumpRange": 52.615143, + "UnladenMass": 1233.057983, + "CargoCapacity": 16, + "Modules": [ + { + "Slot": "TinyHardpoint1", + "Item": "hpt_heatsinklauncher_turret_tiny", + "ItemHealth": 1.0, + "On": true, + "Value": 3072 + }, + { + "Slot": "TinyHardpoint2", + "Item": "hpt_heatsinklauncher_turret_tiny", + "ItemHealth": 1.0, + "On": true, + "Value": 3413 + }, + { + "Slot": "TinyHardpoint3", + "Item": "hpt_heatsinklauncher_turret_tiny", + "ItemHealth": 1.0, + "On": true, + "Value": 3072 + }, + { + "Slot": "TinyHardpoint4", + "Item": "hpt_heatsinklauncher_turret_tiny", + "ItemHealth": 1.0, + "On": true, + "Value": 3072 + }, + { + "Slot": "PaintJob", + "Item": "paintjob_belugaliner_lrpo_azure", + "ItemHealth": 1.0, + "On": true, + "Priority": 1 + }, + { + "Slot": "Armour", + "Item": "belugaliner_armour_grade1", + "ItemHealth": 1.0, + "On": true, + "Priority": 1 + }, + { + "Slot": "PowerPlant", + "Item": "int_powerplant_size6_class5", + "ItemHealth": 1.0, + "On": true, + "Priority": 1, + "Value": 11382139 + }, + { + "Slot": "MainEngines", + "Item": "int_engine_size7_class2", + "ItemHealth": 1.0, + "On": true, + "Priority": 1, + "Value": 1666898 + }, + { + "Slot": "FrameShiftDrive", + "Item": "int_hyperdrive_size7_class5", + "ItemHealth": 1.0, + "On": true, + "Value": 45006196 + }, + { + "Slot": "LifeSupport", + "Item": "int_lifesupport_size8_class2", + "ItemHealth": 1.0, + "On": true, + "Value": 1530326 + }, + { + "Slot": "PowerDistributor", + "Item": "int_powerdistributor_size6_class5", + "ItemHealth": 1.0, + "On": true, + "Priority": 1, + "Value": 3049917 + }, + { + "Slot": "Radar", + "Item": "int_sensors_size5_class2", + "ItemHealth": 1.0, + "On": true, + "Priority": 1, + "Value": 69713 + }, + { + "Slot": "FuelTank", + "Item": "int_fueltank_size7_class3", + "ItemHealth": 1.0, + "On": true, + "Priority": 1, + "Value": 1562752 + }, + { + "Slot": "Decal1", + "Item": "decal_distantworlds2", + "ItemHealth": 1.0, + "On": true, + "Priority": 1 + }, + { + "Slot": "Decal2", + "Item": "decal_distantworlds2", + "ItemHealth": 1.0, + "On": true, + "Priority": 1 + }, + { + "Slot": "Decal3", + "Item": "decal_distantworlds2", + "ItemHealth": 1.0, + "On": true, + "Priority": 1 + }, + { + "Slot": "ShipName0", + "Item": "nameplate_practical01_black", + "ItemHealth": 1.0, + "On": true, + "Priority": 1 + }, + { + "Slot": "ShipName1", + "Item": "nameplate_practical01_black", + "ItemHealth": 1.0, + "On": true, + "Priority": 1 + }, + { + "Slot": "ShipID0", + "Item": "nameplate_shipid_singleline_black", + "ItemHealth": 1.0, + "On": true, + "Priority": 1 + }, + { + "Slot": "ShipID1", + "Item": "nameplate_shipid_singleline_black", + "ItemHealth": 1.0, + "On": true, + "Priority": 1 + }, + { + "Slot": "Slot01_Size6", + "Item": "int_fuelscoop_size6_class5", + "ItemHealth": 1.0, + "On": true, + "Value": 25240068 + }, + { + "Slot": "Slot02_Size6", + "Item": "int_repairer_size6_class2", + "ItemHealth": 1.0, + "On": true, + "Value": 497429 + }, + { + "Slot": "Slot03_Size6", + "Item": "int_shieldgenerator_size5_class2", + "ItemHealth": 1.0, + "On": true, + "Value": 165879 + }, + { + "Slot": "Slot04_Size6", + "Item": "int_dronecontrol_repair_size5_class2", + "ItemHealth": 1.0, + "On": true, + "Value": 85293 + }, + { + "Slot": "Slot05_Size5", + "Item": "int_guardianfsdbooster_size5", + "ItemHealth": 1.0, + "On": true, + "Value": 5688921 + }, + { + "Slot": "Slot07_Size4", + "Item": "int_cargorack_size4_class1", + "ItemHealth": 1.0, + "On": true, + "Priority": 1, + "Value": 30124 + }, + { + "Slot": "Slot11_Size3", + "Item": "int_detailedsurfacescanner_tiny", + "ItemHealth": 1.0, + "On": true, + "Value": 219375 + }, + { + "Slot": "Slot12_Size1", + "Item": "int_repairer_size1_class5", + "ItemHealth": 1.0, + "On": false, + "Value": 710775 + }, + { + "Slot": "PlanetaryApproachSuite", + "Item": "int_planetapproachsuite_advanced", + "ItemHealth": 1.0, + "On": true, + "Priority": 1 + }, + { + "Slot": "WeaponColour", + "Item": "weaponcustomisation_red", + "ItemHealth": 1.0, + "On": true, + "Priority": 1 + }, + { + "Slot": "EngineColour", + "Item": "enginecustomisation_orange", + "ItemHealth": 1.0, + "On": true, + "Priority": 1 + }, + { + "Slot": "VesselVoice", + "Item": "voicepack_verity", + "ItemHealth": 1.0, + "On": true, + "Priority": 1 + }, + { + "Slot": "ShipCockpit", + "Item": "belugaliner_cockpit", + "ItemHealth": 1.0, + "On": true, + "Priority": 1 + }, + { + "Slot": "CargoHatch", + "Item": "modularcargobaydoor", + "ItemHealth": 1.0, + "On": false, + "Priority": 1 + } + ] + } +} \ No newline at end of file diff --git a/plugins/inara.py b/plugins/inara.py index f2c9830e7..1cae5c64c 100644 --- a/plugins/inara.py +++ b/plugins/inara.py @@ -35,6 +35,7 @@ import edmc_data import killswitch import myNotebook as nb # noqa: N813 +from edshipyard import ships import plug import timeout_session from companion import CAPIData @@ -870,6 +871,7 @@ def journal_entry( # noqa: C901, CCR001 cur_ship['shipRebuyCost'] = state['Rebuy'] new_add_event('setCommanderShip', entry['timestamp'], cur_ship) + make_slef(state, entry) # Stored modules if event_name == 'StoredModules': @@ -1476,6 +1478,57 @@ def make_loadout(state: dict[str, Any]) -> dict[str, Any]: # noqa: CCR001 } +def make_slef(state: dict[str, Any], entry) -> None: + initial_dict = { + "header": {"appName": appname, "appVersion": str(appversion())} + } + data_dict = {} + loadout = make_loadout(state) + modules = loadout['shipLoadout'] + mod_dict = [] + for module in modules: + if module['slotName']: + builder = { + 'Slot': module['slotName'], + 'Item': module['itemName'] + } + if module.get('itemHealth'): + builder.update({'ItemHealth': module['itemHealth']}) + if module.get('isOn'): + builder.update({'On': True}) + elif not module.get('isOn'): + builder.update({'On': False}) + if module.get('itemPriority'): + builder.update({'Priority': module['itemPriority']}) + if module.get('itemValue'): + builder.update({'Value': module['itemValue']}) + if not module.get('itemValue'): + builder.update({'Value': 0}) + if module.get('slotName') == 'FuelTank': + cap = module['itemName'].split('size') + cap = cap[1].split('_') + cap = 2**int(cap[0]) + ship = edmc_data.ship_name_map[state["ShipType"]] + fuel = {'Main': cap, 'Reserve': ships[ship]['reserveFuelCapacity']} + data_dict.update({"FuelCapacity": fuel}) + mod_dict.append(builder) + data_dict.update({ + 'Ship': state["ShipType"], + 'ShipName': state['ShipName'], + 'ShipIdent': state['ShipIdent'], + 'HullValue': state['HullValue'], + 'ModulesValue': state['ModulesValue'], + 'Rebuy': state['Rebuy'], + 'MaxJumpRange': entry['MaxJumpRange'], + 'UnladenMass': entry['UnladenMass'], + 'CargoCapacity': entry['CargoCapacity'], + 'Modules': mod_dict, + }) + initial_dict.update({'data': data_dict}) + json.dump(initial_dict, open('inara.json', 'w'), indent=4) + return None + + def new_add_event( name: str, timestamp: str, diff --git a/ships.json b/ships.json index 65a2989e2..ac05d5d8f 100644 --- a/ships.json +++ b/ships.json @@ -1,119 +1,158 @@ { "Adder": { - "hullMass": 35 + "hullMass": 35, + "reserveFuelCapacity": 0.36 }, "Alliance Challenger": { - "hullMass": 450 + "hullMass": 450, + "reserveFuelCapacity": 0.77 }, "Alliance Chieftain": { - "hullMass": 400 + "hullMass": 400, + "reserveFuelCapacity": 0.77 }, "Alliance Crusader": { - "hullMass": 500 + "hullMass": 500, + "reserveFuelCapacity": 0.77 }, "Anaconda": { - "hullMass": 400 + "hullMass": 400, + "reserveFuelCapacity": 1.07 }, "Asp Explorer": { - "hullMass": 280 + "hullMass": 280, + "reserveFuelCapacity": 0.63 }, "Asp Scout": { - "hullMass": 150 + "hullMass": 150, + "reserveFuelCapacity": 0.47 }, "Beluga Liner": { - "hullMass": 950 + "hullMass": 950, + "reserveFuelCapacity": 0.81 }, "Cobra MkIII": { - "hullMass": 180 + "hullMass": 180, + "reserveFuelCapacity": 0.49 }, "Cobra MkIV": { - "hullMass": 210 + "hullMass": 210, + "reserveFuelCapacity": 0.51 }, "Diamondback Explorer": { - "hullMass": 260 + "hullMass": 260, + "reserveFuelCapacity": 0.52 }, "Diamondback Scout": { - "hullMass": 170 + "hullMass": 170, + "reserveFuelCapacity": 0.49 }, "Dolphin": { - "hullMass": 140 + "hullMass": 140, + "reserveFuelCapacity": 0.5 }, "Eagle": { - "hullMass": 50 + "hullMass": 50, + "reserveFuelCapacity": 0.34 }, "Federal Assault Ship": { - "hullMass": 480 + "hullMass": 480, + "reserveFuelCapacity": 0.72 }, "Federal Corvette": { - "hullMass": 900 + "hullMass": 900, + "reserveFuelCapacity": 1.13 }, "Federal Dropship": { - "hullMass": 580 + "hullMass": 580, + "reserveFuelCapacity": 0.83 }, "Federal Gunship": { - "hullMass": 580 + "hullMass": 580, + "reserveFuelCapacity": 0.82 }, "Fer-de-Lance": { - "hullMass": 250 + "hullMass": 250, + "reserveFuelCapacity": 0.67 }, "Hauler": { - "hullMass": 14 + "hullMass": 14, + "reserveFuelCapacity": 0.25 }, "Imperial Clipper": { - "hullMass": 400 + "hullMass": 400, + "reserveFuelCapacity": 0.74 }, "Imperial Courier": { - "hullMass": 35 + "hullMass": 35, + "reserveFuelCapacity": 0.41 }, "Imperial Cutter": { - "hullMass": 1100 + "hullMass": 1100, + "reserveFuelCapacity": 1.16 }, "Imperial Eagle": { - "hullMass": 50 + "hullMass": 50, + "reserveFuelCapacity": 0.37 }, "Keelback": { - "hullMass": 180 + "hullMass": 180, + "reserveFuelCapacity": 0.39 }, "Krait MkII": { - "hullMass": 320 + "hullMass": 320, + "reserveFuelCapacity": 0.63 }, "Krait Phantom": { - "hullMass": 270 + "hullMass": 270, + "reserveFuelCapacity": 0.63 }, "Mamba": { - "hullMass": 250 + "hullMass": 250, + "reserveFuelCapacity": 0.5 }, "Orca": { - "hullMass": 290 + "hullMass": 290, + "reserveFuelCapacity": 0.79 }, "Python": { - "hullMass": 350 + "hullMass": 350, + "reserveFuelCapacity": 0.83 }, "Python Mk II": { - "hullMass": 450 + "hullMass": 450, + "reserveFuelCapacity": 0.83 }, "Sidewinder": { - "hullMass": 25 + "hullMass": 25, + "reserveFuelCapacity": 0.3 }, "Type-10 Defender": { - "hullMass": 1200 + "hullMass": 1200, + "reserveFuelCapacity": 0.77 }, "Type-6 Transporter": { - "hullMass": 155 + "hullMass": 155, + "reserveFuelCapacity": 0.39 }, "Type-7 Transporter": { - "hullMass": 350 + "hullMass": 350, + "reserveFuelCapacity": 0.52 }, "Type-9 Heavy": { - "hullMass": 850 + "hullMass": 850, + "reserveFuelCapacity": 0.77 }, "Viper MkIII": { - "hullMass": 50 + "hullMass": 50, + "reserveFuelCapacity": 0.41 }, "Viper MkIV": { - "hullMass": 190 + "hullMass": 190, + "reserveFuelCapacity": 0.46 }, "Vulture": { - "hullMass": 230 + "hullMass": 230, + "reserveFuelCapacity": 0.57 } } \ No newline at end of file From fb1e3dcab6268a56cad742b3fcf259c3ba17450f Mon Sep 17 00:00:00 2001 From: David Sangrey Date: Mon, 17 Jun 2024 14:51:56 -0400 Subject: [PATCH 11/34] [Minor] Fix MyPy Complaint --- scripts/build_changelog.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/build_changelog.py b/scripts/build_changelog.py index f0c019943..6b0507740 100644 --- a/scripts/build_changelog.py +++ b/scripts/build_changelog.py @@ -34,9 +34,9 @@ def get_changelog() -> tuple[str, str]: changelog[1] = "\n".join(changelog[1].strip().split("\n")[:-2]) version = changelog[0] version = version.split(" ")[1] - changelog = changelog[1].strip() + final_changelog = changelog[1].strip() - return changelog, version + return final_changelog, version def build_html(md_changelog: str, version: str) -> str: From 87f59a54fa41c1333e0fae680884a4da59a8c2fb Mon Sep 17 00:00:00 2001 From: David Sangrey Date: Mon, 17 Jun 2024 16:38:20 -0400 Subject: [PATCH 12/34] [RELEASE] 5.10.2 --- ChangeLog.md | 12 ++++++++++++ config/__init__.py | 2 +- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/ChangeLog.md b/ChangeLog.md index 1c2ac9c29..2f9248f1d 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -6,6 +6,18 @@ This is the master changelog for Elite Dangerous Market Connector. Entries are in the source (not distributed with the Windows installer) for the currently used version. --- +Release 5.11.2 +=== + +This release fixes a bug where minimizing to the system tray could cause the program to not un-minimize. + +**Changes and Enhancements** +* Updated Translations +* Added a developer utilty to help speed up changelog development + +**Bug Fixes** +* Fixed a bug where minimizing to the system tray could cause the program to not un-minimize. + Release 5.11.1 === diff --git a/config/__init__.py b/config/__init__.py index 8dfcf46b3..4fe565e45 100644 --- a/config/__init__.py +++ b/config/__init__.py @@ -52,7 +52,7 @@ # # Major.Minor.Patch(-prerelease)(+buildmetadata) # NB: Do *not* import this, use the functions appversion() and appversion_nobuild() -_static_appversion = '5.11.1' +_static_appversion = '5.11.2' _cached_version: semantic_version.Version | None = None copyright = '© 2015-2019 Jonathan Harris, 2020-2024 EDCD' From 0aa1128094e36a09e4293096ad40d064c00ba96e Mon Sep 17 00:00:00 2001 From: David Sangrey Date: Mon, 17 Jun 2024 17:09:30 -0400 Subject: [PATCH 13/34] [Docs] Fix Typo in Changelog >:( --- ChangeLog.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ChangeLog.md b/ChangeLog.md index 2f9248f1d..300235668 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -13,7 +13,7 @@ This release fixes a bug where minimizing to the system tray could cause the pro **Changes and Enhancements** * Updated Translations -* Added a developer utilty to help speed up changelog development +* Added a developer utility to help speed up changelog development **Bug Fixes** * Fixed a bug where minimizing to the system tray could cause the program to not un-minimize. From fcd7b8da0f458de15960720e2f48e1cd1c81215e Mon Sep 17 00:00:00 2001 From: David Sangrey Date: Mon, 17 Jun 2024 18:20:53 -0400 Subject: [PATCH 14/34] [2215] Clarify Config Imports --- EDMCLogging.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/EDMCLogging.py b/EDMCLogging.py index c09de41e5..3f3e79b10 100644 --- a/EDMCLogging.py +++ b/EDMCLogging.py @@ -50,8 +50,7 @@ from time import gmtime from traceback import print_exc from typing import TYPE_CHECKING, cast -import config as config_mod -from config import appcmdname, appname, config +from config import appcmdname, appname, config, trace_on # TODO: Tests: # @@ -102,7 +101,7 @@ def _trace_if(self: logging.Logger, condition: str, message: str, *args, **kwargs) -> None: - if any(fnmatch(condition, p) for p in config_mod.trace_on): + if any(fnmatch(condition, p) for p in trace_on): self._log(logging.TRACE, message, args, **kwargs) # type: ignore # we added it return From d478a68bc61de1fdc20c6ca526e4c1740944e672 Mon Sep 17 00:00:00 2001 From: David Sangrey Date: Tue, 18 Jun 2024 21:15:11 -0400 Subject: [PATCH 15/34] [2262] Simplify SLEF Building --- plugins/inara.py | 55 +++++++++++++++--------------------------------- 1 file changed, 17 insertions(+), 38 deletions(-) diff --git a/plugins/inara.py b/plugins/inara.py index 1cae5c64c..3fd42f34d 100644 --- a/plugins/inara.py +++ b/plugins/inara.py @@ -871,7 +871,7 @@ def journal_entry( # noqa: C901, CCR001 cur_ship['shipRebuyCost'] = state['Rebuy'] new_add_event('setCommanderShip', entry['timestamp'], cur_ship) - make_slef(state, entry) + make_slef(entry) # Stored modules if event_name == 'StoredModules': @@ -1478,51 +1478,30 @@ def make_loadout(state: dict[str, Any]) -> dict[str, Any]: # noqa: CCR001 } -def make_slef(state: dict[str, Any], entry) -> None: +def make_slef(entry) -> None: initial_dict = { "header": {"appName": appname, "appVersion": str(appversion())} } data_dict = {} - loadout = make_loadout(state) - modules = loadout['shipLoadout'] - mod_dict = [] - for module in modules: - if module['slotName']: - builder = { - 'Slot': module['slotName'], - 'Item': module['itemName'] - } - if module.get('itemHealth'): - builder.update({'ItemHealth': module['itemHealth']}) - if module.get('isOn'): - builder.update({'On': True}) - elif not module.get('isOn'): - builder.update({'On': False}) - if module.get('itemPriority'): - builder.update({'Priority': module['itemPriority']}) - if module.get('itemValue'): - builder.update({'Value': module['itemValue']}) - if not module.get('itemValue'): - builder.update({'Value': 0}) - if module.get('slotName') == 'FuelTank': - cap = module['itemName'].split('size') - cap = cap[1].split('_') - cap = 2**int(cap[0]) - ship = edmc_data.ship_name_map[state["ShipType"]] - fuel = {'Main': cap, 'Reserve': ships[ship]['reserveFuelCapacity']} - data_dict.update({"FuelCapacity": fuel}) - mod_dict.append(builder) + for module in entry['Modules']: + if module.get('Slot') == 'FuelTank': + cap = module['Item'].split('size') + cap = cap[1].split('_') + cap = 2 ** int(cap[0]) + ship = edmc_data.ship_name_map[entry["Ship"]] + fuel = {'Main': cap, 'Reserve': ships[ship]['reserveFuelCapacity']} + data_dict.update({"FuelCapacity": fuel}) data_dict.update({ - 'Ship': state["ShipType"], - 'ShipName': state['ShipName'], - 'ShipIdent': state['ShipIdent'], - 'HullValue': state['HullValue'], - 'ModulesValue': state['ModulesValue'], - 'Rebuy': state['Rebuy'], + 'Ship': entry["Ship"], + 'ShipName': entry['ShipName'], + 'ShipIdent': entry['ShipIdent'], + 'HullValue': entry['HullValue'], + 'ModulesValue': entry['ModulesValue'], + 'Rebuy': entry['Rebuy'], 'MaxJumpRange': entry['MaxJumpRange'], 'UnladenMass': entry['UnladenMass'], 'CargoCapacity': entry['CargoCapacity'], - 'Modules': mod_dict, + 'Modules': entry['Modules'], }) initial_dict.update({'data': data_dict}) json.dump(initial_dict, open('inara.json', 'w'), indent=4) From 0cc35db96b560f971a78b3d4f30003a37805bc18 Mon Sep 17 00:00:00 2001 From: David Sangrey Date: Tue, 18 Jun 2024 22:02:41 -0400 Subject: [PATCH 16/34] [2262] SLEF to this var --- plugins/inara.py | 6 +++++- ttkHyperlinkLabel.py | 10 ++++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/plugins/inara.py b/plugins/inara.py index 3fd42f34d..c3f030c44 100644 --- a/plugins/inara.py +++ b/plugins/inara.py @@ -107,6 +107,7 @@ def __init__(self): self.fleet: list[dict[str, Any]] | None = None self.shipswap: bool = False # just swapped ship self.on_foot = False + self.SLEF: str | None = None self.timer_run = True @@ -1504,7 +1505,10 @@ def make_slef(entry) -> None: 'Modules': entry['Modules'], }) initial_dict.update({'data': data_dict}) - json.dump(initial_dict, open('inara.json', 'w'), indent=4) + output = json.dumps(initial_dict, indent=4) + this.SLEF = str(output) + print('set output') + print(type(this.SLEF)) return None diff --git a/ttkHyperlinkLabel.py b/ttkHyperlinkLabel.py index 6266d9fb2..95ebd2e75 100644 --- a/ttkHyperlinkLabel.py +++ b/ttkHyperlinkLabel.py @@ -194,6 +194,11 @@ def _contextmenu(self, event: tk.Event) -> None: menu.add_command(label=tr.tl('Copy'), command=self.copy) # As in Copy and Paste if self.name == 'ship': + # TODO: Get SLEF from Inara + # print(self.SLEF) + # menu.add_command(label=tr.tl('Copy INARA SLEF'), command=lambda: self.copy_slef(self), state=tk.DISABLED) + # menu.entryconfigure(1, state=self.SLEF and tk.NORMAL or tk.DISABLED) + menu.add_separator() for url in plug.provides('shipyard_url'): menu.add_command( @@ -224,3 +229,8 @@ def copy(self) -> None: """Copy the current text to the clipboard.""" self.clipboard_clear() self.clipboard_append(self['text']) + + # def copy_slef(self, this) -> None: + # """Copy the current text to the clipboard.""" + # self.clipboard_clear() + # self.clipboard_append(this.SLEF) From 3b6259276a9fdfb606abbd59fd8aca488f1c32e3 Mon Sep 17 00:00:00 2001 From: David Sangrey Date: Sun, 23 Jun 2024 13:05:39 -0400 Subject: [PATCH 17/34] [2262] Move SLEF to Monitor --- inara.json | 267 ------------------------------------------- monitor.py | 34 +++++- plugins/inara.py | 35 ------ ttkHyperlinkLabel.py | 14 +-- 4 files changed, 39 insertions(+), 311 deletions(-) delete mode 100644 inara.json diff --git a/inara.json b/inara.json deleted file mode 100644 index 1c5cba8a9..000000000 --- a/inara.json +++ /dev/null @@ -1,267 +0,0 @@ -{ - "header": { - "appName": "EDMarketConnector", - "appVersion": "5.11.1+85f5c328.DIRTY" - }, - "data": { - "FuelCapacity": { - "Main": 128, - "Reserve": 0.81 - }, - "Ship": "belugaliner", - "ShipName": "HSRV Guiding Light", - "ShipIdent": "NFC-23", - "HullValue": 69924158, - "ModulesValue": 96918434, - "Rebuy": 8342132, - "MaxJumpRange": 52.615143, - "UnladenMass": 1233.057983, - "CargoCapacity": 16, - "Modules": [ - { - "Slot": "TinyHardpoint1", - "Item": "hpt_heatsinklauncher_turret_tiny", - "ItemHealth": 1.0, - "On": true, - "Value": 3072 - }, - { - "Slot": "TinyHardpoint2", - "Item": "hpt_heatsinklauncher_turret_tiny", - "ItemHealth": 1.0, - "On": true, - "Value": 3413 - }, - { - "Slot": "TinyHardpoint3", - "Item": "hpt_heatsinklauncher_turret_tiny", - "ItemHealth": 1.0, - "On": true, - "Value": 3072 - }, - { - "Slot": "TinyHardpoint4", - "Item": "hpt_heatsinklauncher_turret_tiny", - "ItemHealth": 1.0, - "On": true, - "Value": 3072 - }, - { - "Slot": "PaintJob", - "Item": "paintjob_belugaliner_lrpo_azure", - "ItemHealth": 1.0, - "On": true, - "Priority": 1 - }, - { - "Slot": "Armour", - "Item": "belugaliner_armour_grade1", - "ItemHealth": 1.0, - "On": true, - "Priority": 1 - }, - { - "Slot": "PowerPlant", - "Item": "int_powerplant_size6_class5", - "ItemHealth": 1.0, - "On": true, - "Priority": 1, - "Value": 11382139 - }, - { - "Slot": "MainEngines", - "Item": "int_engine_size7_class2", - "ItemHealth": 1.0, - "On": true, - "Priority": 1, - "Value": 1666898 - }, - { - "Slot": "FrameShiftDrive", - "Item": "int_hyperdrive_size7_class5", - "ItemHealth": 1.0, - "On": true, - "Value": 45006196 - }, - { - "Slot": "LifeSupport", - "Item": "int_lifesupport_size8_class2", - "ItemHealth": 1.0, - "On": true, - "Value": 1530326 - }, - { - "Slot": "PowerDistributor", - "Item": "int_powerdistributor_size6_class5", - "ItemHealth": 1.0, - "On": true, - "Priority": 1, - "Value": 3049917 - }, - { - "Slot": "Radar", - "Item": "int_sensors_size5_class2", - "ItemHealth": 1.0, - "On": true, - "Priority": 1, - "Value": 69713 - }, - { - "Slot": "FuelTank", - "Item": "int_fueltank_size7_class3", - "ItemHealth": 1.0, - "On": true, - "Priority": 1, - "Value": 1562752 - }, - { - "Slot": "Decal1", - "Item": "decal_distantworlds2", - "ItemHealth": 1.0, - "On": true, - "Priority": 1 - }, - { - "Slot": "Decal2", - "Item": "decal_distantworlds2", - "ItemHealth": 1.0, - "On": true, - "Priority": 1 - }, - { - "Slot": "Decal3", - "Item": "decal_distantworlds2", - "ItemHealth": 1.0, - "On": true, - "Priority": 1 - }, - { - "Slot": "ShipName0", - "Item": "nameplate_practical01_black", - "ItemHealth": 1.0, - "On": true, - "Priority": 1 - }, - { - "Slot": "ShipName1", - "Item": "nameplate_practical01_black", - "ItemHealth": 1.0, - "On": true, - "Priority": 1 - }, - { - "Slot": "ShipID0", - "Item": "nameplate_shipid_singleline_black", - "ItemHealth": 1.0, - "On": true, - "Priority": 1 - }, - { - "Slot": "ShipID1", - "Item": "nameplate_shipid_singleline_black", - "ItemHealth": 1.0, - "On": true, - "Priority": 1 - }, - { - "Slot": "Slot01_Size6", - "Item": "int_fuelscoop_size6_class5", - "ItemHealth": 1.0, - "On": true, - "Value": 25240068 - }, - { - "Slot": "Slot02_Size6", - "Item": "int_repairer_size6_class2", - "ItemHealth": 1.0, - "On": true, - "Value": 497429 - }, - { - "Slot": "Slot03_Size6", - "Item": "int_shieldgenerator_size5_class2", - "ItemHealth": 1.0, - "On": true, - "Value": 165879 - }, - { - "Slot": "Slot04_Size6", - "Item": "int_dronecontrol_repair_size5_class2", - "ItemHealth": 1.0, - "On": true, - "Value": 85293 - }, - { - "Slot": "Slot05_Size5", - "Item": "int_guardianfsdbooster_size5", - "ItemHealth": 1.0, - "On": true, - "Value": 5688921 - }, - { - "Slot": "Slot07_Size4", - "Item": "int_cargorack_size4_class1", - "ItemHealth": 1.0, - "On": true, - "Priority": 1, - "Value": 30124 - }, - { - "Slot": "Slot11_Size3", - "Item": "int_detailedsurfacescanner_tiny", - "ItemHealth": 1.0, - "On": true, - "Value": 219375 - }, - { - "Slot": "Slot12_Size1", - "Item": "int_repairer_size1_class5", - "ItemHealth": 1.0, - "On": false, - "Value": 710775 - }, - { - "Slot": "PlanetaryApproachSuite", - "Item": "int_planetapproachsuite_advanced", - "ItemHealth": 1.0, - "On": true, - "Priority": 1 - }, - { - "Slot": "WeaponColour", - "Item": "weaponcustomisation_red", - "ItemHealth": 1.0, - "On": true, - "Priority": 1 - }, - { - "Slot": "EngineColour", - "Item": "enginecustomisation_orange", - "ItemHealth": 1.0, - "On": true, - "Priority": 1 - }, - { - "Slot": "VesselVoice", - "Item": "voicepack_verity", - "ItemHealth": 1.0, - "On": true, - "Priority": 1 - }, - { - "Slot": "ShipCockpit", - "Item": "belugaliner_cockpit", - "ItemHealth": 1.0, - "On": true, - "Priority": 1 - }, - { - "Slot": "CargoHatch", - "Item": "modularcargobaydoor", - "ItemHealth": 1.0, - "On": false, - "Priority": 1 - } - ] - } -} \ No newline at end of file diff --git a/monitor.py b/monitor.py index 9d33f5eb5..ae0f3ad75 100644 --- a/monitor.py +++ b/monitor.py @@ -21,9 +21,10 @@ from typing import TYPE_CHECKING, Any, BinaryIO, MutableMapping import semantic_version import util_ships -from config import config -from edmc_data import edmc_suit_shortnames, edmc_suit_symbol_localised +from config import config, appname, appversion +from edmc_data import edmc_suit_shortnames, edmc_suit_symbol_localised, ship_name_map from EDMCLogging import get_main_logger +from edshipyard import ships if TYPE_CHECKING: import tkinter @@ -109,6 +110,7 @@ def __init__(self) -> None: self.group: str | None = None self.cmdr: str | None = None self.started: int | None = None # Timestamp of the LoadGame event + self.slef: str | None = None self._navroute_retries_remaining = 0 self._last_navroute_journal_timestamp: float | None = None @@ -701,6 +703,34 @@ def parse_entry(self, line: bytes) -> MutableMapping[str, Any]: # noqa: C901, C module.pop('AmmoInHopper') self.state['Modules'][module['Slot']] = module + # SLEF + initial_dict = { + "header": {"appName": appname, "appVersion": str(appversion())} + } + data_dict = {} + for module in entry['Modules']: + if module.get('Slot') == 'FuelTank': + cap = module['Item'].split('size') + cap = cap[1].split('_') + cap = 2 ** int(cap[0]) + ship = ship_name_map[entry["Ship"]] + fuel = {'Main': cap, 'Reserve': ships[ship]['reserveFuelCapacity']} + data_dict.update({"FuelCapacity": fuel}) + data_dict.update({ + 'Ship': entry["Ship"], + 'ShipName': entry['ShipName'], + 'ShipIdent': entry['ShipIdent'], + 'HullValue': entry['HullValue'], + 'ModulesValue': entry['ModulesValue'], + 'Rebuy': entry['Rebuy'], + 'MaxJumpRange': entry['MaxJumpRange'], + 'UnladenMass': entry['UnladenMass'], + 'CargoCapacity': entry['CargoCapacity'], + 'Modules': entry['Modules'], + }) + initial_dict.update({'data': data_dict}) + output = json.dumps(initial_dict, indent=4) + self.slef = str(output) elif event_type == 'modulebuy': self.state['Modules'][entry['Slot']] = { diff --git a/plugins/inara.py b/plugins/inara.py index c3f030c44..a4cc0e557 100644 --- a/plugins/inara.py +++ b/plugins/inara.py @@ -35,7 +35,6 @@ import edmc_data import killswitch import myNotebook as nb # noqa: N813 -from edshipyard import ships import plug import timeout_session from companion import CAPIData @@ -872,7 +871,6 @@ def journal_entry( # noqa: C901, CCR001 cur_ship['shipRebuyCost'] = state['Rebuy'] new_add_event('setCommanderShip', entry['timestamp'], cur_ship) - make_slef(entry) # Stored modules if event_name == 'StoredModules': @@ -1479,39 +1477,6 @@ def make_loadout(state: dict[str, Any]) -> dict[str, Any]: # noqa: CCR001 } -def make_slef(entry) -> None: - initial_dict = { - "header": {"appName": appname, "appVersion": str(appversion())} - } - data_dict = {} - for module in entry['Modules']: - if module.get('Slot') == 'FuelTank': - cap = module['Item'].split('size') - cap = cap[1].split('_') - cap = 2 ** int(cap[0]) - ship = edmc_data.ship_name_map[entry["Ship"]] - fuel = {'Main': cap, 'Reserve': ships[ship]['reserveFuelCapacity']} - data_dict.update({"FuelCapacity": fuel}) - data_dict.update({ - 'Ship': entry["Ship"], - 'ShipName': entry['ShipName'], - 'ShipIdent': entry['ShipIdent'], - 'HullValue': entry['HullValue'], - 'ModulesValue': entry['ModulesValue'], - 'Rebuy': entry['Rebuy'], - 'MaxJumpRange': entry['MaxJumpRange'], - 'UnladenMass': entry['UnladenMass'], - 'CargoCapacity': entry['CargoCapacity'], - 'Modules': entry['Modules'], - }) - initial_dict.update({'data': data_dict}) - output = json.dumps(initial_dict, indent=4) - this.SLEF = str(output) - print('set output') - print(type(this.SLEF)) - return None - - def new_add_event( name: str, timestamp: str, diff --git a/ttkHyperlinkLabel.py b/ttkHyperlinkLabel.py index 95ebd2e75..5ca4feb4b 100644 --- a/ttkHyperlinkLabel.py +++ b/ttkHyperlinkLabel.py @@ -195,9 +195,9 @@ def _contextmenu(self, event: tk.Event) -> None: if self.name == 'ship': # TODO: Get SLEF from Inara - # print(self.SLEF) - # menu.add_command(label=tr.tl('Copy INARA SLEF'), command=lambda: self.copy_slef(self), state=tk.DISABLED) - # menu.entryconfigure(1, state=self.SLEF and tk.NORMAL or tk.DISABLED) + print(bool(monitor.slef)) + menu.add_command(label=tr.tl('Copy INARA SLEF'), command=self.copy_slef, state=tk.DISABLED) + menu.entryconfigure(1, state=monitor.slef and tk.NORMAL or tk.DISABLED) menu.add_separator() for url in plug.provides('shipyard_url'): @@ -230,7 +230,7 @@ def copy(self) -> None: self.clipboard_clear() self.clipboard_append(self['text']) - # def copy_slef(self, this) -> None: - # """Copy the current text to the clipboard.""" - # self.clipboard_clear() - # self.clipboard_append(this.SLEF) + def copy_slef(self) -> None: + """Copy the current text to the clipboard.""" + self.clipboard_clear() + self.clipboard_append(monitor.slef) From 47192ce674375f4b87590167919547c25a070d92 Mon Sep 17 00:00:00 2001 From: David Sangrey Date: Sun, 23 Jun 2024 13:06:52 -0400 Subject: [PATCH 18/34] [2262] Remove Dev self.SLEF --- plugins/inara.py | 1 - 1 file changed, 1 deletion(-) diff --git a/plugins/inara.py b/plugins/inara.py index a4cc0e557..f2c9830e7 100644 --- a/plugins/inara.py +++ b/plugins/inara.py @@ -106,7 +106,6 @@ def __init__(self): self.fleet: list[dict[str, Any]] | None = None self.shipswap: bool = False # just swapped ship self.on_foot = False - self.SLEF: str | None = None self.timer_run = True From d9f7b72e4655777b70ec4e1cfa0b4183231fe518 Mon Sep 17 00:00:00 2001 From: David Sangrey Date: Sun, 23 Jun 2024 13:09:13 -0400 Subject: [PATCH 19/34] [2262] Update Type Hinting --- monitor.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/monitor.py b/monitor.py index ae0f3ad75..29970ec03 100644 --- a/monitor.py +++ b/monitor.py @@ -704,7 +704,7 @@ def parse_entry(self, line: bytes) -> MutableMapping[str, Any]: # noqa: C901, C self.state['Modules'][module['Slot']] = module # SLEF - initial_dict = { + initial_dict: dict[str, dict[str, Any]] = { "header": {"appName": appname, "appVersion": str(appversion())} } data_dict = {} From 5a9970e6c7cf2191662ebde0bf9d0fa873d7acd6 Mon Sep 17 00:00:00 2001 From: David Sangrey Date: Sun, 23 Jun 2024 13:10:57 -0400 Subject: [PATCH 20/34] [2262] Remove Debug Code --- ttkHyperlinkLabel.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/ttkHyperlinkLabel.py b/ttkHyperlinkLabel.py index 5ca4feb4b..6678530af 100644 --- a/ttkHyperlinkLabel.py +++ b/ttkHyperlinkLabel.py @@ -194,8 +194,6 @@ def _contextmenu(self, event: tk.Event) -> None: menu.add_command(label=tr.tl('Copy'), command=self.copy) # As in Copy and Paste if self.name == 'ship': - # TODO: Get SLEF from Inara - print(bool(monitor.slef)) menu.add_command(label=tr.tl('Copy INARA SLEF'), command=self.copy_slef, state=tk.DISABLED) menu.entryconfigure(1, state=monitor.slef and tk.NORMAL or tk.DISABLED) From 067bef628e64716c3bd655e52369d2de474f09ad Mon Sep 17 00:00:00 2001 From: David Sangrey Date: Sun, 23 Jun 2024 13:14:41 -0400 Subject: [PATCH 21/34] [2262] Update Capitalizations and add Translations --- L10n/en.template | 3 +++ ttkHyperlinkLabel.py | 3 ++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/L10n/en.template b/L10n/en.template index 1c1e110f4..347347878 100644 --- a/L10n/en.template +++ b/L10n/en.template @@ -813,3 +813,6 @@ /* ttkHyperlinkLabel.py: Open Element In Selected Provider; */ "Open in {URL}" = "Open in {URL}"; + +/* ttkHyperlinkLabel.py: Copy the Inara SLEF Format of the active ship to the clipboard; */ +"Copy Inara SLEF" = "Copy Inara SLEF"; diff --git a/ttkHyperlinkLabel.py b/ttkHyperlinkLabel.py index 6678530af..9f4456909 100644 --- a/ttkHyperlinkLabel.py +++ b/ttkHyperlinkLabel.py @@ -194,7 +194,8 @@ def _contextmenu(self, event: tk.Event) -> None: menu.add_command(label=tr.tl('Copy'), command=self.copy) # As in Copy and Paste if self.name == 'ship': - menu.add_command(label=tr.tl('Copy INARA SLEF'), command=self.copy_slef, state=tk.DISABLED) + # LANG: Copy the Inara SLEF Format of the active ship to the clipboard + menu.add_command(label=tr.tl('Copy Inara SLEF'), command=self.copy_slef, state=tk.DISABLED) menu.entryconfigure(1, state=monitor.slef and tk.NORMAL or tk.DISABLED) menu.add_separator() From 19c91511359812f7c1b8bb920e50014e26b564b6 Mon Sep 17 00:00:00 2001 From: David Sangrey Date: Sun, 23 Jun 2024 13:26:44 -0400 Subject: [PATCH 22/34] [2262] Format SLEF with Square Brackets --- monitor.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/monitor.py b/monitor.py index 29970ec03..b11301b8b 100644 --- a/monitor.py +++ b/monitor.py @@ -730,7 +730,7 @@ def parse_entry(self, line: bytes) -> MutableMapping[str, Any]: # noqa: C901, C }) initial_dict.update({'data': data_dict}) output = json.dumps(initial_dict, indent=4) - self.slef = str(output) + self.slef = str(f"[{output}]") elif event_type == 'modulebuy': self.state['Modules'][entry['Slot']] = { From 86b5556efbd41cb7bed0233dba40d580d1466480 Mon Sep 17 00:00:00 2001 From: David Sangrey Date: Tue, 25 Jun 2024 11:25:05 -0400 Subject: [PATCH 23/34] [748] Enable Plugin Change w/ App Restart --- EDMarketConnector.py | 3 +- L10n/en.template | 14 +++++++-- config/__init__.py | 6 ++++ config/linux.py | 6 +++- config/windows.py | 34 ++++++++++++---------- prefs.py | 69 ++++++++++++++++++++++++++++++++------------ 6 files changed, 93 insertions(+), 39 deletions(-) diff --git a/EDMarketConnector.py b/EDMarketConnector.py index 2582f570e..390841cc9 100755 --- a/EDMarketConnector.py +++ b/EDMarketConnector.py @@ -849,9 +849,10 @@ def postprefs(self, dologin: bool = True, **postargs): if stable_popup: webbrowser.open("https://github.com/edCD/eDMarketConnector/releases/latest") if postargs.get('Restart_Req'): + # LANG: Text of Notification Popup for EDMC Restart restart_msg = tr.tl('A restart of EDMC is required. EDMC will now shut down.') restart_box = tk.messagebox.Message( - title=tr.tl('Restart Required'), + title=tr.tl('Restart Required'), # LANG: Title of Notification Popup for EDMC Restart message=restart_msg, type=tk.messagebox.OK ) diff --git a/L10n/en.template b/L10n/en.template index 1c1e110f4..a7b3ec298 100644 --- a/L10n/en.template +++ b/L10n/en.template @@ -468,8 +468,11 @@ /* prefs.py: Label for location of third-party plugins folder; In files: prefs.py:908; */ "Plugins folder" = "Plugins folder"; -/* prefs.py: Label on button used to open a filesystem folder; In files: prefs.py:915; */ -"Open" = "Open"; +/* prefs.py: Label on button used to open the Plugin Folder; */ +"Open Plugins Folder" = "Open Plugins Folder"; + +/* prefs.py: Selecting the Location of the Plugin Directory on the Filesystem; */ +"Plugin Directory Location" = "Plugin Directory Location"; /* prefs.py: Tip/label about how to disable plugins; In files: prefs.py:923; */ "Tip: You can disable a plugin by{CR}adding '{EXT}' to its folder name" = "Tip: You can disable a plugin by{CR}adding '{EXT}' to its folder name"; @@ -804,10 +807,15 @@ /* EDMarketConnector.py: Inform the user the Update Track has changed; */ "Update Track Changed to {TRACK}" = "Update Track Changed to {TRACK}"; - /* EDMarketConnector.py: Inform User of Beta -> Stable Transition Risks; */ "Update track changed to Stable from Beta. You will no longer receive Beta updates. You will stay on your current Beta version until the next Stable release.\r\n\r\nYou can manually revert to the latest Stable version. To do so, you must download and install the latest Stable version manually. Note that this may introduce bugs or break completely if downgrading between major versions with significant changes.\r\n\r\nDo you want to open GitHub to download the latest release?" = "Update track changed to Stable from Beta. You will no longer receive Beta updates. You will stay on your current Beta version until the next Stable release.\r\n\r\nYou can manually revert to the latest Stable version. To do so, you must download and install the latest Stable version manually. Note that this may introduce bugs or break completely if downgrading between major versions with significant changes.\r\n\r\nDo you want to open GitHub to download the latest release?"; +/* EDMarketConnector.py: Title of Notification Popup for EDMC Restart; */ +"Restart Required" = "Restart Required"; + +/* EDMarketConnector.py: Text of Notification Popup for EDMC Restart; */ +"A restart of EDMC is required. EDMC will now shut down." = "A restart of EDMC is required. EDMC will now shut down."; + /* myNotebook.py: Can't Paste Images or Files in Text; */ "Cannot paste non-text content." = "Cannot paste non-text content."; diff --git a/config/__init__.py b/config/__init__.py index 8dfcf46b3..9a00b9a9b 100644 --- a/config/__init__.py +++ b/config/__init__.py @@ -189,6 +189,7 @@ class AbstractConfig(abc.ABC): app_dir_path: pathlib.Path plugin_dir_path: pathlib.Path + default_plugin_dir_path: pathlib.Path internal_plugin_dir_path: pathlib.Path respath_path: pathlib.Path home_path: pathlib.Path @@ -279,6 +280,11 @@ def plugin_dir(self) -> str: """Return a string version of plugin_dir.""" return str(self.plugin_dir_path) + @property + def default_plugin_dir(self) -> str: + """Return a string version of plugin_dir.""" + return str(self.default_plugin_dir_path) + @property def internal_plugin_dir(self) -> str: """Return a string version of internal_plugin_dir.""" diff --git a/config/linux.py b/config/linux.py index 51a406262..5900fe98f 100644 --- a/config/linux.py +++ b/config/linux.py @@ -31,7 +31,11 @@ def __init__(self, filename: str | None = None) -> None: self.app_dir_path = xdg_data_home / appname self.app_dir_path.mkdir(exist_ok=True, parents=True) - self.plugin_dir_path = self.app_dir_path / 'plugins' + self.default_plugin_dir_path = self.app_dir_path / 'plugins' + if (plugdir_str := self.get_str('plugin_dir')) is None or not pathlib.Path(plugdir_str).is_dir(): + self.set("plugin_dir", str(self.default_plugin_dir_path)) + plugdir_str = self.default_plugin_dir + self.plugin_dir_path = pathlib.Path(plugdir_str) self.plugin_dir_path.mkdir(exist_ok=True) self.respath_path = pathlib.Path(__file__).parent.parent diff --git a/config/windows.py b/config/windows.py index 75e3f6977..c0d25b71c 100644 --- a/config/windows.py +++ b/config/windows.py @@ -49,10 +49,28 @@ class WinConfig(AbstractConfig): def __init__(self) -> None: super().__init__() + REGISTRY_SUBKEY = r'Software\Marginal\EDMarketConnector' # noqa: N806 + create_key_defaults = functools.partial( + winreg.CreateKeyEx, + key=winreg.HKEY_CURRENT_USER, + access=winreg.KEY_ALL_ACCESS | winreg.KEY_WOW64_64KEY, + ) + + try: + self.__reg_handle: winreg.HKEYType = create_key_defaults(sub_key=REGISTRY_SUBKEY) + + except OSError: + logger.exception('Could not create required registry keys') + raise + self.app_dir_path = pathlib.Path(known_folder_path(FOLDERID_LocalAppData)) / appname # type: ignore self.app_dir_path.mkdir(exist_ok=True) - self.plugin_dir_path = self.app_dir_path / 'plugins' + self.default_plugin_dir_path = self.app_dir_path / 'plugins' + if (plugdir_str := self.get_str('plugin_dir')) is None or not pathlib.Path(plugdir_str).is_dir(): + self.set("plugin_dir", str(self.default_plugin_dir_path)) + plugdir_str = self.default_plugin_dir + self.plugin_dir_path = pathlib.Path(plugdir_str) self.plugin_dir_path.mkdir(exist_ok=True) if getattr(sys, 'frozen', False): @@ -68,20 +86,6 @@ def __init__(self) -> None: known_folder_path(FOLDERID_SavedGames)) / 'Frontier Developments' / 'Elite Dangerous' # type: ignore self.default_journal_dir_path = journal_dir_path if journal_dir_path.is_dir() else None # type: ignore - REGISTRY_SUBKEY = r'Software\Marginal\EDMarketConnector' # noqa: N806 - create_key_defaults = functools.partial( - winreg.CreateKeyEx, - key=winreg.HKEY_CURRENT_USER, - access=winreg.KEY_ALL_ACCESS | winreg.KEY_WOW64_64KEY, - ) - - try: - self.__reg_handle: winreg.HKEYType = create_key_defaults(sub_key=REGISTRY_SUBKEY) - - except OSError: - logger.exception('Could not create required registry keys') - raise - self.identifier = applongname if (outdir_str := self.get_str('outdir')) is None or not pathlib.Path(outdir_str).is_dir(): docs = known_folder_path(FOLDERID_Documents) diff --git a/prefs.py b/prefs.py index dffe71b94..be9896013 100644 --- a/prefs.py +++ b/prefs.py @@ -912,20 +912,15 @@ def __setup_plugin_tab(self, notebook: ttk.Notebook) -> None: # noqa: CCR001 # Plugin settings and info plugins_frame = nb.Frame(notebook) plugins_frame.columnconfigure(0, weight=1) - plugdir = tk.StringVar() - plugdir.set(config.plugin_dir) row = AutoInc(start=0) - - # Section heading in settings + self.plugdir = tk.StringVar() + self.plugdir.set(str(config.get_str('plugin_dir'))) # LANG: Label for location of third-party plugins folder - nb.Label(plugins_frame, text=tr.tl('Plugins folder') + ':').grid( - padx=self.PADX, pady=self.PADY, sticky=tk.W, row=row.get() - ) - - plugdirentry = ttk.Entry(plugins_frame, justify=tk.LEFT) - self.displaypath(plugdir, plugdirentry) - plugdirentry.grid(columnspan=2, padx=self.PADX, pady=self.BOXY, sticky=tk.EW, row=row.get()) - + self.plugdir_label = nb.Label(plugins_frame, text=tr.tl('Plugins folder') + ':') + self.plugdir_label.grid(padx=self.PADX, pady=self.PADY, sticky=tk.W, row=row.get()) + self.plugdir_entry = ttk.Entry(plugins_frame, takefocus=False, + textvariable=self.plugdir) # Link StringVar to Entry widget + self.plugdir_entry.grid(columnspan=4, padx=self.PADX, pady=self.BOXY, sticky=tk.EW, row=row.get()) with row as cur_row: nb.Label( plugins_frame, @@ -933,19 +928,41 @@ def __setup_plugin_tab(self, notebook: ttk.Notebook) -> None: # noqa: CCR001 # LANG: Tip/label about how to disable plugins text=tr.tl( "Tip: You can disable a plugin by{CR}adding '{EXT}' to its folder name").format(EXT='.disabled') - ).grid(columnspan=2, padx=self.PADX, pady=self.PADY, sticky=tk.EW, row=cur_row) + ).grid(columnspan=1, padx=self.PADX, pady=self.PADY, sticky=tk.EW, row=cur_row) - ttk.Button( + # Open Plugin Folder Button + self.open_plug_folder_btn = ttk.Button( plugins_frame, - # LANG: Label on button used to open a filesystem folder - text=tr.tl('Open'), # Button that opens a folder in Explorer/Finder + # LANG: Label on button used to open the Plugin Folder + text=tr.tl('Open Plugins Folder'), command=lambda: open_folder(config.plugin_dir_path) - ).grid(column=1, padx=self.PADX, pady=self.PADY, sticky=tk.N, row=cur_row) + ) + self.open_plug_folder_btn.grid(column=1, padx=self.PADX, pady=self.PADY, sticky=tk.EW, row=cur_row) + + # Browse Button + text = tr.tl('Browse...') # LANG: NOT-macOS Settings - files location selection button + self.plugbutton = ttk.Button( + plugins_frame, + text=text, + # LANG: Selecting the Location of the Plugin Directory on the Filesystem + command=lambda: self.filebrowse(tr.tl('Plugin Directory Location'), self.plugdir) + ) + self.plugbutton.grid(column=2, padx=self.PADX, pady=self.PADY, sticky=tk.EW, row=cur_row) + + if config.default_journal_dir_path: + # Appearance theme and language setting + ttk.Button( + plugins_frame, + # LANG: Settings > Configuration - Label on 'reset journal files location to default' button + text=tr.tl('Default'), + command=self.plugdir_reset, + state=tk.NORMAL if config.get_str('plugin_dir') else tk.DISABLED + ).grid(column=3, padx=self.PADX, pady=self.PADY, sticky=tk.EW, row=cur_row) enabled_plugins = list(filter(lambda x: x.folder and x.module, plug.PLUGINS)) if enabled_plugins: ttk.Separator(plugins_frame, orient=tk.HORIZONTAL).grid( - columnspan=3, padx=self.PADX, pady=self.SEPY, sticky=tk.EW, row=row.get() + columnspan=4, padx=self.PADX, pady=self.SEPY, sticky=tk.EW, row=row.get() ) nb.Label( plugins_frame, @@ -1123,6 +1140,13 @@ def logdir_reset(self) -> None: self.outvarchanged() + def plugdir_reset(self) -> None: + """Reset the log dir to the default.""" + if config.default_plugin_dir_path: + self.plugdir.set(config.default_plugin_dir) + + self.outvarchanged() + def disable_autoappupdatecheckingame_changed(self) -> None: """Save out the auto update check in game config.""" config.set('disable_autoappupdatecheckingame', self.disable_autoappupdatecheckingame.get()) @@ -1218,7 +1242,7 @@ def hotkeylisten(self, event: 'tk.Event[Any]') -> str: return 'break' # stops further processing - insertion, Tab traversal etc - def apply(self) -> None: + def apply(self) -> None: # noqa: CCR001 """Update the config with the options set on the dialog.""" config.set('PrefsVersion', prefsVersion.stringToSerial(appversion_nobuild())) config.set( @@ -1274,6 +1298,13 @@ def apply(self) -> None: config.set('dark_text', self.theme_colors[0]) config.set('dark_highlight', self.theme_colors[1]) theme.apply(self.parent) + if self.plugdir.get() != config.get('plugin_dir'): + config.set( + 'plugin_dir', + join(config.home_path, self.plugdir.get()[2:]) if self.plugdir.get().startswith( + '~') else self.plugdir.get() + ) + self.req_restart = True # Notify if self.callback: From fee05d3aa5a6993259f3517ef8148dcdb43b3707 Mon Sep 17 00:00:00 2001 From: David Sangrey Date: Tue, 25 Jun 2024 12:01:29 -0400 Subject: [PATCH 24/34] [748] Enable Automatic Restart --- EDMarketConnector.py | 8 +++++--- L10n/en.template | 2 +- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/EDMarketConnector.py b/EDMarketConnector.py index 390841cc9..f905ee296 100755 --- a/EDMarketConnector.py +++ b/EDMarketConnector.py @@ -850,7 +850,7 @@ def postprefs(self, dologin: bool = True, **postargs): webbrowser.open("https://github.com/edCD/eDMarketConnector/releases/latest") if postargs.get('Restart_Req'): # LANG: Text of Notification Popup for EDMC Restart - restart_msg = tr.tl('A restart of EDMC is required. EDMC will now shut down.') + restart_msg = tr.tl('A restart of EDMC is required. EDMC will now restart.') restart_box = tk.messagebox.Message( title=tr.tl('Restart Required'), # LANG: Title of Notification Popup for EDMC Restart message=restart_msg, @@ -858,7 +858,7 @@ def postprefs(self, dologin: bool = True, **postargs): ) restart_box.show() if restart_box: - app.onexit() + app.onexit(restart=True) def set_labels(self): """Set main window labels, e.g. after language change.""" @@ -1862,7 +1862,7 @@ def exit_tray(self, systray: 'SysTrayIcon') -> None: ) exit_thread.start() - def onexit(self, event=None) -> None: + def onexit(self, event=None, restart: bool=False) -> None: """Application shutdown procedure.""" if sys.platform == 'win32': shutdown_thread = threading.Thread( @@ -1925,6 +1925,8 @@ def onexit(self, event=None) -> None: self.w.destroy() logger.info('Done.') + if restart: + return os.execv(sys.executable, ['python'] + sys.argv) def drag_start(self, event) -> None: """Initiate dragging the window.""" diff --git a/L10n/en.template b/L10n/en.template index a7b3ec298..74b9728f0 100644 --- a/L10n/en.template +++ b/L10n/en.template @@ -814,7 +814,7 @@ "Restart Required" = "Restart Required"; /* EDMarketConnector.py: Text of Notification Popup for EDMC Restart; */ -"A restart of EDMC is required. EDMC will now shut down." = "A restart of EDMC is required. EDMC will now shut down."; +"A restart of EDMC is required. EDMC will now restart." = "A restart of EDMC is required. EDMC will now restart."; /* myNotebook.py: Can't Paste Images or Files in Text; */ "Cannot paste non-text content." = "Cannot paste non-text content."; From 6643f838dcbfaab6ea1c5bdb0e6ff595178c7c58 Mon Sep 17 00:00:00 2001 From: David Sangrey Date: Tue, 25 Jun 2024 12:07:49 -0400 Subject: [PATCH 25/34] [748] Update URL --- EDMarketConnector.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/EDMarketConnector.py b/EDMarketConnector.py index f905ee296..b64dc1c86 100755 --- a/EDMarketConnector.py +++ b/EDMarketConnector.py @@ -847,7 +847,7 @@ def postprefs(self, dologin: bool = True, **postargs): update_msg = update_msg.replace('\\r', '\r') stable_popup = tk.messagebox.askyesno(title=title, message=update_msg) if stable_popup: - webbrowser.open("https://github.com/edCD/eDMarketConnector/releases/latest") + webbrowser.open("https://github.com/EDCD/eDMarketConnector/releases/latest") if postargs.get('Restart_Req'): # LANG: Text of Notification Popup for EDMC Restart restart_msg = tr.tl('A restart of EDMC is required. EDMC will now restart.') From b47c2bddde07ebbf4d0fdea7e057bfa8a16c050e Mon Sep 17 00:00:00 2001 From: David Sangrey Date: Tue, 25 Jun 2024 12:11:49 -0400 Subject: [PATCH 26/34] [Minor] Flake8 Fix --- EDMarketConnector.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/EDMarketConnector.py b/EDMarketConnector.py index b64dc1c86..b2b7582eb 100755 --- a/EDMarketConnector.py +++ b/EDMarketConnector.py @@ -1862,7 +1862,7 @@ def exit_tray(self, systray: 'SysTrayIcon') -> None: ) exit_thread.start() - def onexit(self, event=None, restart: bool=False) -> None: + def onexit(self, event=None, restart: bool = False) -> None: """Application shutdown procedure.""" if sys.platform == 'win32': shutdown_thread = threading.Thread( From d22f39496b972bab3b4a042e2fb84965f96c4d4b Mon Sep 17 00:00:00 2001 From: David Sangrey Date: Tue, 25 Jun 2024 12:18:52 -0400 Subject: [PATCH 27/34] [748] Reorder Linux Settings --- EDMarketConnector.py | 2 +- config/linux.py | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/EDMarketConnector.py b/EDMarketConnector.py index b2b7582eb..843b076ab 100755 --- a/EDMarketConnector.py +++ b/EDMarketConnector.py @@ -1926,7 +1926,7 @@ def onexit(self, event=None, restart: bool = False) -> None: logger.info('Done.') if restart: - return os.execv(sys.executable, ['python'] + sys.argv) + os.execv(sys.executable, ['python'] + sys.argv) def drag_start(self, event) -> None: """Initiate dragging the window.""" diff --git a/config/linux.py b/config/linux.py index 5900fe98f..a7a472f48 100644 --- a/config/linux.py +++ b/config/linux.py @@ -32,12 +32,6 @@ def __init__(self, filename: str | None = None) -> None: self.app_dir_path.mkdir(exist_ok=True, parents=True) self.default_plugin_dir_path = self.app_dir_path / 'plugins' - if (plugdir_str := self.get_str('plugin_dir')) is None or not pathlib.Path(plugdir_str).is_dir(): - self.set("plugin_dir", str(self.default_plugin_dir_path)) - plugdir_str = self.default_plugin_dir - self.plugin_dir_path = pathlib.Path(plugdir_str) - self.plugin_dir_path.mkdir(exist_ok=True) - self.respath_path = pathlib.Path(__file__).parent.parent self.internal_plugin_dir_path = self.respath_path / 'plugins' @@ -66,6 +60,12 @@ def __init__(self, filename: str | None = None) -> None: self.config.add_section(self.SECTION) + if (plugdir_str := self.get_str('plugin_dir')) is None or not pathlib.Path(plugdir_str).is_dir(): + self.set("plugin_dir", str(self.default_plugin_dir_path)) + plugdir_str = self.default_plugin_dir + self.plugin_dir_path = pathlib.Path(plugdir_str) + self.plugin_dir_path.mkdir(exist_ok=True) + if (outdir := self.get_str('outdir')) is None or not pathlib.Path(outdir).is_dir(): self.set('outdir', self.home) From a385b6fd80b994f9aa414a39155ec5b26f509eeb Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 1 Jul 2024 17:38:51 +0000 Subject: [PATCH 28/34] Bump flake8 from 7.0.0 to 7.1.0 Bumps [flake8](https://github.com/pycqa/flake8) from 7.0.0 to 7.1.0. - [Commits](https://github.com/pycqa/flake8/compare/7.0.0...7.1.0) --- updated-dependencies: - dependency-name: flake8 dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- requirements-dev.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-dev.txt b/requirements-dev.txt index 05c5ad291..28a239d26 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -8,7 +8,7 @@ wheel setuptools==70.0.0 # Static analysis tools -flake8==7.0.0 +flake8==7.1.0 flake8-annotations-coverage==0.0.6 flake8-cognitive-complexity==0.1.0 flake8-comprehensions==3.14.0 From 0adc9cbe63311c967c7a4b42e96cb513868fd886 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 1 Jul 2024 17:38:56 +0000 Subject: [PATCH 29/34] Bump pep8-naming from 0.13.3 to 0.14.1 Bumps [pep8-naming](https://github.com/PyCQA/pep8-naming) from 0.13.3 to 0.14.1. - [Release notes](https://github.com/PyCQA/pep8-naming/releases) - [Changelog](https://github.com/PyCQA/pep8-naming/blob/main/CHANGELOG.rst) - [Commits](https://github.com/PyCQA/pep8-naming/compare/0.13.3...0.14.1) --- updated-dependencies: - dependency-name: pep8-naming dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- requirements-dev.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-dev.txt b/requirements-dev.txt index 05c5ad291..680627550 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -19,7 +19,7 @@ flake8-polyfill==1.0.2 flake8-use-fstring==1.4 mypy==1.10.0 -pep8-naming==0.13.3 +pep8-naming==0.14.1 safety==3.2.0 types-requests==2.31.0.20240311 types-pkg-resources==0.1.3 From 47b621c2b8abf451952dbe9f41a0c0a11bb71259 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 1 Jul 2024 17:38:59 +0000 Subject: [PATCH 30/34] Bump watchdog from 4.0.0 to 4.0.1 Bumps [watchdog](https://github.com/gorakhargosh/watchdog) from 4.0.0 to 4.0.1. - [Release notes](https://github.com/gorakhargosh/watchdog/releases) - [Changelog](https://github.com/gorakhargosh/watchdog/blob/master/changelog.rst) - [Commits](https://github.com/gorakhargosh/watchdog/compare/v4.0.0...v4.0.1) --- updated-dependencies: - dependency-name: watchdog dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 22f0a360f..877950654 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,5 +1,5 @@ requests==2.32.3 pillow==10.3.0 -watchdog==4.0.0 +watchdog==4.0.1 simplesystray==0.1.0; sys_platform == 'win32' semantic-version==2.10.0 From bac4b30848293bccf224bf505203e827c7982260 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 1 Jul 2024 17:39:04 +0000 Subject: [PATCH 31/34] Bump pytest from 8.2.0 to 8.2.2 Bumps [pytest](https://github.com/pytest-dev/pytest) from 8.2.0 to 8.2.2. - [Release notes](https://github.com/pytest-dev/pytest/releases) - [Changelog](https://github.com/pytest-dev/pytest/blob/main/CHANGELOG.rst) - [Commits](https://github.com/pytest-dev/pytest/compare/8.2.0...8.2.2) --- updated-dependencies: - dependency-name: pytest dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- requirements-dev.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-dev.txt b/requirements-dev.txt index 05c5ad291..9c4f249d9 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -38,7 +38,7 @@ mistune==3.0.2 py2exe==0.13.0.1; sys_platform == 'win32' # Testing -pytest==8.2.0 +pytest==8.2.2 pytest-cov==5.0.0 # Pytest code coverage support coverage[toml]==7.5.0 # pytest-cov dep. This is here to ensure that it includes TOML support for pyproject.toml configs coverage-conditional-plugin==0.9.0 From 054245f34848679d11a4771b38c66f4d1081a6df Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 1 Jul 2024 18:46:54 +0000 Subject: [PATCH 32/34] Bump flake8-comprehensions from 3.14.0 to 3.15.0 Bumps [flake8-comprehensions](https://github.com/adamchainz/flake8-comprehensions) from 3.14.0 to 3.15.0. - [Changelog](https://github.com/adamchainz/flake8-comprehensions/blob/main/CHANGELOG.rst) - [Commits](https://github.com/adamchainz/flake8-comprehensions/compare/3.14.0...3.15.0) --- updated-dependencies: - dependency-name: flake8-comprehensions dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- requirements-dev.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-dev.txt b/requirements-dev.txt index 2f27e2ab3..fe1459932 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -11,7 +11,7 @@ setuptools==70.0.0 flake8==7.1.0 flake8-annotations-coverage==0.0.6 flake8-cognitive-complexity==0.1.0 -flake8-comprehensions==3.14.0 +flake8-comprehensions==3.15.0 flake8-docstrings==1.7.0 flake8-json==24.4.0 flake8-noqa==1.4.0 From e9755fae783bfce12ed05928d5d93e2e510ac1a5 Mon Sep 17 00:00:00 2001 From: David Sangrey Date: Mon, 1 Jul 2024 14:56:51 -0400 Subject: [PATCH 33/34] [2270] C420 Unnecessary Dict Comprehension --- debug_webserver.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/debug_webserver.py b/debug_webserver.py index 87f7eaed0..1825c7717 100644 --- a/debug_webserver.py +++ b/debug_webserver.py @@ -18,7 +18,7 @@ output_lock = threading.Lock() output_data_path = pathlib.Path(tempfile.gettempdir()) / f'{appname}' / 'http_debug' -SAFE_TRANSLATE = str.maketrans({x: '_' for x in "!@#$%^&*()./\\\r\n[]-+='\";:?<>,~`"}) +SAFE_TRANSLATE = str.maketrans(dict.fromkeys("!@#$%^&*()./\\\r\n[]-+='\";:?<>,~`", '_')) class LoggingHandler(server.BaseHTTPRequestHandler): From 40463d4de814ad88ea1d8265d74c4d6c23a4ac15 Mon Sep 17 00:00:00 2001 From: David Sangrey Date: Sun, 21 Jul 2024 23:46:15 -0400 Subject: [PATCH 34/34] [2114] Update Pathlib Usage --- prefs.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/prefs.py b/prefs.py index ffe9eed02..69e6a8402 100644 --- a/prefs.py +++ b/prefs.py @@ -41,7 +41,7 @@ def help_open_log_folder() -> None: """Open the folder logs are stored in.""" warnings.warn('prefs.help_open_log_folder is deprecated, use open_log_folder instead. ' 'This function will be removed in 6.0 or later', DeprecationWarning, stacklevel=2) - open_folder(pathlib.Path(config.app_dir_path / 'logs')) + open_folder(Path(config.app_dir_path / 'logs')) def open_folder(file: Path) -> None: @@ -322,7 +322,7 @@ def __init__(self, parent: tk.Tk, callback: Optional[Callable]): self.geometry(f"+{position.left}+{position.top}") # Set Log Directory - self.logfile_loc = pathlib.Path(config.app_dir_path / 'logs') + self.logfile_loc = Path(config.app_dir_path / 'logs') # Set minimum size to prevent content cut-off self.update_idletasks() # Update "requested size" from geometry manager @@ -1299,8 +1299,8 @@ def apply(self) -> None: # noqa: CCR001 if self.plugdir.get() != config.get('plugin_dir'): config.set( 'plugin_dir', - join(config.home_path, self.plugdir.get()[2:]) if self.plugdir.get().startswith( - '~') else self.plugdir.get() + str(Path(config.home_path, self.plugdir.get()[2:])) if self.plugdir.get().startswith('~') else + str(Path(self.plugdir.get())) ) self.req_restart = True