From 9acb997c8952861f311180e33637e8fa3d8cbbc2 Mon Sep 17 00:00:00 2001 From: Nicholas Bollweg Date: Fri, 22 Apr 2022 21:42:50 -0500 Subject: [PATCH] use watchfiles --- README.md | 10 ++-- setup.cfg | 5 +- src/doit_auto1/filewatch.py | 95 +++++++------------------------------ tests/test_cmd_auto.py | 9 ---- 4 files changed, 22 insertions(+), 97 deletions(-) diff --git a/README.md b/README.md index a9801b5..24a952e 100644 --- a/README.md +++ b/README.md @@ -4,11 +4,7 @@ Watch filesystem and re-execute on changes. # Status -This is the *first* generation of `doit` `auto` command. +This is the *second* generation of `doit` `auto` command. -It is based on: - -- Linux: pyinotify -- MAC: macfsevents ` - -Unfortunately both of this projects are not maintained anymore. +It is based on [`watchfiles`](https://pypi.org/project/watchfiles) with support +from the [Notify](https://docs.rs/notify/latest/notify/) rust package. diff --git a/setup.cfg b/setup.cfg index cf5d43b..6b088e6 100644 --- a/setup.cfg +++ b/setup.cfg @@ -25,9 +25,8 @@ package_dir = packages = doit_auto1 python_requires = >=3.8 install_requires = - macfsevents;sys_platform=='darwin' - pyinotify;sys_platform=='linux' + watchfiles [options.entry_points] doit.COMMAND = - auto = doit_auto1:Auto \ No newline at end of file + auto = doit_auto1:Auto diff --git a/src/doit_auto1/filewatch.py b/src/doit_auto1/filewatch.py index de880ac..6cc0687 100644 --- a/src/doit_auto1/filewatch.py +++ b/src/doit_auto1/filewatch.py @@ -3,32 +3,23 @@ """ import os.path - - -def get_platform_system(): - """return platform.system - platform module has many regexp, so importing it is slow... - import only if required - """ - import platform - return platform.system() +import watchfiles class FileModifyWatcher(object): - """Use inotify to watch file-system for file modifications + """Use watchfiles to watch file-system for file modifications Usage: 1) subclass the method handle_event, action to be performed 2) create an object passing a list of files to be watched 3) call the loop method """ - supported_platforms = ('Darwin', 'Linux') def __init__(self, path_list): """@param file_list (list-str): files to be watched""" self.file_list = set() - self.watch_dirs = set() # all dirs to be watched - self.notify_dirs = set() # dirs that generate notification whatever file + self.watch_dirs = set() # all dirs to be watched + self.notify_dirs = set() # dirs that generate notification whatever file for filename in path_list: path = os.path.abspath(filename) if os.path.isfile(path): @@ -37,74 +28,22 @@ def __init__(self, path_list): else: self.notify_dirs.add(path) self.watch_dirs.add(path) - self.platform = get_platform_system() - if self.platform not in self.supported_platforms: - msg = "Unsupported platform '%s'\n" % self.platform - msg += ("'auto' command is supported only on %s" % - (self.supported_platforms,)) - raise Exception(msg) - def _handle(self, event): - """calls platform specific handler""" - if self.platform == 'Darwin': # pragma: no cover - filename = event.name - elif self.platform == 'Linux': - filename = event.pathname - if (filename in self.file_list or - os.path.dirname(filename) in self.notify_dirs): - self.handle_event(event) + def _handle(self, changes): + """calls implementation handler""" + if any( + change[1] in self.file_list + or os.path.dirname(change[1]) in self.notify_dirs + for change in changes + ): + return self.handle_event(changes) - def handle_event(self, event): + def handle_event(self, event): # pragma: no cover """this should be sub-classed """ raise NotImplementedError + def loop(self): + """Infinite loop watching for file modifications""" - def _loop_darwin(self): # pragma: no cover - """loop implementation for darwin platform""" - from fsevents import Observer #pylint: disable=F0401 - from fsevents import Stream #pylint: disable=F0401 - from fsevents import IN_MODIFY #pylint: disable=F0401 - - observer = Observer() - handler = self._handle - def fsevent_callback(event): - if event.mask == IN_MODIFY: - handler(event) - - for watch_this in self.watch_dirs: - stream = Stream(fsevent_callback, watch_this, file_events=True) - observer.schedule(stream) - - observer.daemon = True - observer.run() - - - def _loop_linux(self, loop_callback): - """loop implementation for linux platform""" - import pyinotify - handler = self._handle - class EventHandler(pyinotify.ProcessEvent): - def process_default(self, event): - handler(event) - - watch_manager = pyinotify.WatchManager() - event_handler = EventHandler() - notifier = pyinotify.Notifier(watch_manager, event_handler) - - mask = pyinotify.IN_CLOSE_WRITE | pyinotify.IN_MOVED_TO - for watch_this in self.watch_dirs: - watch_manager.add_watch(watch_this, mask) - - notifier.loop(loop_callback) - - - def loop(self, loop_callback=None): - """Infinite loop watching for file modifications - @loop_callback: used to stop loop on unittests - """ - - if self.platform == 'Darwin': # pragma: no cover - self._loop_darwin() - - elif self.platform == 'Linux': - self._loop_linux(loop_callback) + for changes in watchfiles.watch(*self.watch_dirs): + self._handle(changes) diff --git a/tests/test_cmd_auto.py b/tests/test_cmd_auto.py index 724e29e..a7a7ff9 100644 --- a/tests/test_cmd_auto.py +++ b/tests/test_cmd_auto.py @@ -1,23 +1,14 @@ import time from multiprocessing import Process -import pytest - from doit.cmdparse import DefaultUpdate from doit.task import Task from doit.cmd_base import TaskLoader2 -from doit_auto1 import filewatch from doit_auto1 import cmd_auto from .conftest import CmdFactory -# skip all tests in this module if platform not supported -platform = filewatch.get_platform_system() -pytestmark = pytest.mark.skipif( - 'platform not in filewatch.FileModifyWatcher.supported_platforms') - - class TestFindFileDeps(object): def find_deps(self, sel_tasks): tasks = {