| tests | ||
| watchinotify | ||
| .gitignore | ||
| LICENSE | ||
| Makefile | ||
| pyproject.toml | ||
| README.md | ||
| setup.cfg | ||
FileWatch
FileWatch is a python library to easily use the libc's inotify functions to watch for any file interaction.
The library aims at being simple yet powerfull with easily auditable code.
Limitations
If you watch a file or a folder for a move it will be unwatched after being moved (renaming is considered a move). This is a limitation of the inotify system which desn't give any way to know the new name/path. Any move/rename within watched folders are still being tracked, this also works with the recursive option.
When using the recursive option the folder SUB_CREATED and SUB_MOVED events are enforced. Otherwise the watcher would not be able to keep track of the file tree after adding or moving folders.
Install
Use pip or equivalent to install package name watchinotify:
pip install watchinotify
Usage
Create a Watcher and assign its callback to your custom function, then with the Watcher's watch function to add a list (or any iterable) or path to file/folder to watch. The watcher is going to run on a separated thread waiting for any event then calling the callback. You can run your code after calling watch or wait for an event using the threading.Event for instance.
The following example explicits the parameters types of the callback function:
from pathlib import Path
import threading
from watchinotify import Watcher
event_received = threading.Event()
def callback(path: Path | None, event, name: bytes):
    print(f'New event {event} from path {path} with additionnal name {name.decode()}')
    event_received.set()
watcher = Watcher() as watcher:
watcher.watch([Path('/path/to/watch')])
watcher.callback = callback
# Any of your code from here
event_received.wait()
# Do not forget to close the watcher once done (or use a 'with' statement)
watcher.close()
Using a with statement is easier and safer so the close will be automatically called.
with Watcher(event_type, exclude_patterns, recursive) as watcher:  # will automatically close
    watcher.watch([Path('/path/to/watch')])
The Watcher has 3 arguments:
- event_type : what type of event to watch see the *Event Type section below. If set to None it is using default value of FileEvent.MODIFIED | FileEvent.DELETED | FileEvent.MOVEDfor files andFolderEvent.DELETED | FolderEvent.MOVED | FolderEvent.SUB_CREATED | FolderEvent.SUB_DELETED | FolderEvent.SUB_MOVEDfor folders. You can use the parentEventtype to set all required events, if you want specific events separated it is better to create multiple watchers. Default: None
- exclude_patterns : iterable (list, tuple, etc) of pattern to ignore,following glob conventions, example : ['*/sub_folder', __pycache__]. Default: None
- recursive : boolean to watch recursively all sub folders/files when adding a folder to watch. This will force the events FolderEvent.SUB_CREATEDandFolderEvent.SUB_MOVEDto be watched in order to properly work, you'll have to filter out those event in you callback if unwanted. Default: True
Event Types
All possible events are un the Event flag. You can cumulate events by using the or or | operator. 2 additionnal types FileEvent and FolderEvent inherits from the Event values but only contains the event that can be trigger respectively from files and folders. The 2 sub types also have an ALL value as a handy shortcut.
The events are defined as:
from enum import IntFlag, auto
class Event(IntFlag):
    OPENED = auto()
    MODIFIED = auto()
    DELETED = auto()
    MOVED = auto()
    CLOSED = auto()
    SUB_CREATED = auto()
    SUB_DELETED = auto()
    SUB_MOVED = auto()
class FileEvent(IntFlag):
    OPENED = Event.OPENED.value
    MODIFIED = Event.MODIFIED.value
    DELETED = Event.DELETED.value
    MOVED = Event.MOVED.value
    CLOSED = Event.CLOSED.value
    ALL = OPENED | MODIFIED | DELETED | MOVED | CLOSED
class FolderEvent(IntFlag):
    DELETED = Event.DELETED.value
    MOVED = Event.MOVED.value
    SUB_CREATED = Event.SUB_CREATED.value
    SUB_DELETED = Event.SUB_DELETED.value
    SUB_MOVED = Event.SUB_MOVED.value
    ALL = DELETED | MOVED | SUB_CREATED | SUB_DELETED | SUB_MOVED
Development
Run test
pytest
Run coverage
python tests/run_coverage.py
LICENSE
This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with this program. If not, see https://www.gnu.org/licenses/.