2018-03-25 13:43:59 +00:00
|
|
|
from os.path import dirname
|
2018-03-29 09:23:38 +00:00
|
|
|
from re import compile, findall
|
|
|
|
from abc import ABC, abstractmethod
|
2018-03-25 13:43:59 +00:00
|
|
|
|
2018-03-03 16:15:21 +00:00
|
|
|
from watchdog.observers import Observer
|
2018-03-05 15:57:50 +00:00
|
|
|
from watchdog.events import PatternMatchingEventHandler
|
2018-03-03 16:15:21 +00:00
|
|
|
|
2018-03-07 09:12:58 +00:00
|
|
|
from tfw.components.mixins.callback_mixin import CallbackMixin
|
2018-03-29 09:23:38 +00:00
|
|
|
from tfw.components.decorators.rate_limiter import RateLimiter
|
2018-03-07 09:12:58 +00:00
|
|
|
|
2018-03-03 16:15:21 +00:00
|
|
|
|
2018-03-29 09:23:38 +00:00
|
|
|
class CallbackEventHandler(PatternMatchingEventHandler, ABC):
|
2018-03-05 15:57:50 +00:00
|
|
|
def __init__(self, files, *callbacks):
|
|
|
|
super().__init__(files)
|
2018-03-03 16:15:21 +00:00
|
|
|
self.callbacks = callbacks
|
|
|
|
|
2018-03-29 09:23:38 +00:00
|
|
|
@RateLimiter(rate_per_second=2)
|
2018-03-03 16:15:21 +00:00
|
|
|
def on_modified(self, event):
|
|
|
|
for callback in self.callbacks:
|
|
|
|
callback()
|
|
|
|
|
|
|
|
|
2018-03-29 09:23:38 +00:00
|
|
|
class HistoryMonitor(CallbackMixin, ABC):
|
2018-03-03 16:15:21 +00:00
|
|
|
def __init__(self, histfile):
|
2018-03-07 09:12:58 +00:00
|
|
|
CallbackMixin.__init__(self)
|
2018-03-03 16:15:21 +00:00
|
|
|
self.histfile = histfile
|
2018-03-03 21:44:05 +00:00
|
|
|
self._history = []
|
2018-03-05 15:23:01 +00:00
|
|
|
self._last_length = len(self._history)
|
2018-03-03 16:15:21 +00:00
|
|
|
self.observer = Observer()
|
2018-03-05 15:57:50 +00:00
|
|
|
self.observer.schedule(CallbackEventHandler([self.histfile],
|
|
|
|
self._fetch_history,
|
2018-03-05 15:23:01 +00:00
|
|
|
self._invoke_callbacks),
|
|
|
|
dirname(self.histfile))
|
2018-03-03 16:15:21 +00:00
|
|
|
|
|
|
|
@property
|
|
|
|
def history(self):
|
|
|
|
return self._history
|
|
|
|
|
|
|
|
def _fetch_history(self):
|
2018-03-05 15:23:01 +00:00
|
|
|
self._last_length = len(self._history)
|
2018-03-03 16:15:21 +00:00
|
|
|
with open(self.histfile, 'r') as ifile:
|
2018-03-29 09:23:38 +00:00
|
|
|
pattern = compile(self.command_pattern)
|
|
|
|
data = ifile.read()
|
|
|
|
self._history = [self.sanitize_command(command) for command in findall(pattern, data)]
|
|
|
|
|
|
|
|
@property
|
|
|
|
@abstractmethod
|
|
|
|
def command_pattern(self):
|
|
|
|
raise NotImplementedError
|
|
|
|
|
|
|
|
def sanitize_command(self, command):
|
|
|
|
return command
|
2018-03-03 16:15:21 +00:00
|
|
|
|
2018-03-05 15:23:01 +00:00
|
|
|
def _invoke_callbacks(self):
|
|
|
|
if self._last_length < len(self._history):
|
2018-03-07 09:12:58 +00:00
|
|
|
self._execute_callbacks(self.history)
|
2018-03-05 15:23:01 +00:00
|
|
|
|
2018-03-03 16:15:21 +00:00
|
|
|
def watch(self):
|
|
|
|
self.observer.start()
|
|
|
|
|
|
|
|
def stop(self):
|
|
|
|
self.observer.stop()
|
|
|
|
self.observer.join()
|
2018-03-29 09:23:38 +00:00
|
|
|
|
|
|
|
|
|
|
|
class BashMonitor(HistoryMonitor):
|
|
|
|
@property
|
|
|
|
def command_pattern(self):
|
|
|
|
return r'.+'
|
|
|
|
|
|
|
|
def sanitize_command(self, command):
|
|
|
|
return command.strip()
|
2018-03-29 09:37:25 +00:00
|
|
|
|
|
|
|
|
|
|
|
class GDBMonitor(HistoryMonitor):
|
|
|
|
@property
|
|
|
|
def command_pattern(self):
|
|
|
|
return r'(?<=\n)\+(.+)\n'
|