Skip to content

Commit

Permalink
#16: Inited the filtering functionality (#34)
Browse files Browse the repository at this point in the history
* #16: Inited the filtering functionality

* #16 Covered filters by tests

* v.0.0.8-alpha1
  • Loading branch information
roma-glushko authored Jan 2, 2024
1 parent 33f21a8 commit 8d2906f
Show file tree
Hide file tree
Showing 10 changed files with 788 additions and 687 deletions.
2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "notifykit_lib"
version = "0.0.7-alpha.1"
version = "0.0.8-alpha.1"
edition = "2021"
license = "A toolkit for building applications watching filesystem changes"
homepage = "https://github.com/roma-glushko/notifykit"
Expand Down
6 changes: 5 additions & 1 deletion notifykit/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from notifykit._notifier import Notifier, NotifierT, Event
from notifykit._filters import EventFilter, CommonFilter
from notifykit._notifier import Notifier, NotifierT
from notifykit._notifykit_lib import (
__version__,
ObjectType,
Expand All @@ -17,13 +18,16 @@
)

from notifykit._testing import NotifierMock
from notifykit._typing import Event

VERSION = __version__

__all__ = (
"Notifier",
"NotifierT",
"VERSION",
"EventFilter",
"CommonFilter",
"Event",
"ObjectType",
"AccessType",
Expand Down
96 changes: 96 additions & 0 deletions notifykit/_filters.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
import re
from pathlib import Path
from typing import Sequence, Union

from notifykit._notifykit_lib import RenameEvent

from notifykit._typing import Event


class EventFilter:
"""
A base class to define rules to filter filesystem events
TODO: Move filtering to the Rust library
"""

__slots__ = "_ignore_dirs", "_ignore_object_regexes", "_ignore_paths"

ignore_dirs: Sequence[str] = ()
"""Full names of directories to ignore like `.git`."""

ignore_object_patterns: Sequence[str] = ()
"""
Patterns of files or directories to ignore, these are compiled into regexes.
"""

ignore_paths: Sequence[Union[str, Path]] = ()
"""
Full paths to ignore, e.g. `/home/users/.cache` or `C:\\Users\\user\\.cache`.
"""

def __init__(self) -> None:
self._ignore_dirs = set(self.ignore_dirs)
self._ignore_object_regexes = tuple(re.compile(r) for r in self.ignore_object_patterns)
self._ignore_paths = tuple(map(str, self.ignore_paths))

def __call__(self, event: Event) -> bool:
"""
Check if event should be filtered (True) or kept in place (False)
"""
if isinstance(event, RenameEvent):
return self._should_be_filtered(Path(event.old_path)) and self._should_be_filtered(Path(event.new_path))

return self._should_be_filtered(Path(event.path))

def _should_be_filtered(self, path: Path) -> bool:
if any(p in self._ignore_dirs for p in path.parts):
return True

object_name = path.name

if any(r.search(object_name) for r in self._ignore_object_regexes):
return True

if self._ignore_paths:
for ignore_path in self._ignore_paths:
if path.is_relative_to(ignore_path):
return True

return False

def __repr__(self) -> str:
args = ", ".join(f"{k}={getattr(self, k, None)!r}" for k in self.__slots__)

return f"{self.__class__.__name__}({args})"


class CommonFilter(EventFilter):
"""
Filter commonly ignored files and directories
"""

ignore_dirs: Sequence[str] = (
"__pycache__",
".git",
".hg",
".svn",
".tox",
".venv",
"site-packages",
".idea",
"node_modules",
".mypy_cache",
".ruff_cache",
".pytest_cache",
".hypothesis",
)

ignore_object_patterns: Sequence[str] = (
r"\.py[cod]$",
r"\.___jb_...___$",
r"\.sw.$",
"~$",
r"^\.\#",
r"^\.DS_Store$",
r"^flycheck_",
)
33 changes: 24 additions & 9 deletions notifykit/_notifier.py
Original file line number Diff line number Diff line change
@@ -1,18 +1,15 @@
from os import PathLike
import anyio
import logging
from typing import Sequence, Protocol, Optional, List
from notifykit._notifykit_lib import (
WatcherWrapper,
AccessEvent,
CreateEvent,
ModifyDataEvent,
ModifyMetadataEvent,
ModifyOtherEvent,
DeleteEvent,
RenameEvent,
)

Event = AccessEvent | CreateEvent | ModifyDataEvent | ModifyMetadataEvent | ModifyOtherEvent | DeleteEvent | RenameEvent
from notifykit._typing import Event
from notifykit._filters import EventFilter

logger = logging.getLogger(__name__)


class AnyEvent(Protocol):
Expand Down Expand Up @@ -51,13 +48,19 @@ class Notifier:
"""

def __init__(
self, debounce_ms: int = 200, tick_ms: int = 50, debug: bool = False, stop_event: Optional[AnyEvent] = None
self,
debounce_ms: int = 200,
tick_ms: int = 50,
debug: bool = False,
filter: Optional[EventFilter] = None,
stop_event: Optional[AnyEvent] = None,
) -> None:
self._debounce_ms = debounce_ms
self._tick_ms = tick_ms
self._debug = debug

self._watcher = WatcherWrapper(debounce_ms, debug)
self._filter = filter
self._stop_event = stop_event if stop_event else anyio.Event()

def watch(
Expand Down Expand Up @@ -89,6 +92,12 @@ def __next__(self) -> List[Event]:
if events is None:
raise StopIteration

if self._filter:
if self._debug:
logger.debug(f"events before filtering: {events}")

events = [event for event in events if not self._filter(event)]

return events

async def __anext__(self) -> List[Event]:
Expand All @@ -107,4 +116,10 @@ async def __anext__(self) -> List[Event]:
if events is None:
raise StopAsyncIteration

if self._filter:
if self._debug:
logger.debug(f"events before filtering: {events}")

events = [event for event in events if not self._filter(event)]

return events
2 changes: 1 addition & 1 deletion notifykit/_testing.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from os import PathLike
from typing import Sequence, List, Optional

from notifykit import Event
from notifykit._typing import Event
from notifykit._notifier import AnyEvent


Expand Down
11 changes: 11 additions & 0 deletions notifykit/_typing.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
from notifykit._notifykit_lib import (
AccessEvent,
CreateEvent,
ModifyDataEvent,
ModifyMetadataEvent,
ModifyOtherEvent,
DeleteEvent,
RenameEvent,
)

Event = AccessEvent | CreateEvent | ModifyDataEvent | ModifyMetadataEvent | ModifyOtherEvent | DeleteEvent | RenameEvent
Loading

0 comments on commit 8d2906f

Please sign in to comment.