From aaaad9c45979df0816a02b345db8df9c0769ee7e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krist=C3=B3f=20T=C3=B3th?= Date: Thu, 15 Mar 2018 20:35:57 +0100 Subject: [PATCH 01/10] Add untested DirectoryMonitoringEventHandler implementation --- lib/tfw/components/directory_monitor.py | 45 +++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/lib/tfw/components/directory_monitor.py b/lib/tfw/components/directory_monitor.py index 1493133..fef9759 100644 --- a/lib/tfw/components/directory_monitor.py +++ b/lib/tfw/components/directory_monitor.py @@ -3,6 +3,7 @@ from watchdog.events import FileSystemEventHandler from tfw.networking.event_handlers.server_connector import ServerUplinkConnector from tfw.components.decorators import RateLimiter +from tfw.event_handler_base import TriggerlessEventHandler from tfw.config.logs import logging log = logging.getLogger(__name__) @@ -65,3 +66,47 @@ class DirectoryMonitor: self.directorymonitor.pause() def __exit__(self, exc_type, exc_val, exc_tb): self.directorymonitor.resume() + + +class DirectoryMonitoringEventHandler(TriggerlessEventHandler): + def __init__(self, key, directory): + super().__init__(key) + self._directory = directory + self._monitor = None + self.reload_monitor() + self.commands = {'pause': self.pause, + 'resume': self.resume, + 'ignore': self.ignore} + + @property + def monitor(self): + return self._monitor + + def reload_monitor(self): + if self._monitor: + try: self._monitor.stop() + except KeyError: logging.debug('Working directory was removed – ignoring...') + self._monitor = DirectoryMonitor(self._directory) + self._monitor.watch() # This runs on a separate thread + + def handle_event(self, key, message): + try: + message['data'] = self.commands[message['data']['command']](message['data']) + return message + except KeyError: + log.error('IGNORING MESSAGE: Invalid message received: {}'.format(message)) + + def pause(self, data): + self.monitor.pause() + return data + + def resume(self, data): + self.monitor.resume() + return data + + def ignore(self, data): + self.monitor.ignore += data['ignore'] + return data + + def cleanup(self): + self.monitor.stop() From 7b834f8f25efb8119ec916688369312a3da05159 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krist=C3=B3f=20T=C3=B3th?= Date: Thu, 15 Mar 2018 21:02:49 +0100 Subject: [PATCH 02/10] Expose DirManagingEH.directory as a property --- lib/tfw/components/directory_monitor.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/lib/tfw/components/directory_monitor.py b/lib/tfw/components/directory_monitor.py index fef9759..25cbe94 100644 --- a/lib/tfw/components/directory_monitor.py +++ b/lib/tfw/components/directory_monitor.py @@ -1,5 +1,6 @@ from watchdog.observers import Observer from watchdog.events import FileSystemEventHandler +from os.path import exists, isdir from tfw.networking.event_handlers.server_connector import ServerUplinkConnector from tfw.components.decorators import RateLimiter @@ -78,6 +79,15 @@ class DirectoryMonitoringEventHandler(TriggerlessEventHandler): 'resume': self.resume, 'ignore': self.ignore} + @property + def directory(self): + return self._directory + + @directory.setter + def directory(self, directory): + if not exists(directory) or not isdir(directory): raise EnvironmentError('No such directory!') + self._directory = directory + @property def monitor(self): return self._monitor From 222143665c4e9f1f20a095b06ab87cd82b69fc2d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krist=C3=B3f=20T=C3=B3th?= Date: Thu, 15 Mar 2018 21:05:16 +0100 Subject: [PATCH 03/10] Implement DirManagingEH selectdir API --- lib/tfw/components/directory_monitor.py | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/lib/tfw/components/directory_monitor.py b/lib/tfw/components/directory_monitor.py index 25cbe94..ca82ca1 100644 --- a/lib/tfw/components/directory_monitor.py +++ b/lib/tfw/components/directory_monitor.py @@ -75,9 +75,10 @@ class DirectoryMonitoringEventHandler(TriggerlessEventHandler): self._directory = directory self._monitor = None self.reload_monitor() - self.commands = {'pause': self.pause, - 'resume': self.resume, - 'ignore': self.ignore} + self.commands = {'pause': self.pause, + 'resume': self.resume, + 'ignore': self.ignore, + 'selectdir': self.selectdir} @property def directory(self): @@ -118,5 +119,13 @@ class DirectoryMonitoringEventHandler(TriggerlessEventHandler): self.monitor.ignore += data['ignore'] return data + def selectdir(self, data): + try: + self.directory = data['directory'] + self.reload_monitor() + return data + except EnvironmentError: + log.error('DirManagingEH failed to switch directory!') + def cleanup(self): self.monitor.stop() From 08693d3ba292cda3fc9e77f0be5f65875371df33 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krist=C3=B3f=20T=C3=B3th?= Date: Tue, 20 Mar 2018 11:31:29 +0100 Subject: [PATCH 04/10] Fix infinite recursive property in DirectoryMonitor --- lib/tfw/components/directory_monitor.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/tfw/components/directory_monitor.py b/lib/tfw/components/directory_monitor.py index ca82ca1..4efa741 100644 --- a/lib/tfw/components/directory_monitor.py +++ b/lib/tfw/components/directory_monitor.py @@ -54,7 +54,7 @@ class DirectoryMonitor: @ignore.setter def ignore(self, value): - self.ignore = value if value >= 0 else 0 + self.eventhandler.ignore = value if value >= 0 else 0 @property def pauser(self): From b1f4842dc71fbbe0900ef654c73f6e30fd5cb045 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krist=C3=B3f=20T=C3=B3th?= Date: Tue, 20 Mar 2018 11:39:37 +0100 Subject: [PATCH 05/10] Use now fixed DirectoryMonitor.ignore property in webide --- lib/tfw/components/source_code_event_handler.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/tfw/components/source_code_event_handler.py b/lib/tfw/components/source_code_event_handler.py index 760b28b..8986bf2 100644 --- a/lib/tfw/components/source_code_event_handler.py +++ b/lib/tfw/components/source_code_event_handler.py @@ -102,7 +102,7 @@ class SourceCodeEventHandler(TriggerlessEventHandler): return data def write(self, data): - self.monitor.eventhandler.ignore = 1 + self.monitor.ignore = self.monitor.ignore + 1 try: self.filemanager.file_contents = data['content'] except Exception: log.exception('Error writing file!') del data['content'] From 6c5361846b130fc3a79807b86b05f9bc3d2ff235 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krist=C3=B3f=20T=C3=B3th?= Date: Tue, 20 Mar 2018 11:40:20 +0100 Subject: [PATCH 06/10] Adjust DirectoryMonitor rate limiting to filter multi-inotify events --- lib/tfw/components/directory_monitor.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/tfw/components/directory_monitor.py b/lib/tfw/components/directory_monitor.py index 4efa741..61fe08a 100644 --- a/lib/tfw/components/directory_monitor.py +++ b/lib/tfw/components/directory_monitor.py @@ -23,7 +23,7 @@ class WebideReloadEventHandler(FileSystemEventHandler): def resume(self): self._paused = False - @RateLimiter(rate_per_second=5) + @RateLimiter(rate_per_second=2) def on_modified(self, event): if self._paused: return if self.ignore > 0: From 598cf6218a05d269f301166583a67880a2f762b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krist=C3=B3f=20T=C3=B3th?= Date: Tue, 20 Mar 2018 12:20:13 +0100 Subject: [PATCH 07/10] Remove duplicate name from logging --- lib/tfw/components/directory_monitor.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/tfw/components/directory_monitor.py b/lib/tfw/components/directory_monitor.py index 61fe08a..d1fc797 100644 --- a/lib/tfw/components/directory_monitor.py +++ b/lib/tfw/components/directory_monitor.py @@ -125,7 +125,7 @@ class DirectoryMonitoringEventHandler(TriggerlessEventHandler): self.reload_monitor() return data except EnvironmentError: - log.error('DirManagingEH failed to switch directory!') + log.error('Failed to switch directory!') def cleanup(self): self.monitor.stop() From 359e4780699573e9607f2bf440c8a9bc65b02344 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krist=C3=B3f=20T=C3=B3th?= Date: Tue, 20 Mar 2018 14:29:14 +0100 Subject: [PATCH 08/10] Move DirectoryManagingEventHandler to seperate module --- lib/tfw/components/directory_monitor.py | 64 ----------------- .../directory_monitoring_event_handler.py | 68 +++++++++++++++++++ 2 files changed, 68 insertions(+), 64 deletions(-) create mode 100644 lib/tfw/components/directory_monitoring_event_handler.py diff --git a/lib/tfw/components/directory_monitor.py b/lib/tfw/components/directory_monitor.py index d1fc797..d690c47 100644 --- a/lib/tfw/components/directory_monitor.py +++ b/lib/tfw/components/directory_monitor.py @@ -1,10 +1,8 @@ from watchdog.observers import Observer from watchdog.events import FileSystemEventHandler -from os.path import exists, isdir from tfw.networking.event_handlers.server_connector import ServerUplinkConnector from tfw.components.decorators import RateLimiter -from tfw.event_handler_base import TriggerlessEventHandler from tfw.config.logs import logging log = logging.getLogger(__name__) @@ -67,65 +65,3 @@ class DirectoryMonitor: self.directorymonitor.pause() def __exit__(self, exc_type, exc_val, exc_tb): self.directorymonitor.resume() - - -class DirectoryMonitoringEventHandler(TriggerlessEventHandler): - def __init__(self, key, directory): - super().__init__(key) - self._directory = directory - self._monitor = None - self.reload_monitor() - self.commands = {'pause': self.pause, - 'resume': self.resume, - 'ignore': self.ignore, - 'selectdir': self.selectdir} - - @property - def directory(self): - return self._directory - - @directory.setter - def directory(self, directory): - if not exists(directory) or not isdir(directory): raise EnvironmentError('No such directory!') - self._directory = directory - - @property - def monitor(self): - return self._monitor - - def reload_monitor(self): - if self._monitor: - try: self._monitor.stop() - except KeyError: logging.debug('Working directory was removed – ignoring...') - self._monitor = DirectoryMonitor(self._directory) - self._monitor.watch() # This runs on a separate thread - - def handle_event(self, key, message): - try: - message['data'] = self.commands[message['data']['command']](message['data']) - return message - except KeyError: - log.error('IGNORING MESSAGE: Invalid message received: {}'.format(message)) - - def pause(self, data): - self.monitor.pause() - return data - - def resume(self, data): - self.monitor.resume() - return data - - def ignore(self, data): - self.monitor.ignore += data['ignore'] - return data - - def selectdir(self, data): - try: - self.directory = data['directory'] - self.reload_monitor() - return data - except EnvironmentError: - log.error('Failed to switch directory!') - - def cleanup(self): - self.monitor.stop() diff --git a/lib/tfw/components/directory_monitoring_event_handler.py b/lib/tfw/components/directory_monitoring_event_handler.py new file mode 100644 index 0000000..afceaaf --- /dev/null +++ b/lib/tfw/components/directory_monitoring_event_handler.py @@ -0,0 +1,68 @@ +from os.path import isdir, exists + +from tfw.components.directory_monitor import DirectoryMonitor +from tfw.event_handler_base import TriggerlessEventHandler +from tfw.config.logs import logging +log = logging.getLogger(__name__) + + +class DirectoryMonitoringEventHandler(TriggerlessEventHandler): + def __init__(self, key, directory): + super().__init__(key) + self._directory = directory + self._monitor = None + self.reload_monitor() + self.commands = {'pause': self.pause, + 'resume': self.resume, + 'ignore': self.ignore, + 'selectdir': self.selectdir} + + @property + def directory(self): + return self._directory + + @directory.setter + def directory(self, directory): + if not exists(directory) or not isdir(directory): raise EnvironmentError('No such directory!') + self._directory = directory + + @property + def monitor(self): + return self._monitor + + def reload_monitor(self): + if self._monitor: + try: self._monitor.stop() + except KeyError: logging.debug('Working directory was removed – ignoring...') + self._monitor = DirectoryMonitor(self._directory) + self._monitor.watch() # This runs on a separate thread + + def handle_event(self, key, message): + try: + message['data'] = self.commands[message['data']['command']](message['data']) + return message + except KeyError: + log.error('IGNORING MESSAGE: Invalid message received: {}'.format(message)) + + def pause(self, data): + self.monitor.pause() + return data + + def resume(self, data): + self.monitor.resume() + return data + + def ignore(self, data): + self.monitor.ignore += data['ignore'] + return data + + def selectdir(self, data): + try: + self.directory = data['directory'] + self.reload_monitor() + return data + except EnvironmentError: + log.error('Failed to switch directory!') + + def cleanup(self): + self.monitor.stop() From c8dca5f33b386aed00520ede7eb66c75a174d93c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krist=C3=B3f=20T=C3=B3th?= Date: Tue, 20 Mar 2018 14:32:27 +0100 Subject: [PATCH 09/10] Change order of classes in directory_monitor.py --- lib/tfw/components/directory_monitor.py | 48 ++++++++++++------------- 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/lib/tfw/components/directory_monitor.py b/lib/tfw/components/directory_monitor.py index d690c47..ecdc056 100644 --- a/lib/tfw/components/directory_monitor.py +++ b/lib/tfw/components/directory_monitor.py @@ -8,30 +8,6 @@ from tfw.config.logs import logging log = logging.getLogger(__name__) -class WebideReloadEventHandler(FileSystemEventHandler): - def __init__(self): - super().__init__() - self.uplink = ServerUplinkConnector() - self._paused = False - self.ignore = 0 - - def pause(self): - self._paused = True - - def resume(self): - self._paused = False - - @RateLimiter(rate_per_second=2) - def on_modified(self, event): - if self._paused: return - if self.ignore > 0: - self.ignore = self.ignore - 1 - return - log.debug(event) - key = 'webide' - self.uplink.send(key, {'data': {'command': 'reload'}}) - - class DirectoryMonitor: def __init__(self, directory): self.observer = Observer() @@ -65,3 +41,27 @@ class DirectoryMonitor: self.directorymonitor.pause() def __exit__(self, exc_type, exc_val, exc_tb): self.directorymonitor.resume() + + +class WebideReloadEventHandler(FileSystemEventHandler): + def __init__(self): + super().__init__() + self.uplink = ServerUplinkConnector() + self._paused = False + self.ignore = 0 + + def pause(self): + self._paused = True + + def resume(self): + self._paused = False + + @RateLimiter(rate_per_second=2) + def on_modified(self, event): + if self._paused: return + if self.ignore > 0: + self.ignore = self.ignore - 1 + return + log.debug(event) + key = 'webide' + self.uplink.send(key, {'data': {'command': 'reload'}}) From 6e391b6b2cc2ebf84e78945bdf1aea8a73625fee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krist=C3=B3f=20T=C3=B3th?= Date: Tue, 20 Mar 2018 14:35:21 +0100 Subject: [PATCH 10/10] Rename watchdog's EventHandlers to avoid confusion --- lib/tfw/components/directory_monitor.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/tfw/components/directory_monitor.py b/lib/tfw/components/directory_monitor.py index ecdc056..64d5c23 100644 --- a/lib/tfw/components/directory_monitor.py +++ b/lib/tfw/components/directory_monitor.py @@ -1,5 +1,5 @@ from watchdog.observers import Observer -from watchdog.events import FileSystemEventHandler +from watchdog.events import FileSystemEventHandler as FileSystemWatchdogEventHandler from tfw.networking.event_handlers.server_connector import ServerUplinkConnector from tfw.components.decorators import RateLimiter @@ -11,7 +11,7 @@ log = logging.getLogger(__name__) class DirectoryMonitor: def __init__(self, directory): self.observer = Observer() - self.eventhandler = WebideReloadEventHandler() + self.eventhandler = WebideReloadWatchdogEventHandler() self.observer.schedule(self.eventhandler, directory, recursive=True) self.pause, self.resume = self.eventhandler.pause, self.eventhandler.resume @@ -43,7 +43,7 @@ class DirectoryMonitor: self.directorymonitor.resume() -class WebideReloadEventHandler(FileSystemEventHandler): +class WebideReloadWatchdogEventHandler(FileSystemWatchdogEventHandler): def __init__(self): super().__init__() self.uplink = ServerUplinkConnector()