Python library and shell utilities to monitor filesystem events.
This report summarizes scanner evidence for gorakhargosh/watchdog, a Python project with 7,419 GitHub stars. SynthScan examined 16,018 lines of code across 81 source files, recording 222 pattern matches distributed across 11 syntactic categories. The legacy adjusted pattern signal is 16.2 (Elevated pattern signal); it is not an authorship estimate.
Use the findings to ask whether maintainers can explain intent, verify dependencies, simplify unnecessary scaffolding, and focus review effort. Risk Payload v1 adds ownership, supply-chain, over-engineering, and review-effort vectors when available. These are prompts for human verification, not probabilities, security findings, or measured maintenance costs.
Use each vector to guide manual verification of the underlying findings.
Can maintainers explain intent, assumptions, and trade-offs? 147 finding(s).
Are imports declared and package/API names verified? 49 finding(s).
Are wrappers, guards, and repeated structures necessary? 128 finding(s).
Which changes need focused human review? 222 finding(s).
This chart tracks the legacy adjusted pattern score across scan runs. Changes can reflect source changes, scan coverage, or detector/scoring versions; they do not measure AI adoption or prove that a team refactored code. Compare runs only when their scoring versions are compatible. The dashed line tracks the raw pattern-hit count.
Groups findings by configured severity weight. Severity is not a confidence estimate, security rating, or proof that a package is missing. Verify imports against the project's manifests and inspect each finding in its local design context.
This horizontal bar chart shows where configured pattern scores accumulate across directories. Treat the bars as locations to inspect, not as evidence of authorship or code quality. Use the file-level findings to decide whether a directory needs a focused ownership, dependency, complexity, or review check.
The scanner identified 222 distinct pattern matches across 11 syntactic categories. Each entry below represents a discrete location in the source code where the engine recorded a detector match to inspect in context. Expand any category row to inspect the individual file paths, line numbers, code snippets, and the lexical context (CODE, COMMENT, or STRING) in which each match was detected.
Reading the findings table: The Severity column indicates the configured finding weight (CRITICAL / HIGH / MEDIUM / LOW). The Context column identifies whether the match occurred inside executable code, an inline comment, or a string literal — legacy comment-context matches receive a ×1.5 score weight. The ⚡ bolt icon marks clustered matches: three or more patterns within a 10-line window, each receiving an additional ×1.5 density multiplier under the legacy scoring model.
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW | tests/test_inotify_c.py | 44 | def test_late_double_deletion(helper: Helper, p: P, event_queue: TestEventQueue, start_watching: StartWatching) -> None: | CODE |
| LOW | tests/test_kqueue.py | 19 | def test_register_kevent_ignores_permission_errors(tmpdir, err): | CODE |
| LOW | tests/test_logging_event_handler.py | 63 | def test_logging_event_handler_dispatch(): | CODE |
| LOW | tests/test_isolated.py | 13 | def test_observer_stops_in_eventlet(): | CODE |
| LOW | tests/test_isolated.py | 21 | def test_eventlet_skip_repeat_queue(): | CODE |
| LOW | tests/test_snapshot_diff.py | 44 | def test_move_to_with_context_manager(p): | CODE |
| LOW | tests/test_snapshot_diff.py | 93 | def test_dir_modify_on_create(p): | CODE |
| LOW | tests/test_snapshot_diff.py | 112 | def test_detect_modify_for_moved_files(p): | CODE |
| LOW | tests/test_snapshot_diff.py | 123 | def test_replace_dir_with_file(p): | CODE |
| LOW⚡ | tests/test_observer.py | 35 | def test_schedule_should_start_emitter_if_running(observer): | CODE |
| LOW⚡ | tests/test_observer.py | 42 | def test_schedule_should_not_start_emitter_if_not_running(observer): | CODE |
| LOW⚡ | tests/test_observer.py | 48 | def test_start_should_start_emitter(observer): | CODE |
| LOW⚡ | tests/test_observer.py | 55 | def test_stop_should_stop_emitter(observer): | CODE |
| LOW | tests/test_observer.py | 88 | def test_schedule_after_unschedule_all(observer): | CODE |
| LOW | tests/test_observer.py | 110 | def test_start_failure_should_not_prevent_further_try(observer): | CODE |
| LOW | tests/test_observer.py | 135 | def test_schedule_failure_should_not_prevent_future_schedules(observer): | CODE |
| LOW | tests/test_observer.py | 152 | def test_context_manager_starts_observer(): | CODE |
| LOW | tests/test_observer.py | 168 | def test_context_manager_with_already_started_observer(): | CODE |
| LOW | tests/test_observer.py | 186 | def test_context_manager_stops_on_exit(): | CODE |
| LOW | tests/test_observer.py | 202 | def test_context_manager_handles_exceptions(): | CODE |
| LOW | tests/test_observer.py | 222 | def test_context_manager_with_scheduled_watches(): | CODE |
| LOW | tests/test_fsevents.py | 78 | def test_coalesced_event_check(event, expectation): | CODE |
| LOW | tests/test_fsevents.py | 82 | def test_native_event_owns_path() -> None: | CODE |
| LOW | tests/test_fsevents.py | 99 | def test_native_event_cycles_are_collected() -> None: | CODE |
| LOW | tests/test_fsevents.py | 111 | def test_native_event_allocations_are_released() -> None: | CODE |
| LOW | tests/test_fsevents.py | 292 | def test_stopping_unstarted_observer_releases_emitter(p: P) -> None: | CODE |
| LOW | tests/test_fsevents.py | 302 | def test_stream_stop_racing_run(p: P) -> None: | CODE |
| LOW | tests/test_fsevents.py | 313 | def test_two_streams_same_path(p: P) -> None: | CODE |
| LOW | tests/test_fsevents.py | 326 | def test_stream_concurrent_run_race(p: P) -> None: | CODE |
| LOW | tests/test_fsevents.py | 367 | def test_stream_concurrent_stop_calls(p: P) -> None: | CODE |
| LOW | tests/test_fsevents.py | 387 | def test_stream_stop_from_inside_callback(p: P) -> None: | CODE |
| LOW | tests/test_fsevents.py | 411 | def test_stream_callback_failure_terminates_stream(p: P) -> None: | CODE |
| LOW | tests/test_fsevents.py | 447 | def test_observer_stop_racing_start(p: P, start_watching: StartWatching, expect_event: ExpectEvent) -> None: | CODE |
| LOW | tests/test_fsevents.py | 467 | def test_rapid_observer_start_stop(p: P) -> None: | CODE |
| LOW | tests/test_fsevents.py | 477 | def test_module_keeps_gil_disabled() -> None: | CODE |
| LOW | tests/test_fsevents.py | 598 | def test_unschedule_removed_folder(observer: BaseObserver, p: P) -> None: | CODE |
| LOW | tests/test_fsevents.py | 620 | def test_converting_cfstring_to_pyunicode(p: P, start_watching: StartWatching, events_checker: EventsChecker) -> None: | CODE |
| LOW | tests/test_fsevents.py | 636 | def test_recursive_check_accepts_relative_paths(p: P) -> None: | CODE |
| LOW⚡ | tests/test_fsevents.py | 131 | def test_native_event_requires_arguments() -> None: | STRING |
| LOW⚡ | tests/test_fsevents.py | 136 | def test_native_event_unsigned_id() -> None: | STRING |
| LOW⚡ | tests/test_fsevents.py | 141 | def test_stream_does_not_retain_paths_list(p: P) -> None: | STRING |
| LOW | tests/test_fsevents.py | 184 | def test_observer_propagates_stream_startup_failure(p: P, *, already_running: bool) -> None: | STRING |
| LOW | tests/test_fsevents.py | 207 | def test_stream_stop_before_run(p: P) -> None: | STRING |
| LOW | tests/test_fsevents.py | 228 | def test_stream_stop_after_start(p: P) -> None: | STRING |
| LOW | tests/test_fsevents.py | 243 | def test_stream_can_retry_after_startup_callback_failure(p: P) -> None: | STRING |
| LOW | tests/test_fsevents.py | 257 | def test_emitter_start_waits_for_stream(p: P) -> None: | STRING |
| LOW | tests/test_observers_winapi.py | 142 | def test_drop_case_only_rename_deletions(): | STRING |
| LOW | tests/test_observers_winapi.py | 174 | def test_drop_case_only_rename_deletions_keeps_real_deletions(events): | CODE |
| LOW | tests/test_observers_winapi.py | 189 | def test_parse_event_buffer_reads_the_whole_file_name(file_name): | CODE |
| LOW | tests/utils.py | 217 | def _check_events_without_order(self, found_events: list[FileSystemEvent]) -> None: | CODE |
| LOW | tests/test_0_watchmedo.py | 102 | def test_auto_restart_stop_signal_is_delivered(tmpdir, capfd): | CODE |
| LOW | tests/test_0_watchmedo.py | 132 | def test_shell_command_wait_for_completion(tmpdir, capfd): | CODE |
| LOW | tests/test_0_watchmedo.py | 144 | def test_shell_command_subprocess_termination_nowait(tmpdir): | CODE |
| LOW | tests/test_0_watchmedo.py | 155 | def test_shell_command_subprocess_termination_not_happening_on_file_opened_event( | CODE |
| LOW | tests/test_0_watchmedo.py | 169 | def test_auto_restart_not_happening_on_file_opened_event(tmpdir, capfd): | CODE |
| LOW | tests/test_0_watchmedo.py | 185 | def test_auto_restart_on_file_change(tmpdir, capfd): | CODE |
| LOW | tests/test_0_watchmedo.py | 204 | def test_auto_restart_on_file_change_debounce(tmpdir, capfd): | CODE |
| LOW | tests/test_0_watchmedo.py | 251 | def test_auto_restart_subprocess_termination(tmpdir, capfd, restart_on_command_exit): | CODE |
| LOW | tests/test_0_watchmedo.py | 270 | def test_auto_restart_arg_parsing_basic(): | CODE |
| LOW | tests/test_0_watchmedo.py | 279 | def test_auto_restart_arg_parsing(): | CODE |
| 42 more matches not shown… | ||||
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW | tests/test_inotify_c.py | 1 | CODE | |
| LOW | tests/conftest.py | 1 | CODE | |
| LOW | tests/test_pattern_matching_event_handler.py | 1 | CODE | |
| LOW | tests/test_kqueue.py | 1 | CODE | |
| LOW | tests/test_logging_event_handler.py | 1 | CODE | |
| LOW | tests/test_snapshot_diff.py | 1 | CODE | |
| LOW | tests/test_delayed_queue.py | 1 | CODE | |
| LOW | tests/test_observer.py | 1 | CODE | |
| LOW | tests/test_fsevents.py | 1 | CODE | |
| LOW | tests/shell.py | 8 | CODE | |
| LOW | tests/test_patterns.py | 1 | CODE | |
| LOW | tests/test_observers_winapi.py | 1 | CODE | |
| LOW | tests/utils.py | 1 | CODE | |
| LOW | tests/test_0_watchmedo.py | 1 | CODE | |
| LOW | tests/test_skip_repeats_queue.py | 1 | CODE | |
| LOW | tests/test_observers_polling.py | 1 | CODE | |
| LOW | tests/test_observers_api.py | 1 | CODE | |
| LOW | tests/test_events.py | 1 | CODE | |
| LOW | tests/test_regex_matching_event_handler.py | 1 | CODE | |
| LOW | tests/test_examples.py | 1 | CODE | |
| LOW | tests/test_inotify_watch_group.py | 1 | CODE | |
| LOW | tests/test_emitter.py | 1 | CODE | |
| LOW | docs/source/examples/debounced.py | 1 | CODE | |
| LOW | docs/source/examples/simple.py | 1 | CODE | |
| LOW | docs/source/examples/file_organizer.py | 14 | CODE | |
| LOW | src/watchdog/watchmedo.py | 7 | CODE | |
| LOW | src/watchdog/version.py | 1 | CODE | |
| LOW | src/watchdog/events.py | 82 | CODE | |
| LOW | src/watchdog/utils/patterns.py | 8 | CODE | |
| LOW | src/watchdog/utils/__init__.py | 15 | CODE | |
| LOW | src/watchdog/utils/event_debouncer.py | 1 | CODE | |
| LOW | src/watchdog/utils/dirsnapshot.py | 32 | CODE | |
| LOW | src/watchdog/utils/delayed_queue.py | 6 | CODE | |
| LOW | src/watchdog/utils/platform.py | 1 | CODE | |
| LOW | src/watchdog/utils/process_watcher.py | 1 | CODE | |
| LOW | src/watchdog/utils/bricks.py | 20 | CODE | |
| LOW | src/watchdog/utils/echo.py | 26 | CODE | |
| LOW | src/watchdog/observers/fsevents.py | 8 | CODE | |
| LOW | src/watchdog/observers/inotify_move_event_grouper.py | 9 | CODE | |
| LOW | src/watchdog/observers/inotify.py | 69 | CODE | |
| LOW | src/watchdog/observers/__init__.py | 37 | CODE | |
| LOW | src/watchdog/observers/api.py | 1 | CODE | |
| LOW | src/watchdog/observers/winapi.py | 12 | CODE | |
| LOW | src/watchdog/observers/read_directory_changes.py | 1 | CODE | |
| LOW | src/watchdog/observers/kqueue.py | 58 | CODE | |
| LOW | src/watchdog/observers/inotify_c.py | 1 | CODE | |
| LOW | src/watchdog/observers/polling.py | 22 | CODE | |
| LOW | src/watchdog/observers/fsevents2.py | 8 | CODE | |
| LOW | src/watchdog/tricks/__init__.py | 26 | CODE |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| HIGH | tests/conftest.py | 0 | convenience function to join the temporary directory path with the provided arguments. | STRING |
| HIGH | tests/test_observers_winapi.py | 0 | convenience function to join the temporary directory path with the provided arguments. | STRING |
| HIGH | tests/test_observers_polling.py | 0 | convenience function to join the temporary directory path with the provided arguments. | STRING |
| HIGH | src/watchdog/observers/inotify.py | 0 | observer thread that schedules watching directories and dispatches calls to event handlers. | STRING |
| HIGH | src/watchdog/observers/read_directory_changes.py | 0 | observer thread that schedules watching directories and dispatches calls to event handlers. | STRING |
| HIGH | src/watchdog/observers/kqueue.py | 0 | observer thread that schedules watching directories and dispatches calls to event handlers. | STRING |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW | tests/test_inotify_c.py | 44 | CODE | |
| LOW | tests/test_fsevents.py | 184 | CODE | |
| LOW | tests/utils.py | 217 | CODE | |
| LOW | tests/test_emitter.py | 383 | CODE | |
| LOW | tests/test_emitter.py | 424 | CODE | |
| LOW | src/watchdog/watchmedo.py | 269 | CODE | |
| LOW | src/watchdog/watchmedo.py | 463 | CODE | |
| LOW | src/watchdog/utils/backwards_compat.py | 24 | CODE | |
| LOW | src/watchdog/utils/backwards_compat.py | 96 | CODE | |
| LOW | src/watchdog/utils/event_debouncer.py | 58 | CODE | |
| LOW | src/watchdog/utils/dirsnapshot.py | 321 | CODE | |
| LOW | src/watchdog/observers/fsevents.py | 164 | CODE | |
| LOW | src/watchdog/observers/inotify.py | 210 | CODE | |
| LOW | src/watchdog/observers/inotify.py | 319 | CODE | |
| LOW | src/watchdog/observers/inotify.py | 442 | CODE | |
| LOW | src/watchdog/observers/inotify.py | 502 | CODE | |
| LOW | src/watchdog/observers/__init__.py | 53 | CODE | |
| LOW | src/watchdog/observers/read_directory_changes.py | 67 | CODE | |
| LOW | src/watchdog/observers/kqueue.py | 424 | CODE | |
| LOW | src/watchdog/observers/kqueue.py | 494 | CODE | |
| LOW | src/watchdog/observers/kqueue.py | 610 | CODE | |
| LOW | src/watchdog/observers/inotify_c.py | 376 | CODE | |
| LOW | src/watchdog/observers/fsevents2.py | 200 | CODE | |
| LOW | src/watchdog/tricks/__init__.py | 275 | CODE |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW | docs/source/conf.py | 1 | # watchdog documentation build configuration file, created by | COMMENT |
| LOW | docs/source/conf.py | 41 | ] | COMMENT |
| LOW | .github/workflows/build-and-publish.yml | 1 | # Because this library provides extension modules for macOS, but not for other | COMMENT |
| LOW | src/pythoncapi_compat.h | 1 | // Header file providing new C API functions to old Python versions. | COMMENT |
| LOW | src/pythoncapi_compat.h | 21 | #include <Python.h> | COMMENT |
| LOW | src/pythoncapi_compat.h | 41 | # if (defined (__STDC_VERSION__) && __STDC_VERSION__ > 201710L) \ | COMMENT |
| LOW | src/pythoncapi_compat.h | 121 | # define Py_IsNone(x) Py_Is(x, Py_None) | COMMENT |
| LOW | src/pythoncapi_compat.h | 481 | COMMENT | |
| LOW | src/pythoncapi_compat.h | 541 | #if PY_VERSION_HEX < 0x030B00C1 && !defined(PYPY_VERSION) | COMMENT |
| LOW | src/pythoncapi_compat.h | 1221 | # define PyHASH_IMAG _PyHASH_IMAG | COMMENT |
| LOW | src/pythoncapi_compat.h | 1921 | #if PY_VERSION_HEX < 0x030C00A3 | COMMENT |
| LOW | src/pythoncapi_compat.h | 1941 | COMMENT | |
| LOW | src/watchdog/observers/fsevents.py | 181 | stat = os.stat(src_path) | COMMENT |
| LOW | src/watchdog/observers/inotify.py | 221 | if event.is_moved_from: | COMMENT |
| LOW | src/watchdog/observers/kqueue.py | 441 | # of created and deleted events for the path. | COMMENT |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW | tests/test_inotify_c.py | 28 | logger = logging.getLogger(__name__) | CODE |
| LOW | tests/test_fsevents.py | 42 | logger = logging.getLogger(__name__) | CODE |
| LOW | tests/test_emitter.py | 33 | logger = logging.getLogger(__name__) | CODE |
| LOW | src/watchdog/utils/event_debouncer.py | 14 | logger = logging.getLogger(__name__) | CODE |
| LOW | src/watchdog/utils/process_watcher.py | 12 | logger = logging.getLogger(__name__) | CODE |
| LOW | src/watchdog/observers/inotify_move_event_grouper.py | 21 | logger = logging.getLogger(__name__) | CODE |
| LOW | src/watchdog/observers/inotify.py | 114 | logger = logging.getLogger(__name__) | CODE |
| LOW | src/watchdog/observers/__init__.py | 91 | __all__ = ["Observer"] | CODE |
| LOW | src/watchdog/observers/inotify_c.py | 22 | logger = logging.getLogger(__name__) | CODE |
| LOW | src/watchdog/observers/fsevents2.py | 65 | logger = logging.getLogger(__name__) | CODE |
| LOW | src/watchdog/tricks/__init__.py | 42 | logger = logging.getLogger(__name__) | CODE |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| HIGH | src/watchdog/watchmedo.py | 768 | except WatchdogShutdownError: | CODE |
| HIGH | src/watchdog/tricks/__init__.py | 290 | except OSError: | CODE |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| MEDIUM | src/watchdog/watchmedo.py | 799 | print(f"Error: {exc.args[0]}", file=sys.stderr) # noqa:T201 | CODE |
| LOW | src/watchdog/utils/process_watcher.py | 29 | except Exception: | CODE |
| LOW | src/watchdog/observers/fsevents.py | 305 | except Exception: | CODE |
| LOW⚡ | src/watchdog/observers/__init__.py | 62 | except Exception: | CODE |
| LOW⚡ | src/watchdog/observers/__init__.py | 65 | except Exception: | CODE |
| LOW⚡ | src/watchdog/observers/__init__.py | 75 | except Exception: | CODE |
| LOW | src/watchdog/observers/api.py | 308 | except Exception: | CODE |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| MEDIUM | tests/test_snapshot_diff.py | 169 | # Create a file and take a snapshot. | COMMENT |
| MEDIUM | tests/test_snapshot_diff.py | 206 | # Create a file and declare a DirectorySnapshot and a EmptyDirectorySnapshot. | COMMENT |
| MEDIUM | docs/source/conf.py | 4 | # This file is execfile()d with the current directory set to its containing dir. | COMMENT |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW | src/watchdog/observers/fsevents.py | 231 | # Check if we have a corresponding destination event in the watched path. | COMMENT |
| LOW | src/watchdog/observers/inotify_move_event_grouper.py | 73 | # Check if move_from is in delayqueue already | COMMENT |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW | src/watchdog/utils/echo.py | 21 | def my_function(args): | STRING |