pypi watchdog 7.0.0

2 hours ago

Breaking Changes

  • Drop support for Python 3.9 and 3.10.
  • [core] Changed pattern matching mechanism from using path.match() to path.full_match(). Users must update patterns to glob-like syntax: e.g., *.py to **/*.py. (#1101)
  • [core] Added FileAttribEvent and DirAttribEvent for file and directory metadata modifications (such as permissions or timestamps), separate from content modifications. These events no longer trigger on_modified(); implement on_attrib() to handle them. On Windows 10 (1709+) and Windows Server 2016+, ReadDirectoryChangesExW is used to differentiate attribute modifications, with fallback to ReadDirectoryChangesW on older versions or unsupported file shares. (#260)
  • [watchmedo] Changed the default --patterns value from "*" to "**", so the default matches absolute paths at any depth under the full_match() semantics introduced by #1101, and updated the CLI help text and docs to the **/ glob syntax. (#1212)

Other Changes

  • Add support for Python 3.15 (#1226)
  • [core] Add context manager support to Observer class. The observer can now be used with a with statement for automatic start/stop management. (#1090)
  • [core] Add support for the follow_symlink keyword argument to ObservedWatch. (#1086)
  • [core] Added the ignore_policy argument to PatternMatchingEventHandler and RegexMatchingEventHandler to configure multi-path event filtering (moves/renames). PatternMatchingEventHandler is now strict by default, aligning with RegexMatchingEventHandler. (#412, #423)
  • [core] Added the watchdog.utils.Seconds type alias and used it for the time-duration parameters (timeout, polling_interval, kill_after, ...) instead of documenting the unit in each docstring. Duration parameters previously hinted as int now accept float.
  • [core] Adjust Observer.schedule() path type annotation to reflect the pathlib.Path support. (#1096)
  • [core] Allow scheduling watches on individual files in Observer.schedule(). (#1034)
  • [core] Call task_done() for the stop sentinel in dispatch_events() to prevent join() from hanging. (#1159)
  • [core] case_sensitive=False matching now really ignores case on Python < 3.13. The PurePath.full_match() fallback compiled its regex without re.IGNORECASE, so only the patterns were lower-cased and a path such as FOO.PY never matched **/*.py. (#1263)
  • [core] dispatch_events() no longer re-adds a watch that unschedule() removed, which leaked one _handlers entry per watch that had an event in flight. (#1261)
  • [core] Do not retain an event handler when Observer.schedule() fails to create or start its emitter.
  • [core] EventEmitter and EventDispatcher reject a bool timeout. bool subclasses int, so timeout=True was stored and treated as 1.0 seconds. (#1287)
  • [core] Fixed generate_sub_moved_events() corrupting paths when the directory name appears multiple times in the path. (#1158)
  • [core] ObservedWatch equality now includes follow_symlink, so scheduling a path a second time with a different follow_symlink value no longer collapses onto the first watch and silently drops the request. (#1262)
  • [core] RegexMatchingEventHandler no longer matches its regexes against an empty destination path for events that are not moves. Since events became dataclasses, hasattr(event, "dest_path") is always true, so a regex matching the empty string (e.g. ^(?!.*\.pyc$).*$) let every event through, and such an ignore regex dropped every event. PatternMatchingEventHandler had the same check.
  • [core] remove_handler_for_watch() no longer re-adds a watch that unschedule() removed, which leaked one empty _handlers entry per call. (#1285)
  • [core] Add win_arm64 to the list of platform-specific wheels published to PyPI. (#1137)
  • [core] Add manylinux_2_17 to the list of platform-specific wheels published to PyPI. (#1125)
  • [inotify] Add support for the follow_symlink keyword argument. (#1086)
  • [inotify] Do not suppress registration errors for the requested root path. A missing root now raises FileNotFoundError from observer.start(), or from observer.schedule() when already running. Recursive descendant failures remain tolerated. (#1157)
  • [inotify] Keep watching newly created subdirectories when recursive watches filter out creation events. (#990)
  • [inotify] Reduce number of created inotify instance. (#1099)
  • [inotify] Snapshot the watch group before the blocking read in InotifyEmitter.queue_events() so stopping the emitter cannot raise AttributeError on a cleared self._inotify.
  • [fsevents] Added the latency keyword argument to FSEventsEmitter and FSEventsObserver (defaulting to watchdog.observers.fsevents.DEFAULT_FSEVENTS_LATENCY), exposing the FSEvents stream latency previously hard-coded at 10 ms. Raising it coalesces duplicate events. (#835)
  • [fsevents] Add support for the follow_symlink keyword argument. (#1086)
  • [fsevents] Support free-threading and give each emitter its own stream and run loop to prevent shutdown leaks. (#1140)
  • [fsevents] Use multi-phase initialization (PEP 489). (#1108)
  • [windows] A rename whose RENAMED_OLD and RENAMED_NEW records arrive in different get_events() batches no longer emits a move event with an empty src_path. (#1286)
  • [windows] Changing only the case of a name no longer emits a deletion event on top of the move event. (#752)
  • [windows] Event file names are decoded as UTF-16LE, so a name starting with U+FEFF or U+FFFE no longer reports the wrong path. (#1228)
  • [windows] Fixed an unsynchronized access to WindowsApiEmitter._reader: queue_events() now
    reads it under the same lock used by on_thread_stop(). (#1170)
  • [windows] Use double-buffering for read_directory_changes to avoid missed events under high load. (#1152)
  • [polling] A transient OSError while polling, such as a network share that is briefly unreachable, no longer emits a DirDeletedEvent for the watched directory and stops the emitter. The error is logged and the next poll tries again, so changes made in the meantime are still reported. Only FileNotFoundError and NotADirectoryError are still treated as a deletion. (#1065)
  • [polling] Detect permission, ownership, and file attribute modifications in DirectorySnapshotDiff and emit FileAttribEvent and DirAttribEvent in PollingObserver. (#881)
  • [bsd] Do not let a PermissionError raised while registering a kevent kill the emitter thread. (#1115)
  • [freebsd] Supports inotify on FreeBSD 15+. (#1147)
  • [utils] Deduplicate consecutive None items in SkipRepeatsQueue. (#1348)
  • [utils] Deprecated the debounce_interval_seconds argument and attribute of EventDebouncer and AutoRestartTrick in favour of debounce_interval, and the DelayedQueue.delay_sec attribute in favour of delay. The old names still work but emit a DeprecationWarning.
  • [utils] DirectorySnapshotDiff(ignore_device=True) now detects moved files and directories when the device ID changes between snapshots.
  • [utils] Fix EventDebouncer starvation on high frequency events and ensure pending events are delivered upon stop. (#998)
  • [utils] Fixed DirectorySnapshotDiff reporting a newly created symlink as a move of its target (and a removed symlink as a move onto its target), so PollingObserver now emits a create/delete event to match the native observers. (#1176)
  • [utils] Fixed repr(EmptyDirectorySnapshot), before that it was throwing an AttributeError: 'EmptyDirectorySnapshot' object has no attribute '_stat_info'.
  • [utils] Implemented len(DirectorySnapshotDiff) to return the total number of changes.
  • [utils] Make SkipRepeatsQueue.put() and put_nowait() reject duplicate items after shutdown() on Python 3.13+.
  • [utils] Serialize SkipRepeatsQueue producers to prevent consecutive duplicates, respecting blocking and timeout limits while acquiring the producer lock. (#1338)
  • [utils] Use a monotonic clock for DelayedQueue delays so system clock adjustments do not stall or prematurely release events. (#1296)
  • [tricks] Allow configuring shell_executable and cwd for ShellCommandTrick and watchmedo shell-command. (#921)
  • [watchmedo] Support running watchmedo via python -m watchdog. (#1162)
  • [watchmedo] The AutoRestartTrick stop signal is now actually delivered on Windows. The subprocess is started in its own process group and signalled with CTRL_BREAK_EVENT, so it can run cleanup handlers and its descendants are no longer orphaned on every restart. (#1221)
  • [watchmedo] AutoRestartTrick's --signal SIGBREAK now routes through CTRL_BREAK_EVENT on Windows too, instead of falling back to the hard kill that skips the child's cleanup handlers. (#1221)
  • [watchmedo] Show file system events by default in watchmedo log. (#913)
  • [tests] Remove the eventlet tests and test dependency.
  • [docs] Document that BaseThread always runs as a daemon thread, instead of inheriting daemon from the creating thread as the inherited threading.Thread documentation states. (#1117)
  • [docs] FileSystemEvent no longer claims to be immutable; its fields stay writable and the hash is derived from them, so mutating an event held in a dict or set strands it there. (#1240)
  • [docs] Note that FileClosedEvent, FileClosedNoWriteEvent and FileOpenedEvent, and their on_closed(), on_closed_no_write() and on_opened() handlers, are emitted only by InotifyObserver on Linux. (#1235)

💟 Thanks to our beloved contributors: @BoboTiG, @Corentin-pro, @Liam-DeVoe, @kirkhansen, @JoachimCoenen, @HaoZeke, @LincolnPuzey, @adicohen1005, @zastrowm, @ngoldbaum, @QWERTIOX, @cclauss, @blitztide, @nascheme, @Prosperibe12, @bysiber, @abra-code, @toroleapinc, @maxtaran2010, @gorakhargosh, @Sanjays2402, @prateek-dagar, @BortnikMaxim, @MohammedAlkindi, @Skeletor-Pirate, @btschwertfeger, @zain-asif-dev, @subikshajegadish, @FanWu-ai, @ryanchou1994, @Rosuav, @TobiasRzepka, @GreatBahram, @arindamsikder, @apoorvdarshan, @kokotatan, @Om-singhaI, @amprew, @Pitchfork-and-Torch, @Dev-iL, @CLCK0622, @Enesuygurs, @galafis, @vaibhav8a, @ivg, @newchannelid432-code, @Aschenbath, @Joelbit-io, @Alex0AI

Don't miss a new watchdog release

NewReleases is sending notifications on new releases.