diff --git a/ChangeLog.md b/ChangeLog.md index ca5ad793..d8f721e3 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -27,6 +27,51 @@ produce the Windows executables and installer. --- +Release 5.3.1 +=== + +This release addresses some issues with newer EDDN code which could cause +erroneous alerts to the player, or sending of bad messages. + +* EDDN: Cope with `ApproachSettlement` on login occurring before `Location`, + such that we don't yet know the name of the star system the player is in. + + Closes [#1484](https://github.com/EDCD/EDMarketConnector/pull/1484) + +* EDDN: Cope with `ApproachSettlement` missing planetary coordinates on login + at/near a settlement in Horizons. + + Closes [#1476](https://github.com/EDCD/EDMarketConnector/pull/1476) + +* EDDN: Change the `CodexEntry` "empty string" checks to only apply to those + values where the schema enforces "must be at least one character". + + This prevents the big 'CodexEntry had empty string, PLEASE ALERT THE EDMC + DEVELOPERS' message from triggering on, e.g. `NearestDestination` being + empty, which the schema allows. + + Closes [#1481](https://github.com/EDCD/EDMarketConnector/issues/1481) + +Plugin Developers +--- + +* If you use a sub-class for a widget the core code will no longer break if + your code raises an exception. e.g. a plugin was failing due to Python + 3.10 using `collections.abc` instead of `collections`, and the plugin's + custom widget had a `configure()` method which was called by the core + theme code on startup or theme change. This then caused the whole + application UI to never show up on startup. + + This also applies if you set up a button such that enter/leave on it, i.e. + mouse in/out, causes the `theme.py` code for that to trigger. + + So, now in such cases the main UI should actually show up, although your + plugin's UI might look weird due to theming not being properly applied. + + The plugin exception **WILL** be logged, at ERROR level. + +--- + Release 5.3.0 === diff --git a/PLUGINS.md b/PLUGINS.md index 00072e18..b26ee58b 100644 --- a/PLUGINS.md +++ b/PLUGINS.md @@ -38,6 +38,8 @@ Please be sure to read the [Avoiding potential pitfalls](#avoiding-potential-pit section, else you might inadvertently cause issues for the core EDMarketConnector code including whole application crashes. +## Being aware of core application changes + It is highly advisable to ensure you are aware of all EDMarketConnector releases, including the pre-releases. The -beta and -rc changelogs will contain valuable information about any forthcoming changes that affect plugins. @@ -55,6 +57,11 @@ The easiest way is: And, of course, either ensure you check your GitHub messages regularly, or have it set up to email you such notifications. +You should also keep an eye on [our GitHub Discussions](https://github.com/EDCD/EDMarketConnector/discussions) +in case there are any proposed changes to EDMC plugin functionality. You can +do this by ensuring 'Discussions' is also ticked when following the steps +above to set up a 'Custom' watch on this repository. + --- ## Examples diff --git a/config/__init__.py b/config/__init__.py index 63505dac..0307386e 100644 --- a/config/__init__.py +++ b/config/__init__.py @@ -52,7 +52,7 @@ appcmdname = 'EDMC' # # Major.Minor.Patch(-prerelease)(+buildmetadata) # NB: Do *not* import this, use the functions appversion() and appversion_nobuild() -_static_appversion = '5.3.0' +_static_appversion = '5.3.1' _cached_version: Optional[semantic_version.Version] = None copyright = '© 2015-2019 Jonathan Harris, 2020-2022 EDCD' diff --git a/plugins/eddn.py b/plugins/eddn.py index 1c3fdc43..c7cd4059 100644 --- a/plugins/eddn.py +++ b/plugins/eddn.py @@ -829,7 +829,7 @@ class EDDN: else: logger.warning("Neither this_coordinates or this.coordinates set, can't add StarPos") - return 'No source for adding StarPos to approachsettlement/1 !' + return 'No source for adding StarPos to EDDN message !' return entry @@ -970,7 +970,9 @@ class EDDN: logger.warning(f'this.body_id was not set properly: "{this.body_id}" ({type(this.body_id)})') ####################################################################### - for k, v in entry.items(): + # Check just the top-level strings with minLength=1 in the schema + for k in ('System', 'Name', 'Region', 'Category', 'SubCategory'): + v = entry[k] if v is None or isinstance(v, str) and v == '': logger.warning(f'post-processing entry contains entry["{k}"] = {v} {(type(v))}') # We should drop this message and VERY LOUDLY inform the @@ -978,6 +980,13 @@ class EDDN: # raw Journal event that caused this. return 'CodexEntry had empty string, PLEASE ALERT THE EDMC DEVELOPERS' + # Also check traits + if 'Traits' in entry: + for v in entry['Traits']: + if v is None or isinstance(v, str) and v == '': + logger.warning(f'post-processing entry[\'Traits\'] contains {v} {(type(v))}\n{entry["Traits"]}\n') + return 'CodexEntry Trait had empty string, PLEASE ALERT THE EDMC DEVELOPERS' + msg = { '$schemaRef': f'https://eddn.edcd.io/schemas/codexentry/1{"/test" if is_beta else ""}', 'message': entry @@ -1126,6 +1135,27 @@ class EDDN: # "event": "ApproachSettlement", # "timestamp": "2021-10-14T12:37:54Z" # } + + ####################################################################### + # Bugs + ####################################################################### + # WORKAROUND 3.8.0.404 | 2022-02-18: ApproachSettlement missing coords + # As of Horizons ("gameversion":"3.8.0.404", "build":"r280105/r0 ") + # if you log back in (certainly a game client restart) at a + # Planetary Port, then the ApproachSettlement event written will be + # missing the Latitude and Longitude. + # Ref: https://github.com/EDCD/EDMarketConnector/issues/1476 + if any( + k not in entry for k in ('Latitude', 'Longitude') + ): + logger.debug( + f'ApproachSettlement without at least one of Latitude or Longitude:\n{entry}\n' + ) + # No need to alert the user, it will only annoy them + return "" + # WORKAROUND END + ####################################################################### + ####################################################################### # Augmentations ####################################################################### @@ -1499,6 +1529,12 @@ def journal_entry( # noqa: C901, CCR001 return this.eddn.export_journal_navroute(cmdr, is_beta, entry) elif event_name == 'approachsettlement': + # An `ApproachSettlement` can appear *before* `Location` if you + # logged at one. We won't have necessary augmentation data + # at this point, so bail. + if system is None: + return "" + return this.eddn.export_journal_approachsettlement( cmdr, system, @@ -1507,6 +1543,14 @@ def journal_entry( # noqa: C901, CCR001 entry ) + # NB: If adding FSSSignalDiscovered these absolutely come in at login + # time **BEFORE** the `Location` event, so we won't yet know things + # like SystemNane, or StarPos. + # We can either have the "now send the batch" code add such (but + # that has corner cases around changing systems in the meantime), + # drop those events, or if the schema allows, send without those + # augmentations. + elif event_name == 'fssallbodiesfound': return this.eddn.export_journal_fssallbodiesfound( cmdr, diff --git a/requirements-dev.txt b/requirements-dev.txt index 88f68a80..abeab851 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -5,7 +5,7 @@ wheel # We can't rely on just picking this up from either the base (not venv), # or venv-init-time version. Specify here so that dependabot will prod us # about new versions. -setuptools==60.8.2 +setuptools==60.9.3 # Static analysis tools flake8==4.0.1 @@ -23,7 +23,7 @@ flake8-use-fstring==1.3 mypy==0.931 pep8-naming==0.12.1 safety==1.10.3 -types-requests==2.27.9 +types-requests==2.27.10 # Code formatting tools autopep8==1.6.0 @@ -33,14 +33,14 @@ grip==4.6.0 # Packaging # Used to put together a WiX configuration from template/auto-gen -lxml==4.7.1 +lxml==4.8.0 # We only need py2exe on windows. # Pre-release version addressing semantic_version 2.9.0+ issues: # py2exe==0.11.1.0; sys_platform == 'win32' # Testing -pytest==7.0.0 +pytest==7.0.1 pytest-cov==3.0.0 # Pytest code coverage support coverage[toml]==6.3.1 # pytest-cov dep. This is here to ensure that it includes TOML support for pyproject.toml configs # For manipulating folder permissions and the like. diff --git a/theme.py b/theme.py index 8515bfbb..4f60f976 100644 --- a/theme.py +++ b/theme.py @@ -14,6 +14,9 @@ from tkinter import ttk from config import config from ttkHyperlinkLabel import HyperlinkLabel +from EDMCLogging import get_main_logger + +logger = get_main_logger() if __debug__: from traceback import print_exc @@ -192,17 +195,35 @@ class _Theme(object): def _enter(self, event, image): widget = event.widget if widget and widget['state'] != tk.DISABLED: - widget.configure(state=tk.ACTIVE) + try: + widget.configure(state=tk.ACTIVE) + + except Exception: + logger.exception(f'Failure setting widget active: {widget=}') + if image: - image.configure(foreground=self.current['activeforeground'], - background=self.current['activebackground']) + try: + image.configure(foreground=self.current['activeforeground'], + background=self.current['activebackground']) + + except Exception: + logger.exception(f'Failure configuring image: {image=}') def _leave(self, event, image): widget = event.widget if widget and widget['state'] != tk.DISABLED: - widget.configure(state=tk.NORMAL) + try: + widget.configure(state=tk.NORMAL) + + except Exception: + logger.exception(f'Failure setting widget normal: {widget=}') + if image: - image.configure(foreground=self.current['foreground'], background=self.current['background']) + try: + image.configure(foreground=self.current['foreground'], background=self.current['background']) + + except Exception: + logger.exception(f'Failure configuring image: {image=}') # Set up colors def _colors(self, root, theme): @@ -266,50 +287,64 @@ class _Theme(object): widget.winfo_class(), widget, 'text' in widget.keys() and widget['text']) attribs = self.widgets.get(widget, []) - if isinstance(widget, tk.BitmapImage): - # not a widget - if 'fg' not in attribs: - widget.configure(foreground=self.current['foreground']), - if 'bg' not in attribs: - widget.configure(background=self.current['background']) - elif 'cursor' in widget.keys() and str(widget['cursor']) not in ['', 'arrow']: - # Hack - highlight widgets like HyperlinkLabel with a non-default cursor - if 'fg' not in attribs: - widget.configure(foreground=self.current['highlight']), - if 'insertbackground' in widget.keys(): # tk.Entry - widget.configure(insertbackground=self.current['foreground']), - if 'bg' not in attribs: - widget.configure(background=self.current['background']) - if 'highlightbackground' in widget.keys(): # tk.Entry - widget.configure(highlightbackground=self.current['background']) - if 'font' not in attribs: - widget.configure(font=self.current['font']) - elif 'activeforeground' in widget.keys(): - # e.g. tk.Button, tk.Label, tk.Menu - if 'fg' not in attribs: - widget.configure(foreground=self.current['foreground'], - activeforeground=self.current['activeforeground'], - disabledforeground=self.current['disabledforeground']) - if 'bg' not in attribs: - widget.configure(background=self.current['background'], - activebackground=self.current['activebackground']) - if sys.platform == 'darwin' and isinstance(widget, tk.Button): - widget.configure(highlightbackground=self.current['background']) - if 'font' not in attribs: - widget.configure(font=self.current['font']) - elif 'foreground' in widget.keys(): - # e.g. ttk.Label - if 'fg' not in attribs: - widget.configure(foreground=self.current['foreground']), - if 'bg' not in attribs: - widget.configure(background=self.current['background']) - if 'font' not in attribs: - widget.configure(font=self.current['font']) - elif 'background' in widget.keys() or isinstance(widget, tk.Canvas): - # e.g. Frame, Canvas - if 'bg' not in attribs: - widget.configure(background=self.current['background'], - highlightbackground=self.current['disabledforeground']) + try: + if isinstance(widget, tk.BitmapImage): + # not a widget + if 'fg' not in attribs: + widget.configure(foreground=self.current['foreground']), + + if 'bg' not in attribs: + widget.configure(background=self.current['background']) + + elif 'cursor' in widget.keys() and str(widget['cursor']) not in ['', 'arrow']: + # Hack - highlight widgets like HyperlinkLabel with a non-default cursor + if 'fg' not in attribs: + widget.configure(foreground=self.current['highlight']), + if 'insertbackground' in widget.keys(): # tk.Entry + widget.configure(insertbackground=self.current['foreground']), + + if 'bg' not in attribs: + widget.configure(background=self.current['background']) + if 'highlightbackground' in widget.keys(): # tk.Entry + widget.configure(highlightbackground=self.current['background']) + + if 'font' not in attribs: + widget.configure(font=self.current['font']) + + elif 'activeforeground' in widget.keys(): + # e.g. tk.Button, tk.Label, tk.Menu + if 'fg' not in attribs: + widget.configure(foreground=self.current['foreground'], + activeforeground=self.current['activeforeground'], + disabledforeground=self.current['disabledforeground']) + + if 'bg' not in attribs: + widget.configure(background=self.current['background'], + activebackground=self.current['activebackground']) + if sys.platform == 'darwin' and isinstance(widget, tk.Button): + widget.configure(highlightbackground=self.current['background']) + + if 'font' not in attribs: + widget.configure(font=self.current['font']) + elif 'foreground' in widget.keys(): + # e.g. ttk.Label + if 'fg' not in attribs: + widget.configure(foreground=self.current['foreground']), + + if 'bg' not in attribs: + widget.configure(background=self.current['background']) + + if 'font' not in attribs: + widget.configure(font=self.current['font']) + + elif 'background' in widget.keys() or isinstance(widget, tk.Canvas): + # e.g. Frame, Canvas + if 'bg' not in attribs: + widget.configure(background=self.current['background'], + highlightbackground=self.current['disabledforeground']) + + except Exception: + logger.exception(f'Plugin widget issue ? {widget=}') # Apply configured theme