From f92385244c8f8deaaafe2e0dd9499220a0188e08 Mon Sep 17 00:00:00 2001 From: Athanasius Date: Mon, 11 Jan 2021 15:37:17 +0000 Subject: [PATCH 01/14] config: Add class member and methods for signalling/checking shutdown status --- config.py | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/config.py b/config.py index 08d7ec89..f0915cac 100644 --- a/config.py +++ b/config.py @@ -117,6 +117,8 @@ class Config(object): if platform=='darwin': def __init__(self): + self.in_shutdown = False # Is the application currently shutting down ? + self.app_dir = join(NSSearchPathForDirectoriesInDomains(NSApplicationSupportDirectory, NSUserDomainMask, True)[0], appname) if not isdir(self.app_dir): mkdir(self.app_dir) @@ -177,9 +179,16 @@ class Config(object): self.save() self.defaults = None + def set_shutdown(self): + self.in_shutdown = True + + def shutting_down(self) -> bool: + return self.in_shutdown + elif platform=='win32': def __init__(self): + self.in_shutdown = False # Is the application currently shutting down ? self.app_dir = join(KnownFolderPath(FOLDERID_LocalAppData), appname) if not isdir(self.app_dir): @@ -268,11 +277,18 @@ class Config(object): RegCloseKey(self.hkey) self.hkey = None + def set_shutdown(self): + self.in_shutdown = True + + def shutting_down(self) -> bool: + return self.in_shutdown + elif platform=='linux': SECTION = 'config' def __init__(self): + self.in_shutdown = False # Is the application currently shutting down ? # http://standards.freedesktop.org/basedir-spec/latest/ar01s03.html self.app_dir = join(getenv('XDG_DATA_HOME', expanduser('~/.local/share')), appname) @@ -346,6 +362,12 @@ class Config(object): self.save() self.config = None + def set_shutdown(self): + self.in_shutdown = True + + def shutting_down(self) -> bool: + return self.in_shutdown + def _escape(self, val): return str(val).replace(u'\\', u'\\\\').replace(u'\n', u'\\n').replace(u';', u'\\;') From 08f1f3e7d99e2e1f47a89ba5e1bf9eabcec24bc1 Mon Sep 17 00:00:00 2001 From: Athanasius Date: Mon, 11 Jan 2021 15:37:47 +0000 Subject: [PATCH 02/14] EDMarketConnector: call config.set_shutdown() in AppWindow.onexit() --- EDMarketConnector.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/EDMarketConnector.py b/EDMarketConnector.py index 210186e3..aba22e7b 100755 --- a/EDMarketConnector.py +++ b/EDMarketConnector.py @@ -1059,6 +1059,8 @@ class AppWindow(object): self.status['text'] = str(e) def onexit(self, event=None): + config.set_shutdown() # Signal we're in shutdown now. + # http://core.tcl.tk/tk/tktview/c84f660833546b1b84e7 if platform != 'darwin' or self.w.winfo_rooty() > 0: x, y = self.w.geometry().split('+')[1:3] # e.g. '212x170+2881+1267' From 5175136419876d0fb1269bc81a5d3aabed785f4a Mon Sep 17 00:00:00 2001 From: Athanasius Date: Mon, 11 Jan 2021 15:49:08 +0000 Subject: [PATCH 03/14] plug.py: Avoid Tk event_generate() call from show_error() if in shutdown --- plug.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/plug.py b/plug.py index 07d43dfb..98591a84 100644 --- a/plug.py +++ b/plug.py @@ -366,9 +366,16 @@ def notify_newdata(data, is_beta): def show_error(err): """ Display an error message in the status line of the main window. + + Will be NOP during shutdown to avoid Tk hang. :param err: .. versionadded:: 2.3.7 """ + + if config.shutting_down(): + logger.info(f'Called during shutdown: "{str(err)}"') + return + if err and last_error['root']: last_error['msg'] = str(err) last_error['root'].event_generate('<>', when="tail") From 50f520ab66ff4720e99c92ddd884d8d758a912f7 Mon Sep 17 00:00:00 2001 From: Athanasius Date: Mon, 11 Jan 2021 15:53:12 +0000 Subject: [PATCH 04/14] config: Change self.in_shutdown to be mangled. I specifically made set_shutdown() *not* take an argument and only ever set this true so no-one else can monkey with it, so might as well mangle it too. --- config.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/config.py b/config.py index f0915cac..0ff7ae7e 100644 --- a/config.py +++ b/config.py @@ -117,7 +117,7 @@ class Config(object): if platform=='darwin': def __init__(self): - self.in_shutdown = False # Is the application currently shutting down ? + self.__in_shutdown = False # Is the application currently shutting down ? self.app_dir = join(NSSearchPathForDirectoriesInDomains(NSApplicationSupportDirectory, NSUserDomainMask, True)[0], appname) if not isdir(self.app_dir): @@ -180,15 +180,15 @@ class Config(object): self.defaults = None def set_shutdown(self): - self.in_shutdown = True + self.__in_shutdown = True def shutting_down(self) -> bool: - return self.in_shutdown + return self.__in_shutdown elif platform=='win32': def __init__(self): - self.in_shutdown = False # Is the application currently shutting down ? + self.__in_shutdown = False # Is the application currently shutting down ? self.app_dir = join(KnownFolderPath(FOLDERID_LocalAppData), appname) if not isdir(self.app_dir): @@ -278,17 +278,17 @@ class Config(object): self.hkey = None def set_shutdown(self): - self.in_shutdown = True + self.__in_shutdown = True def shutting_down(self) -> bool: - return self.in_shutdown + return self.__in_shutdown elif platform=='linux': SECTION = 'config' def __init__(self): - self.in_shutdown = False # Is the application currently shutting down ? + self.__in_shutdown = False # Is the application currently shutting down ? # http://standards.freedesktop.org/basedir-spec/latest/ar01s03.html self.app_dir = join(getenv('XDG_DATA_HOME', expanduser('~/.local/share')), appname) @@ -363,10 +363,10 @@ class Config(object): self.config = None def set_shutdown(self): - self.in_shutdown = True + self.__in_shutdown = True def shutting_down(self) -> bool: - return self.in_shutdown + return self.__in_shutdown def _escape(self, val): return str(val).replace(u'\\', u'\\\\').replace(u'\n', u'\\n').replace(u';', u'\\;') From cb9bd30a05f7389f8d95721a1485cc95856e41dc Mon Sep 17 00:00:00 2001 From: Athanasius Date: Mon, 11 Jan 2021 16:41:36 +0000 Subject: [PATCH 05/14] dashboard: Bail early from process() if in shutdown --- dashboard.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/dashboard.py b/dashboard.py index 4e7f9ce6..68e828e5 100644 --- a/dashboard.py +++ b/dashboard.py @@ -115,6 +115,9 @@ class Dashboard(FileSystemEventHandler): # Can be called either in watchdog thread or, if polling, in main thread. def process(self, logfile=None): + if config.shutting_down(): + return + try: with open(join(self.currentdir, 'Status.json'), 'rb') as h: data = h.read().strip() @@ -126,6 +129,7 @@ class Dashboard(FileSystemEventHandler): self.status != entry): self.status = entry self.root.event_generate('<>', when="tail") + except Exception: logger.exception('Reading Status.json') From 6cdc1dc20cafc296b86a87740511d20230fc8d60 Mon Sep 17 00:00:00 2001 From: Athanasius Date: Mon, 11 Jan 2021 16:41:57 +0000 Subject: [PATCH 06/14] hotkey: Avoid event_generate() calls during shutdown --- hotkey.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/hotkey.py b/hotkey.py index 8ee8762d..5b3323b8 100644 --- a/hotkey.py +++ b/hotkey.py @@ -92,11 +92,15 @@ if platform == 'darwin': self.observer = NSEvent.addGlobalMonitorForEventsMatchingMask_handler_(NSKeyDownMask, self._handler) def _poll(self): + if config.shutting_down(): + return + # No way of signalling to Tkinter from within the callback handler block that doesn't # cause Python to crash, so poll. if self.activated: self.activated = False self.root.event_generate('<>', when="tail") + if self.keycode or self.modifiers: self.root.after(HotkeyMgr.POLL, self._poll) @@ -123,6 +127,9 @@ if platform == 'darwin': self.acquire_state = HotkeyMgr.ACQUIRE_INACTIVE def _acquire_poll(self): + if config.shutting_down(): + return + # No way of signalling to Tkinter from within the monkey-patched event handler that doesn't # cause Python to crash, so poll. if self.acquire_state: @@ -316,7 +323,8 @@ elif platform == 'win32': while GetMessage(ctypes.byref(msg), None, 0, 0) != 0: if msg.message == WM_HOTKEY: if config.getint('hotkey_always') or WindowTitle(GetForegroundWindow()).startswith('Elite - Dangerous'): - self.root.event_generate('<>', when="tail") + if not config.shutting_down(): + self.root.event_generate('<>', when="tail") else: # Pass the key on UnregisterHotKey(None, 1) From 571f9665b3dc3cc74577b174361f9ae2ce0d030b Mon Sep 17 00:00:00 2001 From: Athanasius Date: Mon, 11 Jan 2021 16:43:07 +0000 Subject: [PATCH 07/14] monitor: Avoid Tk event_generate() calls during shutdown. --- monitor.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/monitor.py b/monitor.py index 9b57ef2d..4901c3f2 100644 --- a/monitor.py +++ b/monitor.py @@ -337,7 +337,8 @@ class EDLogs(FileSystemEventHandler): # type: ignore # See below self.event_queue.append(line) if self.event_queue: - self.root.event_generate('<>', when="tail") + if not config.shutting_down(): + self.root.event_generate('<>', when="tail") log_pos = loghandle.tell() @@ -355,7 +356,9 @@ class EDLogs(FileSystemEventHandler): # type: ignore # See below '{{ "timestamp":"{}", "event":"ShutDown" }}'.format(strftime('%Y-%m-%dT%H:%M:%SZ', gmtime())) ) - self.root.event_generate('<>', when="tail") + if not config.shutting_down(): + self.root.event_generate('<>', when="tail") + self.game_was_running = False else: From 4bddc4c0cb8fac74b85316577d1910bc18f84ddc Mon Sep 17 00:00:00 2001 From: Athanasius Date: Mon, 11 Jan 2021 16:43:33 +0000 Subject: [PATCH 08/14] prefs: Avoid Tk event_generate() calls during shutdown --- prefs.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/prefs.py b/prefs.py index 95612e6a..69f7140c 100644 --- a/prefs.py +++ b/prefs.py @@ -1157,4 +1157,5 @@ class PreferencesDialog(tk.Toplevel): except Exception: AXIsProcessTrustedWithOptions({kAXTrustedCheckOptionPrompt: True}) - self.parent.event_generate('<>', when="tail") + if not config.shutting_down(): + self.parent.event_generate('<>', when="tail") From 92d5657e216ae5b60713624b7b53843878d00f10 Mon Sep 17 00:00:00 2001 From: Athanasius Date: Mon, 11 Jan 2021 16:43:53 +0000 Subject: [PATCH 09/14] protocol: Avoid Tk event_generate() calls during shutdown --- protocol.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/protocol.py b/protocol.py index 49345643..ea7bfaf6 100644 --- a/protocol.py +++ b/protocol.py @@ -32,7 +32,9 @@ class GenericProtocolHandler(object): def event(self, url): self.lastpayload = url - self.master.event_generate('<>', when="tail") + + if not config.shutting_down(): + self.master.event_generate('<>', when="tail") if sys.platform == 'darwin' and getattr(sys, 'frozen', False): From fb1c9fc0347e38043d2a4873688177dc956b2304 Mon Sep 17 00:00:00 2001 From: Athanasius Date: Mon, 11 Jan 2021 16:44:11 +0000 Subject: [PATCH 10/14] update: Avoid Tk event_generate() calls during shutdown. --- update.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/update.py b/update.py index c234dfc9..3b57fb9e 100644 --- a/update.py +++ b/update.py @@ -40,7 +40,8 @@ class Updater(object): Receive (Win)Sparkle shutdown request and send it to parent. :rtype: None """ - self.root.event_generate('<>', when="tail") + if not config.shutting_down(): + self.root.event_generate('<>', when="tail") def use_internal(self) -> bool: """ From bca0231dfc6d6166514a50515b3f67f2d17600a9 Mon Sep 17 00:00:00 2001 From: Athanasius Date: Mon, 11 Jan 2021 16:45:26 +0000 Subject: [PATCH 11/14] plugins/edsm: Avoid Tk event_generate() calls during shutdown --- plugins/edsm.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/plugins/edsm.py b/plugins/edsm.py index 80c0077a..a77de879 100644 --- a/plugins/edsm.py +++ b/plugins/edsm.py @@ -598,8 +598,10 @@ def worker() -> None: if not closing and e['event'] in ('StartUp', 'Location', 'FSDJump', 'CarrierJump'): # Update main window's system status this.lastlookup = r - # calls update_status in main thread - this.system_link.event_generate('<>', when="tail") + + if not config.shutting_down(): + # calls update_status in main thread + this.system_link.event_generate('<>', when="tail") if r['msgnum'] // 100 != 1: logger.warning(f'EDSM event with not-1xx status:\n{r["msgnum"]}\n{r["msg"]}\n' From 111f445cac62291870f2354eacf907c8020d22cc Mon Sep 17 00:00:00 2001 From: Athanasius Date: Mon, 11 Jan 2021 16:45:45 +0000 Subject: [PATCH 12/14] plugins/inara: Avoid Tk event_generate() calls during shutdown --- plugins/inara.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/plugins/inara.py b/plugins/inara.py index 4b61322c..9aeef582 100644 --- a/plugins/inara.py +++ b/plugins/inara.py @@ -1290,13 +1290,17 @@ def send_data(url: str, data: Mapping[str, Any]) -> bool: 'setCommanderTravelLocation' ): this.lastlocation = reply_event.get('eventData', {}) - # calls update_location in main thread - this.system_link.event_generate('<>', when="tail") + + if not config.shutting_down(): + # calls update_location in main thread + this.system_link.event_generate('<>', when="tail") elif data_event['eventName'] in ['addCommanderShip', 'setCommanderShip']: this.lastship = reply_event.get('eventData', {}) - # calls update_ship in main thread - this.system_link.event_generate('<>', when="tail") + + if not config.shutting_down(): + # calls update_ship in main thread + this.system_link.event_generate('<>', when="tail") return True # regardless of errors above, we DID manage to send it, therefore inform our caller as such From 462cd253129319d67e0b715e6b3f6cf17b511375 Mon Sep 17 00:00:00 2001 From: Athanasius Date: Mon, 11 Jan 2021 16:54:09 +0000 Subject: [PATCH 13/14] config: Make config.shutting_down() a property, and change 'callers' --- config.py | 1 + dashboard.py | 4 ++-- hotkey.py | 6 +++--- monitor.py | 4 ++-- plug.py | 2 +- plugins/edsm.py | 2 +- plugins/inara.py | 4 ++-- prefs.py | 2 +- protocol.py | 2 +- update.py | 4 ++-- 10 files changed, 16 insertions(+), 15 deletions(-) diff --git a/config.py b/config.py index 0ff7ae7e..76013029 100644 --- a/config.py +++ b/config.py @@ -182,6 +182,7 @@ class Config(object): def set_shutdown(self): self.__in_shutdown = True + @property def shutting_down(self) -> bool: return self.__in_shutdown diff --git a/dashboard.py b/dashboard.py index 68e828e5..4259dccb 100644 --- a/dashboard.py +++ b/dashboard.py @@ -115,9 +115,9 @@ class Dashboard(FileSystemEventHandler): # Can be called either in watchdog thread or, if polling, in main thread. def process(self, logfile=None): - if config.shutting_down(): + if config.shutting_down: return - + try: with open(join(self.currentdir, 'Status.json'), 'rb') as h: data = h.read().strip() diff --git a/hotkey.py b/hotkey.py index 5b3323b8..9b261ccc 100644 --- a/hotkey.py +++ b/hotkey.py @@ -92,7 +92,7 @@ if platform == 'darwin': self.observer = NSEvent.addGlobalMonitorForEventsMatchingMask_handler_(NSKeyDownMask, self._handler) def _poll(self): - if config.shutting_down(): + if config.shutting_down: return # No way of signalling to Tkinter from within the callback handler block that doesn't @@ -127,7 +127,7 @@ if platform == 'darwin': self.acquire_state = HotkeyMgr.ACQUIRE_INACTIVE def _acquire_poll(self): - if config.shutting_down(): + if config.shutting_down: return # No way of signalling to Tkinter from within the monkey-patched event handler that doesn't @@ -323,7 +323,7 @@ elif platform == 'win32': while GetMessage(ctypes.byref(msg), None, 0, 0) != 0: if msg.message == WM_HOTKEY: if config.getint('hotkey_always') or WindowTitle(GetForegroundWindow()).startswith('Elite - Dangerous'): - if not config.shutting_down(): + if not config.shutting_down: self.root.event_generate('<>', when="tail") else: # Pass the key on diff --git a/monitor.py b/monitor.py index 4901c3f2..f8d4a201 100644 --- a/monitor.py +++ b/monitor.py @@ -337,7 +337,7 @@ class EDLogs(FileSystemEventHandler): # type: ignore # See below self.event_queue.append(line) if self.event_queue: - if not config.shutting_down(): + if not config.shutting_down: self.root.event_generate('<>', when="tail") log_pos = loghandle.tell() @@ -356,7 +356,7 @@ class EDLogs(FileSystemEventHandler): # type: ignore # See below '{{ "timestamp":"{}", "event":"ShutDown" }}'.format(strftime('%Y-%m-%dT%H:%M:%SZ', gmtime())) ) - if not config.shutting_down(): + if not config.shutting_down: self.root.event_generate('<>', when="tail") self.game_was_running = False diff --git a/plug.py b/plug.py index 98591a84..755fec1c 100644 --- a/plug.py +++ b/plug.py @@ -372,7 +372,7 @@ def show_error(err): .. versionadded:: 2.3.7 """ - if config.shutting_down(): + if config.shutting_down: logger.info(f'Called during shutdown: "{str(err)}"') return diff --git a/plugins/edsm.py b/plugins/edsm.py index a77de879..e9fbd46c 100644 --- a/plugins/edsm.py +++ b/plugins/edsm.py @@ -599,7 +599,7 @@ def worker() -> None: # Update main window's system status this.lastlookup = r - if not config.shutting_down(): + if not config.shutting_down: # calls update_status in main thread this.system_link.event_generate('<>', when="tail") diff --git a/plugins/inara.py b/plugins/inara.py index 9aeef582..ac5d53f5 100644 --- a/plugins/inara.py +++ b/plugins/inara.py @@ -1291,14 +1291,14 @@ def send_data(url: str, data: Mapping[str, Any]) -> bool: ): this.lastlocation = reply_event.get('eventData', {}) - if not config.shutting_down(): + if not config.shutting_down: # calls update_location in main thread this.system_link.event_generate('<>', when="tail") elif data_event['eventName'] in ['addCommanderShip', 'setCommanderShip']: this.lastship = reply_event.get('eventData', {}) - if not config.shutting_down(): + if not config.shutting_down: # calls update_ship in main thread this.system_link.event_generate('<>', when="tail") diff --git a/prefs.py b/prefs.py index 69f7140c..e9c3afab 100644 --- a/prefs.py +++ b/prefs.py @@ -1157,5 +1157,5 @@ class PreferencesDialog(tk.Toplevel): except Exception: AXIsProcessTrustedWithOptions({kAXTrustedCheckOptionPrompt: True}) - if not config.shutting_down(): + if not config.shutting_down: self.parent.event_generate('<>', when="tail") diff --git a/protocol.py b/protocol.py index ea7bfaf6..7990b952 100644 --- a/protocol.py +++ b/protocol.py @@ -33,7 +33,7 @@ class GenericProtocolHandler(object): def event(self, url): self.lastpayload = url - if not config.shutting_down(): + if not config.shutting_down: self.master.event_generate('<>', when="tail") diff --git a/update.py b/update.py index 3b57fb9e..aa2a1a8f 100644 --- a/update.py +++ b/update.py @@ -40,7 +40,7 @@ class Updater(object): Receive (Win)Sparkle shutdown request and send it to parent. :rtype: None """ - if not config.shutting_down(): + if not config.shutting_down: self.root.event_generate('<>', when="tail") def use_internal(self) -> bool: @@ -209,4 +209,4 @@ class Updater(object): :return: None """ - pass \ No newline at end of file + pass From 247577c5f4b778c2ac81ba26497b3036d941236d Mon Sep 17 00:00:00 2001 From: Athanasius Date: Mon, 11 Jan 2021 17:13:31 +0000 Subject: [PATCH 14/14] config: Also set Config.shutting_down to be @propety on win32 and linux --- config.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/config.py b/config.py index 76013029..76fa6fdf 100644 --- a/config.py +++ b/config.py @@ -281,6 +281,7 @@ class Config(object): def set_shutdown(self): self.__in_shutdown = True + @property def shutting_down(self) -> bool: return self.__in_shutdown @@ -366,6 +367,7 @@ class Config(object): def set_shutdown(self): self.__in_shutdown = True + @property def shutting_down(self) -> bool: return self.__in_shutdown