Source code for watch_do.watcher_manager

"""The :class:`.WatcherManager` class is responsible for orchestrating the
watchers.

The watchers are created based on the files returned by the
:class:`.GlobManager` instance that get provided to this class. For each file
that the :class:`.GlobManager` returns a new :class:`.Watcher` is created.
Multiple options can be provided to this class that allows for some
configuration, please see the :meth:`__init__` method documentation for
details.

As an example, the following code would set up watchers for all files returned
by ``glob_manager`` using the :class:`.ModificationTime` method. Newly created
files are detected and added to the watch list and files that get deleted are
considered to be a change (this is specified as the last two parameters of the
constructor).

>>> manager = WatcherManager(
...     ModificationTime, glob_manager, True, True)

All of the changed files can be retrieved by calling :meth:`get_changed_files`.

>>> manager.get_changed_files()
"""


[docs]class WatcherManager: """This class creates and manages watchers. A :class:`.Watcher` and an instance of :class:`.GlobManager` are passed in, which provides the necessary information for the class to create the required watchers that can be used to detect changes. """ def __init__(self, watcher, glob_manager, reglob, changed_on_remove): """Initialise the :class:`.WatcherManager`. Parameters: watcher (:class:`.Watcher`): A reference to a subclass of :class:`.Watcher` (i.e. :class:`.ModificationTime`) that will be used watch the files provided by the :class:`.GlobManager`. glob_manager (:class:`.GlobManager`): The glob manager responsible for providing a list of files. reglob (bool): A boolean value indicating whether to re-evaluate globs when :meth:`get_changed_files` is called. changed_on_remove (bool): A boolean value indicating whether to consider the removal of a file a change. """ self._watcher = watcher self._glob_manager = glob_manager self._reglob = reglob self._changed_on_remove = changed_on_remove self._first_call_to_changed_files = True self._files = set() self._watchers = {} @property def watcher(self): """:class:`.Watcher`: A reference to the :class:`.Watcher` class that is being used to detect changes. """ return self._watcher @property def glob_manager(self): """:class:`.GlobManager`: The instance of the :class:`.GlobManager` that was passed in. """ return self._glob_manager @property def reglob(self): """bool: A boolean value indicating whether we are re-evaluating file globs each time :meth:`get_changed_files` is called. """ return self._reglob @property def changed_on_remove(self): """bool: A boolean value indicating if removed files count as a change. """ return self._changed_on_remove @property def files(self): """set: The ``set`` of file names (relative to the current directory) that are being watched. """ return self._files
[docs] def get_changed_files(self): """Get a ``set`` containing the changed files since the last call. This method determines which files have changed since the last time this method was called. Added files, changed files (determined by the type of watcher) and removed files (if `changed_on_remove` is True) are all counted as changed files. The watchers are stored and managed internally to this class. Returns: set: A ``set`` of files that have changed since the last time this method was called. """ # Keep track of added and removed files added_files = set() removed_files = set() # Update the list of files we should be watching. Only do this if # this is the first time, or if reglob is set to True if self.reglob or self._first_call_to_changed_files: new_files = self.glob_manager.get_files() added_files = new_files - self._files removed_files = self._files - new_files self._files = new_files changed_files = set() # Create watchers for newly found files for file_name in added_files: self._watchers[file_name] = self.watcher(file_name) if not self._first_call_to_changed_files: changed_files.add(file_name) # Remove watchers for non existent files for file_name in removed_files: del self._watchers[file_name] if self.changed_on_remove: changed_files.add(file_name) # Check for changed files for watcher in self._watchers: try: if self._watchers[watcher].has_changed(): changed_files.add(self._watchers[watcher].file_name) except FileNotFoundError as ex: # Handle FileNotFoundError exceptions only if reglobbing is # False, otherwise re-raise it if self.reglob: raise ex self._first_call_to_changed_files = False return changed_files