From c62d1104486777a3571d0b990f6cf7151b9b31eb Mon Sep 17 00:00:00 2001 From: Jonathan Harris Date: Wed, 2 Oct 2019 03:01:08 +0100 Subject: [PATCH] Plugin migration --- PLUGINS.md | 12 ++++----- plug.py | 66 +++++++++++++-------------------------------- plugins/coriolis.py | 2 +- plugins/eddb.py | 2 +- plugins/eddn.py | 2 +- plugins/edsm.py | 4 ++- plugins/edsy.py | 2 +- plugins/inara.py | 2 +- 8 files changed, 33 insertions(+), 59 deletions(-) diff --git a/PLUGINS.md b/PLUGINS.md index ae37e7a2..d63f0cb1 100644 --- a/PLUGINS.md +++ b/PLUGINS.md @@ -22,7 +22,7 @@ Plugins are python files. The plugin folder must have a file named `load.py` tha EDMC will import the `load.py` file as a module and then call the `plugin_start()` function. ```python -def plugin_start(plugin_dir): +def plugin_start3(plugin_dir): """ Load this plugin into EDMC """ @@ -30,7 +30,7 @@ def plugin_start(plugin_dir): return "Test" ``` -Any errors or print statements from your plugin will appear in `%TMP%\EDMarketConnector.log` on Windows or `$TMPDIR/EDMarketConnector.log` on Mac. +Any errors or print statements from your plugin will appear in `%TMP%\EDMarketConnector.log` on Windows, `$TMPDIR/EDMarketConnector.log` on Mac, and `$TMP/EDMarketConnector.log` on Linux. This gets called when the user closes the program: @@ -279,12 +279,12 @@ EDMC now lets you disable a plugin without deleting it, simply rename the plugin Disabled and enabled plugins are listed on the "Plugins" Settings tab -# Migration to Python 3.7 +# Migration -In a future release EDMC will use Python **3.7**, instead of the Python 2.7 it has historically used. As of mid-2020 there is a [python3 branch](https://github.com/EDCD/EDMarketConnector/tree/python3) to test migrated plugins against. This is a brief outline of the steps required to migrate a plugin from earlier versions of EDMC: +Starting with release 3.5 EDMC uses Python **3.7**. This is a brief outline of the steps required to migrate a plugin from earlier versions of EDMC: -- Rename the function `plugin_start` to `plugin_start3(plugin_dir)`. Plugins without a `plugin_start3` function will be listed as disabled on EDMC's "Plugins" tab and a message like "plugin SuperSpaceHelper needs migrating" will appear in the log. +- Rename the function `plugin_start` to `plugin_start3(plugin_dir)`. Plugins without a `plugin_start3` function are listed as disabled on EDMC's "Plugins" tab and a message like "plugin SuperSpaceHelper needs migrating" appears in the log. - Check that callback functions `plugin_prefs`, `prefs_changed`, `journal_entry`, `dashboard_entry` and `cmdr_data` if used are declared with the correct number of arguments. Older versions of this app were tolerant of missing arguments in these function declarations. - Port the code to Python 3.7. The [2to3](https://docs.python.org/3/library/2to3.html) tool can automate much of this work. -Depending on the complexity of the plugin it may be feasible to make it compatible with both EDMC 3.4 + Python 2.7 and later EDMC + Python 3.7. [Here's](https://python-future.org/compatible_idioms.html) a guide on writing Python 2/3 compatible code and [here's](https://github.com/Marginal/HabZone/commit/3c41cd41d5ad81ef36aab40e967e3baf77b4bd06) an example of the changes required for a simple plugin. +Depending on the complexity of the plugin it may be feasible to make it compatible with both EDMC 3.4 + Python 2.7 and EDMC 3.5 + Python 3.7. [Here's](https://python-future.org/compatible_idioms.html) a guide on writing Python 2/3 compatible code and [here's](https://github.com/Marginal/HabZone/commit/3c41cd41d5ad81ef36aab40e967e3baf77b4bd06) an example of the changes required for a simple plugin. diff --git a/plug.py b/plug.py index b4fd585b..bb954c24 100644 --- a/plug.py +++ b/plug.py @@ -91,13 +91,19 @@ class Plugin(object): if loadfile: sys.stdout.write('loading plugin {} from "{}"\n'.format(name.replace('.', '_'), loadfile)) - module = importlib.machinery.SourceFileLoader('plugin_{}'.format(name.encode(encoding='ascii', errors='replace').decode('utf-8').replace('.', '_')), loadfile).load_module() - if module.plugin_start.__code__.co_argcount == 0: - newname = module.plugin_start() - else: - newname = module.plugin_start(os.path.dirname(loadfile)) - self.name = newname and str(newname) or name - self.module = module + try: + module = importlib.machinery.SourceFileLoader('plugin_{}'.format(name.encode(encoding='ascii', errors='replace').decode('utf-8').replace('.', '_')), loadfile).load_module() + if getattr(module, 'plugin_start3', None): + newname = module.plugin_start3(os.path.dirname(loadfile)) + self.name = newname and str(newname) or name + self.module = module + elif getattr(module, 'plugin_start', None): + sys.stdout.write('plugin %s needs migrating\n' % name) + else: + sys.stdout.write('plugin %s has no plugin_start3() function\n' % name) + except: + print_exc() + raise else: sys.stdout.write('plugin %s disabled\n' % name) @@ -143,10 +149,7 @@ class Plugin(object): plugin_prefs = self._get_func('plugin_prefs') if plugin_prefs: try: - if plugin_prefs.__code__.co_argcount == 1: - frame = plugin_prefs(parent) - else: - frame = plugin_prefs(parent, cmdr, is_beta) + frame = plugin_prefs(parent, cmdr, is_beta) if not isinstance(frame, nb.Frame): raise AssertionError return frame @@ -169,7 +172,7 @@ def load_plugins(master): plugin.folder = None # Suppress listing in Plugins prefs tab internal.append(plugin) except: - print_exc() + pass PLUGINS.extend(sorted(internal, key = lambda p: operator.attrgetter('name')(p).lower())) # Add plugin folder to load path so packages can be loaded from plugin folder @@ -190,7 +193,7 @@ def load_plugins(master): sys.path.append(os.path.join(config.plugin_dir, name)) found.append(Plugin(name, os.path.join(config.plugin_dir, name, 'load.py'))) except: - print_exc() + pass PLUGINS.extend(sorted(found, key = lambda p: operator.attrgetter('name')(p).lower())) def provides(fn_name): @@ -268,10 +271,7 @@ def notify_prefs_changed(cmdr, is_beta): prefs_changed = plugin._get_func('prefs_changed') if prefs_changed: try: - if prefs_changed.__code__.co_argcount == 0: - prefs_changed() - else: - prefs_changed(cmdr, is_beta) + prefs_changed(cmdr, is_beta) except: print_exc() @@ -293,12 +293,7 @@ def notify_journal_entry(cmdr, is_beta, system, station, entry, state): if journal_entry: try: # Pass a copy of the journal entry in case the callee modifies it - if journal_entry.__code__.co_argcount == 4: - newerror = journal_entry(cmdr, system, station, dict(entry)) - elif journal_entry.__code__.co_argcount == 5: - newerror = journal_entry(cmdr, system, station, dict(entry), dict(state)) - else: - newerror = journal_entry(cmdr, is_beta, system, station, dict(entry), dict(state)) + newerror = journal_entry(cmdr, is_beta, system, station, dict(entry), dict(state)) error = error or newerror except: print_exc() @@ -326,26 +321,6 @@ def notify_dashboard_entry(cmdr, is_beta, entry): return error -def notify_system_changed(timestamp, system, coordinates): - """ - Send notification data to each plugin when we arrive at a new system. - :param timestamp: - :param system: - .. deprecated:: 2.2 - Use :func:`journal_entry` with the 'FSDJump' event. - """ - for plugin in PLUGINS: - system_changed = plugin._get_func('system_changed') - if system_changed: - try: - if system_changed.__code__.co_argcount == 2: - system_changed(timestamp, system) - else: - system_changed(timestamp, system, coordinates) - except: - print_exc() - - def notify_newdata(data, is_beta): """ Send the latest EDMC data from the FD servers to each plugin @@ -358,10 +333,7 @@ def notify_newdata(data, is_beta): cmdr_data = plugin._get_func('cmdr_data') if cmdr_data: try: - if cmdr_data.__code__.co_argcount == 1: - newerror = cmdr_data(data) - else: - newerror = cmdr_data(data, is_beta) + newerror = cmdr_data(data, is_beta) error = error or newerror except: print_exc() diff --git a/plugins/coriolis.py b/plugins/coriolis.py index 2755761c..2a75662e 100644 --- a/plugins/coriolis.py +++ b/plugins/coriolis.py @@ -12,7 +12,7 @@ if not config.get('shipyard_provider') and config.getint('shipyard'): config.delete('shipyard') -def plugin_start(): +def plugin_start3(plugin_dir): return 'Coriolis' # Return a URL for the current ship diff --git a/plugins/eddb.py b/plugins/eddb.py index 8789d517..1f91b023 100644 --- a/plugins/eddb.py +++ b/plugins/eddb.py @@ -51,7 +51,7 @@ def station_id(system_name, station_name): return this.station_ids.get((system_id(system_name), station_name), 0) -def plugin_start(): +def plugin_start3(plugin_dir): return 'eddb' def plugin_app(parent): diff --git a/plugins/eddn.py b/plugins/eddn.py index e3a2bb6d..2f6e6622 100644 --- a/plugins/eddn.py +++ b/plugins/eddn.py @@ -331,7 +331,7 @@ class EDDN(object): # Plugin callbacks -def plugin_start(): +def plugin_start3(plugin_dir): return 'EDDN' def plugin_app(parent): diff --git a/plugins/edsm.py b/plugins/edsm.py index e2367ee6..84bf71a3 100644 --- a/plugins/edsm.py +++ b/plugins/edsm.py @@ -50,7 +50,7 @@ def station_url(system_name, station_name): return 'https://www.edsm.net/en/system?systemName=%s&stationName=ALL' % urllib.parse.quote(system_name) -def plugin_start(): +def plugin_start3(plugin_dir): # Can't be earlier since can only call PhotoImage after window is created this._IMG_KNOWN = tk.PhotoImage(data = 'R0lGODlhEAAQAMIEAFWjVVWkVWS/ZGfFZ////////////////yH5BAEKAAQALAAAAAAQABAAAAMvSLrc/lAFIUIkYOgNXt5g14Dk0AQlaC1CuglM6w7wgs7rMpvNV4q932VSuRiPjQQAOw==') # green circle this._IMG_UNKNOWN = tk.PhotoImage(data = 'R0lGODlhEAAQAKEDAGVLJ+ddWO5fW////yH5BAEKAAMALAAAAAAQABAAAAItnI+pywYRQBtA2CtVvTwjDgrJFlreEJRXgKSqwB5keQ6vOKq1E+7IE5kIh4kCADs=') # red circle @@ -89,6 +89,8 @@ def plugin_stop(): this.queue.put(None) this.thread.join() this.thread = None + # Suppress 'Exception ignored in: ' errors + this._IMG_KNOWN = this._IMG_UNKNOWN = this._IMG_NEW = this._IMG_ERROR = None def plugin_prefs(parent, cmdr, is_beta): diff --git a/plugins/edsy.py b/plugins/edsy.py index aefdaad3..7da92fa4 100644 --- a/plugins/edsy.py +++ b/plugins/edsy.py @@ -6,7 +6,7 @@ import json import io -def plugin_start(): +def plugin_start3(plugin_dir): return 'EDSY' # Return a URL for the current ship diff --git a/plugins/inara.py b/plugins/inara.py index 31b0257c..31386c37 100644 --- a/plugins/inara.py +++ b/plugins/inara.py @@ -64,7 +64,7 @@ def station_url(system_name, station_name): return this.station or this.system -def plugin_start(): +def plugin_start3(plugin_dir): this.thread = Thread(target = worker, name = 'Inara worker') this.thread.daemon = True this.thread.start()