From 3c0ac76f906851666fc2f5094511596c18d7cd66 Mon Sep 17 00:00:00 2001 From: Athanasius Date: Wed, 23 Sep 2020 17:38:38 +0100 Subject: [PATCH] Trace: Add support to EDMC.py & misc cleanups * EDMC: Add --trace (to match EDMarketConnector.py) and TRACE as option to --loglevel. * EDMC: docstrings added. * EDMCLogging: Set logger name based on if GUI or CLI. * EDMarketConnector: * Re-order imports. * Misc. formatting cleanups. * f-strings not .format(). * Removed un-necessary "# noqa: N806" comments. --- EDMC.py | 69 ++++++++++++++++++++++++++++++++------------ EDMCLogging.py | 8 ++++- EDMarketConnector.py | 66 ++++++++++++++++++++++-------------------- 3 files changed, 92 insertions(+), 51 deletions(-) diff --git a/EDMC.py b/EDMC.py index d1f9411d..4f9baf54 100755 --- a/EDMC.py +++ b/EDMC.py @@ -1,19 +1,23 @@ #!/usr/bin/env python3 -# -# Command-line interface. Requires prior setup through the GUI. -# +"""Command-line interface. Requires prior setup through the GUI.""" import argparse import json import locale -import logging import os import re import sys from os.path import getmtime, join from time import sleep, time -from typing import Any, Optional +from typing import TYPE_CHECKING, Any, Optional + +# isort: off +from EDMCLogging import edmclogger, logger, logging +if TYPE_CHECKING: + from logging import trace, TRACE # type: ignore # noqa: F401 +# isort: on +edmclogger.set_channels_loglevel(logging.INFO) # workaround for https://github.com/EDCD/EDMarketConnector/issues/568 os.environ["EDMC_NO_UI"] = "1" @@ -21,7 +25,6 @@ os.environ["EDMC_NO_UI"] = "1" import collate import commodity import companion -import EDMCLogging import edshipyard import l10n import loadout @@ -40,11 +43,9 @@ sys.path.append(config.internal_plugin_dir) import eddn # noqa: E402 # isort: on -logger = EDMCLogging.Logger(appcmdname).get_logger() -logger.setLevel(logging.INFO) - def log_locale(prefix: str) -> None: + """Log the current state of locale settings.""" logger.debug(f'''Locale: {prefix} Locale LC_COLLATE: {locale.getlocale(locale.LC_COLLATE)} Locale LC_CTYPE: {locale.getlocale(locale.LC_CTYPE)} @@ -62,12 +63,30 @@ EXIT_SUCCESS, EXIT_SERVER, EXIT_CREDENTIALS, EXIT_VERIFICATION, EXIT_LAGGING, EX JOURNAL_RE = re.compile(r'^Journal(Beta)?\.[0-9]{12}\.[0-9]{2}\.log$') -# quick and dirty version comparison assuming "strict" numeric only version numbers def versioncmp(versionstring): + """Quick and dirty version comparison assuming "strict" numeric only version numbers.""" return list(map(int, versionstring.split('.'))) def deep_get(target: dict, *args: str, default=None) -> Any: + """ + Walk into a dict and return the specified deep value. + + Example usage: + + >>> thing = {'a': {'b': {'c': 'foo'} } } + >>> deep_get(thing, ('a', 'b', 'c'), None) + 'foo' + >>> deep_get(thing, ('a', 'b'), None) + {'c': 'foo'} + >>> deep_get(thing, ('a', 'd'), None) + None + + :param target: The dict to walk into for the desired value. + :param args: The list of keys to walk down through. + :param default: What to return if the target has no value. + :return: The value at the target deep key. + """ if not hasattr(target, 'get'): raise ValueError(f"Cannot call get on {target} ({type(target)})") @@ -83,6 +102,7 @@ def deep_get(target: dict, *args: str, default=None) -> Any: def main(): + """Run the main code of the program.""" try: # arg parsing parser = argparse.ArgumentParser( @@ -93,7 +113,16 @@ def main(): ) parser.add_argument('-v', '--version', help='print program version and exit', action='store_const', const=True) - parser.add_argument('--loglevel', metavar='loglevel', help='Set the logging loglevel to one of: CRITICAL, ERROR, WARNING, INFO, DEBUG') # noqa: E501 + group_loglevel = parser.add_mutually_exclusive_group() + group_loglevel.add_argument('--loglevel', + metavar='loglevel', + help='Set the logging loglevel to one of: ' + 'CRITICAL, ERROR, WARNING, INFO, DEBUG, TRACE', + ) + group_loglevel.add_argument('--trace', + help='Set the Debug logging loglevel to TRACE', + action='store_true', + ) parser.add_argument('-a', metavar='FILE', help='write ship loadout to FILE in Companion API json format') parser.add_argument('-e', metavar='FILE', help='write ship loadout to FILE in E:D Shipyard plain text format') parser.add_argument('-l', metavar='FILE', help='write ship locations to FILE in CSV format') @@ -117,11 +146,14 @@ def main(): return - if args.loglevel: - if args.loglevel not in ('CRITICAL', 'ERROR', 'WARNING', 'INFO', 'DEBUG'): - print('loglevel must be one of: CRITICAL, ERROR, WARNING, INFO, DEBUG', file=sys.stderr) + if args.trace: + edmclogger.set_channels_loglevel(logging.TRACE) + + elif args.loglevel: + if args.loglevel not in ('CRITICAL', 'ERROR', 'WARNING', 'INFO', 'DEBUG', 'TRACE'): + print('loglevel must be one of: CRITICAL, ERROR, WARNING, INFO, DEBUG, TRACE', file=sys.stderr) sys.exit(EXIT_ARGS) - logger.setLevel(args.loglevel) + edmclogger.set_channels_loglevel(args.loglevel) logger.debug(f'Startup v{appversion} : Running on Python v{sys.version}') logger.debug(f'''Platform: {sys.platform} @@ -250,10 +282,9 @@ sys.path: {sys.path}''' stats.export_status(data, args.t) if data['commander'].get('docked'): - print('{},{}'.format( - deep_get(data, 'lastSystem', 'name', default='Unknown'), - deep_get(data, 'lastStarport', 'name', default='Unknown') - )) + print(f'{deep_get(data, "lastSystem", "name", default="Unknown")},' + f'{deep_get(data, "lastStarport", "name", default="Unknown")}' + ) else: print(deep_get(data, 'lastSystem', 'name', default='Unknown')) diff --git a/EDMCLogging.py b/EDMCLogging.py index 7bf96b2a..f90ca5b7 100644 --- a/EDMCLogging.py +++ b/EDMCLogging.py @@ -381,5 +381,11 @@ def get_main_logger() -> logging.Logger: loglevel = config.get('loglevel') if not loglevel: loglevel = logging.INFO -edmclogger = Logger(appname, loglevel=loglevel) + +if not os.getenv('EDMC_NO_UI'): + base_logger_name = appname +else: + base_logger_name = appcmdname + +edmclogger = Logger(base_logger_name, loglevel=loglevel) logger = edmclogger.get_logger() diff --git a/EDMarketConnector.py b/EDMarketConnector.py index f5d3b18c..649e67e4 100755 --- a/EDMarketConnector.py +++ b/EDMarketConnector.py @@ -2,20 +2,27 @@ # -*- coding: utf-8 -*- import argparse -from builtins import str -from builtins import object -import sys -from sys import platform +import html import json import locale +import re +import sys +import webbrowser +from builtins import object, str from os import chdir, environ from os.path import dirname, isdir, join -import re -import html -from time import time, localtime, strftime -import webbrowser +from sys import platform +from time import localtime, strftime, time +from typing import TYPE_CHECKING -from config import appname, applongname, appversion, appversion_nobuild, copyright, config +from EDMCLogging import edmclogger, logger, logging + +# isort: off +if TYPE_CHECKING: + from logging import trace, TRACE # type: ignore # noqa: F401 +# isort: on + +from config import applongname, appname, appversion, appversion_nobuild, config, copyright # TODO: Test: Make *sure* this redirect is working, else py2exe is going to cause an exit popup if __name__ == "__main__": @@ -37,10 +44,11 @@ if getattr(sys, 'frozen', False): environ['TK_LIBRARY'] = join(dirname(sys.path[0]), 'lib', 'tk') import tkinter as tk -from tkinter import ttk import tkinter.filedialog import tkinter.font import tkinter.messagebox +from tkinter import ttk + from ttkHyperlinkLabel import HyperlinkLabel if __debug__: @@ -50,18 +58,18 @@ if __debug__: signal.signal(signal.SIGTERM, lambda sig, frame: pdb.Pdb().set_trace(frame)) -import companion import commodity -from commodity import COMMODITY_CSV -import td -import stats -import prefs +import companion import plug +import prefs +import stats +import td +from commodity import COMMODITY_CSV +from dashboard import dashboard from hotkey import hotkeymgr from l10n import Translations from monitor import monitor from protocol import protocolhandler -from dashboard import dashboard from theme import theme SERVER_RETRY = 5 # retry pause for Companion servers [s] @@ -614,11 +622,11 @@ class AppWindow(object): def crewroletext(role): # Return translated crew role. Needs to be dynamic to allow for changing language. return { - None : '', - 'Idle' : '', + None: '', + 'Idle': '', 'FighterCon': _('Fighter'), # Multicrew role - 'FireCon' : _('Gunner'), # Multicrew role - 'FlightCon' : _('Helm'), # Multicrew role + 'FireCon': _('Gunner'), # Multicrew role + 'FlightCon': _('Helm'), # Multicrew role }.get(role, role) while True: @@ -641,8 +649,8 @@ class AppWindow(object): self.ship_label['text'] = _('Ship') + ':' # Main window self.ship.configure( text=monitor.state['ShipName'] - or companion.ship_map.get(monitor.state['ShipType'], monitor.state['ShipType']) - or '', + or companion.ship_map.get(monitor.state['ShipType'], monitor.state['ShipType']) + or '', url=self.shipyard_url) else: self.cmdr['text'] = '' @@ -935,7 +943,8 @@ class AppWindow(object): def onexit(self, event=None): # http://core.tcl.tk/tk/tktview/c84f660833546b1b84e7 if platform != 'darwin' or self.w.winfo_rooty() > 0: - config.set('geometry', '+{1}+{2}'.format(*self.w.geometry().split('+'))) + x, y = self.w.geometry().split('+')[1:3] # e.g. '212x170+2881+1267' + config.set('geometry', f'+{x}+{y}') self.w.withdraw() # Following items can take a few seconds, so hide the main window while they happen protocolhandler.close() hotkeymgr.unregister() @@ -987,13 +996,13 @@ def enforce_single_instance() -> None: # Ensure only one copy of the app is running under this user account. OSX does this automatically. Linux TODO. if platform == 'win32': import ctypes - from ctypes.wintypes import HWND, LPWSTR, LPCWSTR, INT, BOOL, LPARAM + from ctypes.wintypes import BOOL, HWND, INT, LPARAM, LPCWSTR, LPWSTR EnumWindows = ctypes.windll.user32.EnumWindows # noqa: N806 GetClassName = ctypes.windll.user32.GetClassNameW # noqa: N806 - GetClassName.argtypes = [HWND, LPWSTR, ctypes.c_int] # noqa: N806 + GetClassName.argtypes = [HWND, LPWSTR, ctypes.c_int] GetWindowText = ctypes.windll.user32.GetWindowTextW # noqa: N806 - GetWindowText.argtypes = [HWND, LPWSTR, ctypes.c_int] # noqa: N806 + GetWindowText.argtypes = [HWND, LPWSTR, ctypes.c_int] GetWindowTextLength = ctypes.windll.user32.GetWindowTextLengthW # noqa: N806 GetProcessHandleFromHwnd = ctypes.windll.oleacc.GetProcessHandleFromHwnd # noqa: N806 @@ -1076,11 +1085,6 @@ if __name__ == "__main__": args = parser.parse_args() - from EDMCLogging import edmclogger, logger, logging - # isort: off - from logging import trace, TRACE # type: ignore # noqa: F401 - # isort: on - if args.trace: logger.setLevel(logging.TRACE) edmclogger.set_channels_loglevel(logging.TRACE)