Skip to content

Commit

Permalink
feat!: more typing clean-up + enforce keyword-arguments (gorakhargosh…
Browse files Browse the repository at this point in the history
  • Loading branch information
BoboTiG authored Aug 11, 2024
1 parent 3b00a42 commit b87e645
Show file tree
Hide file tree
Showing 39 changed files with 313 additions and 291 deletions.
5 changes: 5 additions & 0 deletions changelog.rst
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,14 @@ Changelog

- Drop support for Python 3.8 (`#1055 <https://github.com/gorakhargosh/watchdog/pull/1055>`__)
- [core] Enable ``disallow_untyped_calls`` Mypy rule (`#1055 <https://github.com/gorakhargosh/watchdog/pull/1055>`__)
- [core] Enforced usage of proper keyword-arguments (`#1057 <https://github.com/gorakhargosh/watchdog/pull/1057>`__)
- [core] Deleted the ``BaseObserverSubclassCallable`` class. Use ``type[BaseObserver]`` directly (`#1055 <https://github.com/gorakhargosh/watchdog/pull/1055>`__)
- [inotify] Renamed the ``inotify_event_struct`` class to ``InotifyEventStruct`` (`#1055 <https://github.com/gorakhargosh/watchdog/pull/1055>`__)
- [inotify] Renamed the ``UnsupportedLibc`` exception to ``UnsupportedLibcError`` (`#1057 <https://github.com/gorakhargosh/watchdog/pull/1057>`__)
- [watchmedo] Renamed the ``LogLevelException`` exception to ``LogLevelError`` (`#1057 <https://github.com/gorakhargosh/watchdog/pull/1057>`__)
- [watchmedo] Renamed the ``WatchdogShutdown`` exception to ``WatchdogShutdownError`` (`#1057 <https://github.com/gorakhargosh/watchdog/pull/1057>`__)
- [windows] Renamed the ``FILE_NOTIFY_INFORMATION`` class to ``FileNotifyInformation`` (`#1055 <https://github.com/gorakhargosh/watchdog/pull/1055>`__)
- [windows] Removed the unused ``WATCHDOG_TRAVERSE_MOVED_DIR_DELAY`` constant (`#1057 <https://github.com/gorakhargosh/watchdog/pull/1057>`__)
- Thanks to our beloved contributors: @BoboTiG

4.0.2
Expand Down
16 changes: 4 additions & 12 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -38,28 +38,20 @@ extend-select = ["ALL"]
ignore = [
"ARG",
"ANN", # TODO
"ANN002",
"ANN003",
"ANN401",
"B006",
"B023", # TODO
"B028",
"BLE001",
"C90",
"COM",
"COM812",
"D",
"EM",
"ERA",
"FBT",
"EM101",
"EM102",
"FIX",
"ISC001",
"N818",
"PERF203", # TODO
"PERF203",
"PL",
"PTH", # TODO?
"S",
"TD",
"TRY003",
]
fixable = ["ALL"]

Expand Down
8 changes: 4 additions & 4 deletions src/watchdog/events.py
Original file line number Diff line number Diff line change
Expand Up @@ -288,14 +288,14 @@ def on_opened(self, event: FileSystemEvent) -> None:


class PatternMatchingEventHandler(FileSystemEventHandler):
"""
Matches given patterns with file paths associated with occurring events.
"""Matches given patterns with file paths associated with occurring events.
Uses pathlib's `PurePath.match()` method. `patterns` and `ignore_patterns`
are expected to be a list of strings.
"""

def __init__(
self,
*,
patterns=None,
ignore_patterns=None,
ignore_directories=False,
Expand Down Expand Up @@ -364,13 +364,13 @@ def dispatch(self, event: FileSystemEvent) -> None:


class RegexMatchingEventHandler(FileSystemEventHandler):
"""
Matches given regexes with file paths associated with occurring events.
"""Matches given regexes with file paths associated with occurring events.
Uses the `re` module.
"""

def __init__(
self,
*,
regexes=None,
ignore_regexes=None,
ignore_directories=False,
Expand Down
10 changes: 5 additions & 5 deletions src/watchdog/observers/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,15 +55,15 @@
import warnings
from typing import TYPE_CHECKING

from watchdog.utils import UnsupportedLibc, platform
from watchdog.utils import UnsupportedLibcError, platform

if TYPE_CHECKING:
from watchdog.observers.api import BaseObserver


def _get_observer_cls() -> type[BaseObserver]:
if platform.is_linux():
with contextlib.suppress(UnsupportedLibc):
with contextlib.suppress(UnsupportedLibcError):
from watchdog.observers.inotify import InotifyObserver

return InotifyObserver
Expand All @@ -74,17 +74,17 @@ def _get_observer_cls() -> type[BaseObserver]:
try:
from watchdog.observers.kqueue import KqueueObserver
except Exception:
warnings.warn("Failed to import fsevents and kqueue. Fall back to polling.")
warnings.warn("Failed to import fsevents and kqueue. Fall back to polling.", stacklevel=1)
else:
warnings.warn("Failed to import fsevents. Fall back to kqueue")
warnings.warn("Failed to import fsevents. Fall back to kqueue", stacklevel=1)
return KqueueObserver
else:
return FSEventsObserver
elif platform.is_windows():
try:
from watchdog.observers.read_directory_changes import WindowsApiObserver
except Exception:
warnings.warn("Failed to import `read_directory_changes`. Fall back to polling.")
warnings.warn("Failed to import `read_directory_changes`. Fall back to polling.", stacklevel=1)
else:
return WindowsApiObserver
elif platform.is_bsd():
Expand Down
8 changes: 4 additions & 4 deletions src/watchdog/observers/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ class ObservedWatch:
Optional collection of :class:`watchdog.events.FileSystemEvent` to watch
"""

def __init__(self, path, recursive, event_filter=None):
def __init__(self, path, *, recursive, event_filter=None):
self._path = str(path) if isinstance(path, Path) else path
self._is_recursive = recursive
self._event_filter = frozenset(event_filter) if event_filter is not None else None
Expand Down Expand Up @@ -217,7 +217,7 @@ def run(self):
class BaseObserver(EventDispatcher):
"""Base observer."""

def __init__(self, emitter_class, timeout=DEFAULT_OBSERVER_TIMEOUT):
def __init__(self, emitter_class, *, timeout=DEFAULT_OBSERVER_TIMEOUT):
super().__init__(timeout)
self._emitter_class = emitter_class
self._lock = threading.RLock()
Expand Down Expand Up @@ -268,7 +268,7 @@ def start(self):
raise
super().start()

def schedule(self, event_handler, path, recursive=False, event_filter=None):
def schedule(self, event_handler, path, *, recursive=False, event_filter=None):
"""Schedules watching a path and calls appropriate methods specified
in the given event handler in response to file system events.
Expand Down Expand Up @@ -296,7 +296,7 @@ def schedule(self, event_handler, path, recursive=False, event_filter=None):
a watch.
"""
with self._lock:
watch = ObservedWatch(path, recursive, event_filter)
watch = ObservedWatch(path, recursive=recursive, event_filter=event_filter)
self._add_handler_for_watch(event_handler, watch)

# If we don't have an emitter for this watch already, create it.
Expand Down
5 changes: 3 additions & 2 deletions src/watchdog/observers/fsevents.py
Original file line number Diff line number Diff line change
Expand Up @@ -75,11 +75,12 @@ def __init__(
self,
event_queue,
watch,
*,
timeout=DEFAULT_EMITTER_TIMEOUT,
event_filter=None,
suppress_history=False,
):
super().__init__(event_queue, watch, timeout, event_filter)
super().__init__(event_queue, watch, timeout=timeout, event_filter=event_filter)
self._fs_view = set()
self.suppress_history = suppress_history
self._start_time = 0.0
Expand Down Expand Up @@ -324,7 +325,7 @@ class FSEventsObserver(BaseObserver):
def __init__(self, timeout=DEFAULT_OBSERVER_TIMEOUT):
super().__init__(FSEventsEmitter, timeout=timeout)

def schedule(self, event_handler, path, recursive=False, event_filter=None):
def schedule(self, event_handler, path, *, recursive=False, event_filter=None):
# Fix for issue #26: Trace/BPT error when given a unicode path
# string. https://github.com/gorakhargosh/watchdog/issues#issue/26
if isinstance(path, str):
Expand Down
12 changes: 7 additions & 5 deletions src/watchdog/observers/fsevents2.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@
logger = logging.getLogger(__name__)

message = "watchdog.observers.fsevents2 is deprecated and will be removed in a future release."
warnings.warn(message, category=DeprecationWarning)
warnings.warn(message, category=DeprecationWarning, stacklevel=1)
logger.warning(message)


Expand Down Expand Up @@ -99,7 +99,8 @@ def __init__(self, path):
kFSEventStreamCreateFlagNoDefer | kFSEventStreamCreateFlagFileEvents,
)
if self._stream_ref is None:
raise OSError("FSEvents. Could not create stream.")
error = "FSEvents. Could not create stream."
raise OSError(error)

def run(self):
pool = AppKit.NSAutoreleasePool.alloc().init()
Expand All @@ -108,7 +109,8 @@ def run(self):
if not FSEventStreamStart(self._stream_ref):
FSEventStreamInvalidate(self._stream_ref)
FSEventStreamRelease(self._stream_ref)
raise OSError("FSEvents. Could not start stream.")
error = "FSEvents. Could not start stream."
raise OSError(error)

CFRunLoopRun()
FSEventStreamStop(self._stream_ref)
Expand Down Expand Up @@ -178,8 +180,8 @@ def __repr__(self):
class FSEventsEmitter(EventEmitter):
"""FSEvents based event emitter. Handles conversion of native events."""

def __init__(self, event_queue, watch, timeout=DEFAULT_EMITTER_TIMEOUT, event_filter=None):
super().__init__(event_queue, watch, timeout, event_filter)
def __init__(self, event_queue, watch, *, timeout=DEFAULT_EMITTER_TIMEOUT, event_filter=None):
super().__init__(event_queue, watch, timeout=timeout, event_filter=event_filter)
self._fsevents = FSEventsQueue(watch.path)
self._fsevents.start()

Expand Down
19 changes: 8 additions & 11 deletions src/watchdog/observers/inotify.py
Original file line number Diff line number Diff line change
Expand Up @@ -111,22 +111,22 @@ class InotifyEmitter(EventEmitter):
Iterable[:class:`watchdog.events.FileSystemEvent`] | None
"""

def __init__(self, event_queue, watch, timeout=DEFAULT_EMITTER_TIMEOUT, event_filter=None):
super().__init__(event_queue, watch, timeout, event_filter)
def __init__(self, event_queue, watch, *, timeout=DEFAULT_EMITTER_TIMEOUT, event_filter=None):
super().__init__(event_queue, watch, timeout=timeout, event_filter=event_filter)
self._lock = threading.Lock()
self._inotify = None

def on_thread_start(self):
path = os.fsencode(self.watch.path)
event_mask = self.get_event_mask_from_filter()
self._inotify = InotifyBuffer(path, self.watch.is_recursive, event_mask)
self._inotify = InotifyBuffer(path, recursive=self.watch.is_recursive, event_mask=event_mask)

def on_thread_stop(self):
if self._inotify:
self._inotify.close()
self._inotify = None

def queue_events(self, timeout, full_events=False):
def queue_events(self, timeout, *, full_events=False):
# If "full_events" is true, then the method will report unmatched move events as separate events
# This behavior is by default only called by a InotifyFullEmitter
if self._inotify is None:
Expand Down Expand Up @@ -188,9 +188,6 @@ def queue_events(self, timeout, full_events=False):
elif event.is_open and not event.is_directory:
cls = FileOpenedEvent
self.queue_event(cls(src_path))
# elif event.is_close_nowrite and not event.is_directory:
# cls = FileClosedEvent
# self.queue_event(cls(src_path))
elif event.is_delete_self and src_path == self.watch.path:
cls = DirDeletedEvent if event.is_directory else FileDeletedEvent
self.queue_event(cls(src_path))
Expand Down Expand Up @@ -254,10 +251,10 @@ class InotifyFullEmitter(InotifyEmitter):
"""

def __init__(self, event_queue, watch, timeout=DEFAULT_EMITTER_TIMEOUT, event_filter=None):
super().__init__(event_queue, watch, timeout, event_filter)
def __init__(self, event_queue, watch, *, timeout=DEFAULT_EMITTER_TIMEOUT, event_filter=None):
super().__init__(event_queue, watch, timeout=timeout, event_filter=event_filter)

def queue_events(self, timeout, events=True):
def queue_events(self, timeout, *, events=True):
InotifyEmitter.queue_events(self, timeout, full_events=events)


Expand All @@ -266,6 +263,6 @@ class InotifyObserver(BaseObserver):
calls to event handlers.
"""

def __init__(self, timeout=DEFAULT_OBSERVER_TIMEOUT, generate_full_events=False):
def __init__(self, *, timeout=DEFAULT_OBSERVER_TIMEOUT, generate_full_events=False):
cls = InotifyFullEmitter if generate_full_events else InotifyEmitter
super().__init__(cls, timeout=timeout)
6 changes: 3 additions & 3 deletions src/watchdog/observers/inotify_buffer.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,10 @@ class InotifyBuffer(BaseThread):

delay = 0.5

def __init__(self, path, recursive=False, event_mask=None):
def __init__(self, path, *, recursive=False, event_mask=None):
super().__init__()
self._queue = DelayedQueue[InotifyEvent](self.delay)
self._inotify = Inotify(path, recursive, event_mask)
self._inotify = Inotify(path, recursive=recursive, event_mask=event_mask)
self.start()

def read_event(self):
Expand Down Expand Up @@ -97,7 +97,7 @@ def run(self):

# Only add delay for unmatched move_from events
delay = not isinstance(inotify_event, tuple) and inotify_event.is_moved_from
self._queue.put(inotify_event, delay)
self._queue.put(inotify_event, delay=delay)

if (
not isinstance(inotify_event, tuple)
Expand Down
7 changes: 4 additions & 3 deletions src/watchdog/observers/inotify_c.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,13 @@
from ctypes import c_char_p, c_int, c_uint32
from functools import reduce

from watchdog.utils import UnsupportedLibc
from watchdog.utils import UnsupportedLibcError

libc = ctypes.CDLL(None)

if not hasattr(libc, "inotify_init") or not hasattr(libc, "inotify_add_watch") or not hasattr(libc, "inotify_rm_watch"):
raise UnsupportedLibc(f"Unsupported libc version found: {libc._name}") # noqa:SLF001
error = f"Unsupported libc version found: {libc._name}" # noqa:SLF001
raise UnsupportedLibcError(error)

inotify_add_watch = ctypes.CFUNCTYPE(c_int, c_int, c_char_p, c_uint32, use_errno=True)(("inotify_add_watch", libc))

Expand Down Expand Up @@ -151,7 +152,7 @@ class Inotify:
``True`` if subdirectories should be monitored; ``False`` otherwise.
"""

def __init__(self, path, recursive=False, event_mask=None):
def __init__(self, path, *, recursive=False, event_mask=None):
# The file descriptor associated with the inotify instance.
inotify_fd = inotify_init()
if inotify_fd == -1:
Expand Down
11 changes: 2 additions & 9 deletions src/watchdog/observers/kqueue.py
Original file line number Diff line number Diff line change
Expand Up @@ -408,8 +408,8 @@ class KqueueEmitter(EventEmitter):
:param stat: stat function. See ``os.stat`` for details.
"""

def __init__(self, event_queue, watch, timeout=DEFAULT_EMITTER_TIMEOUT, event_filter=None, stat=os.stat):
super().__init__(event_queue, watch, timeout, event_filter)
def __init__(self, event_queue, watch, *, timeout=DEFAULT_EMITTER_TIMEOUT, event_filter=None, stat=os.stat):
super().__init__(event_queue, watch, timeout=timeout, event_filter=event_filter)

self._kq = select.kqueue()
self._lock = threading.RLock()
Expand Down Expand Up @@ -442,13 +442,6 @@ def _register_kevent(self, path, is_directory):
# and then quickly deleted before we could open
# a descriptor for it. Therefore, simply queue a sequence
# of created and deleted events for the path.
# path = absolute_path(path)
# if is_directory:
# self.queue_event(DirCreatedEvent(path))
# self.queue_event(DirDeletedEvent(path))
# else:
# self.queue_event(FileCreatedEvent(path))
# self.queue_event(FileDeletedEvent(path))

# TODO: We could simply ignore these files.
# Locked files cause the python process to die with
Expand Down
7 changes: 4 additions & 3 deletions src/watchdog/observers/polling.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,17 +60,18 @@ def __init__(
self,
event_queue,
watch,
*,
timeout=DEFAULT_EMITTER_TIMEOUT,
event_filter=None,
stat=os.stat,
listdir=os.scandir,
):
super().__init__(event_queue, watch, timeout, event_filter)
super().__init__(event_queue, watch, timeout=timeout, event_filter=event_filter)
self._snapshot: DirectorySnapshot = EmptyDirectorySnapshot()
self._lock = threading.Lock()
self._take_snapshot = lambda: DirectorySnapshot(
self.watch.path,
self.watch.is_recursive,
recursive=self.watch.is_recursive,
stat=stat,
listdir=listdir,
)
Expand Down Expand Up @@ -126,7 +127,7 @@ class PollingObserver(BaseObserver):
system changes.
"""

def __init__(self, timeout=DEFAULT_OBSERVER_TIMEOUT):
def __init__(self, *, timeout=DEFAULT_OBSERVER_TIMEOUT):
super().__init__(PollingEmitter, timeout=timeout)


Expand Down
7 changes: 2 additions & 5 deletions src/watchdog/observers/read_directory_changes.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,17 +35,14 @@
from watchdog.observers.api import DEFAULT_EMITTER_TIMEOUT, DEFAULT_OBSERVER_TIMEOUT, BaseObserver, EventEmitter
from watchdog.observers.winapi import close_directory_handle, get_directory_handle, read_events

# Obsolete constant, it's no more used since v4.0.0.
WATCHDOG_TRAVERSE_MOVED_DIR_DELAY = 1 # seconds


class WindowsApiEmitter(EventEmitter):
"""Windows API-based emitter that uses ReadDirectoryChangesW
to detect file system changes for a watch.
"""

def __init__(self, event_queue, watch, timeout=DEFAULT_EMITTER_TIMEOUT, event_filter=None):
super().__init__(event_queue, watch, timeout, event_filter)
def __init__(self, event_queue, watch, *, timeout=DEFAULT_EMITTER_TIMEOUT, event_filter=None):
super().__init__(event_queue, watch, timeout=timeout, event_filter=event_filter)
self._lock = threading.Lock()
self._whandle = None

Expand Down
Loading

0 comments on commit b87e645

Please sign in to comment.