diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 038c9caf..a8a62686 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -15,7 +15,7 @@ jobs: fail-fast: false matrix: os: [ubuntu, macos, windows] - rust-version: [stable, '1.63.0'] + rust-version: [stable, '1.72'] python-version: - '3.8' - '3.9' @@ -27,9 +27,9 @@ jobs: - 'pypy3.9' - 'pypy3.10' exclude: - - rust-version: '1.63.0' + - rust-version: '1.72' os: macos - - rust-version: '1.63.0' + - rust-version: '1.72' os: windows runs-on: ${{ matrix.os }}-latest diff --git a/Cargo.lock b/Cargo.lock index 93921cbc..f4ab6199 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -16,9 +16,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.5.0" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1" +checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" [[package]] name = "cc" @@ -49,14 +49,14 @@ checksum = "248e3bacc7dc6baa3b21e405ee045c3047101a49145e7e9eca583ab4c2ca5345" [[package]] name = "filetime" -version = "0.2.23" +version = "0.2.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ee447700ac8aa0b2f2bd7bc4462ad686ba06baa6727ac149a2d6277f0d240fd" +checksum = "bf401df4a4e3872c4fe8151134cf483738e74b67fc934d6532c882b3d24a4550" dependencies = [ "cfg-if", "libc", - "redox_syscall", - "windows-sys 0.52.0", + "libredox", + "windows-sys 0.59.0", ] [[package]] @@ -82,9 +82,9 @@ checksum = "b248f5224d1d606005e02c97f5aa4e88eeb230488bcc03bc9ca4d7991399f2b5" [[package]] name = "inotify" -version = "0.9.6" +version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8069d3ec154eb856955c1c0fbffefbf5f3c40a104ec912d4797314c1801abff" +checksum = "fdd168d97690d0b8c412d6b6c10360277f4d7ee495c5d0d5d5fe0854923255cc" dependencies = [ "bitflags 1.3.2", "inotify-sys", @@ -100,6 +100,15 @@ dependencies = [ "libc", ] +[[package]] +name = "instant" +version = "0.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0242819d153cba4b4b05a5a8f2a7e9bbf97b6055b2a002b395c96b5ff3c0222" +dependencies = [ + "cfg-if", +] + [[package]] name = "kqueue" version = "1.0.8" @@ -126,11 +135,22 @@ version = "0.2.154" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ae743338b92ff9146ce83992f766a31066a91a8c84a45e0e9f21e7cf6de6d346" +[[package]] +name = "libredox" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" +dependencies = [ + "bitflags 2.6.0", + "libc", + "redox_syscall", +] + [[package]] name = "log" -version = "0.4.21" +version = "0.4.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" +checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" [[package]] name = "memoffset" @@ -156,11 +176,9 @@ dependencies = [ [[package]] name = "notify" version = "6.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6205bd8bb1e454ad2e27422015fb5e4f2bcc7e08fa8f27058670d208324a4d2d" +source = "git+https://github.com/samuelcolvin/notify.git?branch=keep-io-error#0f87ab12707d231fa44180454d59478d3992eb59" dependencies = [ - "bitflags 2.5.0", - "crossbeam-channel", + "bitflags 2.6.0", "filetime", "fsevent-sys", "inotify", @@ -168,8 +186,17 @@ dependencies = [ "libc", "log", "mio", + "notify-types", "walkdir", - "windows-sys 0.48.0", + "windows-sys 0.52.0", +] + +[[package]] +name = "notify-types" +version = "1.0.0" +source = "git+https://github.com/samuelcolvin/notify.git?branch=keep-io-error#0f87ab12707d231fa44180454d59478d3992eb59" +dependencies = [ + "instant", ] [[package]] @@ -277,11 +304,11 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.4.1" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" +checksum = "2a908a6e00f1fdd0dfd9c0eb08ce85126f6d8bbda50017e74bc4a4b7d4a926a4" dependencies = [ - "bitflags 1.3.2", + "bitflags 2.6.0", ] [[package]] @@ -349,11 +376,11 @@ dependencies = [ [[package]] name = "winapi-util" -version = "0.1.8" +version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d4cc384e1e73b93bafa6fb4f1df8c41695c8a91cf9c4c64358067d15a7b6c6b" +checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" dependencies = [ - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -371,7 +398,16 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ - "windows-targets 0.52.5", + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets 0.52.6", ] [[package]] @@ -391,18 +427,18 @@ dependencies = [ [[package]] name = "windows-targets" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" dependencies = [ - "windows_aarch64_gnullvm 0.52.5", - "windows_aarch64_msvc 0.52.5", - "windows_i686_gnu 0.52.5", + "windows_aarch64_gnullvm 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", "windows_i686_gnullvm", - "windows_i686_msvc 0.52.5", - "windows_x86_64_gnu 0.52.5", - "windows_x86_64_gnullvm 0.52.5", - "windows_x86_64_msvc 0.52.5", + "windows_i686_msvc 0.52.6", + "windows_x86_64_gnu 0.52.6", + "windows_x86_64_gnullvm 0.52.6", + "windows_x86_64_msvc 0.52.6", ] [[package]] @@ -413,9 +449,9 @@ checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" [[package]] name = "windows_aarch64_gnullvm" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" [[package]] name = "windows_aarch64_msvc" @@ -425,9 +461,9 @@ checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" [[package]] name = "windows_aarch64_msvc" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" [[package]] name = "windows_i686_gnu" @@ -437,15 +473,15 @@ checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" [[package]] name = "windows_i686_gnu" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" [[package]] name = "windows_i686_gnullvm" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" [[package]] name = "windows_i686_msvc" @@ -455,9 +491,9 @@ checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" [[package]] name = "windows_i686_msvc" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" [[package]] name = "windows_x86_64_gnu" @@ -467,9 +503,9 @@ checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" [[package]] name = "windows_x86_64_gnu" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" [[package]] name = "windows_x86_64_gnullvm" @@ -479,9 +515,9 @@ checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" [[package]] name = "windows_x86_64_gnullvm" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" [[package]] name = "windows_x86_64_msvc" @@ -491,6 +527,6 @@ checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" [[package]] name = "windows_x86_64_msvc" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" diff --git a/Cargo.toml b/Cargo.toml index e4cf78a5..cfcbe1d0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -25,7 +25,7 @@ rust-version = "1.63" [dependencies] crossbeam-channel = "0.5.12" -notify = "6.1.1" +notify = {git = "https://github.com/samuelcolvin/notify.git", branch = "keep-io-error"} pyo3 = { version = "0.22.2", features = ["extension-module", "generate-import-lib"] } [lib] diff --git a/src/lib.rs b/src/lib.rs index c94517fc..20b33486 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -181,6 +181,21 @@ impl RustNotify { } } Err(e) => { + if debug { + eprintln!("raw-error={:?} error.kind={:?} error.paths={:?}", e, e.kind, e.paths); + } + // see https://github.com/samuelcolvin/watchfiles/issues/282 + // if we have IO errors from files not found, we return "file deleted", rather than the error + if let NotifyErrorKind::Io(io_error) = &e.kind { + if io_error.kind() == IOErrorKind::NotFound { + changes_clone.lock().unwrap().extend( + e.paths + .iter() + .map(|p| (CHANGE_DELETED, p.to_string_lossy().to_string())), + ); + return; + } + } *error_clone.lock().unwrap() = Some(format!("error in underlying watcher: {}", e)); } }; diff --git a/tests/test_rust_notify.py b/tests/test_rust_notify.py index 1e4a6683..46861e2a 100644 --- a/tests/test_rust_notify.py +++ b/tests/test_rust_notify.py @@ -161,7 +161,7 @@ def test_does_not_exist(tmp_path: Path): @skip_unless_linux def test_does_not_exist_message(tmp_path: Path): p = tmp_path / 'missing' - with pytest.raises(FileNotFoundError, match='No such file or directory'): + with pytest.raises(FileNotFoundError, match='(No such file or directory|No path was found.)'): RustNotify([str(p)], False, False, 0, True, False) diff --git a/watchfiles/main.py b/watchfiles/main.py index 2fb9ef87..59e507b7 100644 --- a/watchfiles/main.py +++ b/watchfiles/main.py @@ -57,7 +57,7 @@ def watch( stop_event: Optional['AbstractEvent'] = None, rust_timeout: int = 5_000, yield_on_timeout: bool = False, - debug: bool = False, + debug: Optional[bool] = None, raise_interrupt: bool = True, force_polling: Optional[bool] = None, poll_delay_ms: int = 300, @@ -93,7 +93,8 @@ def watch( this can be anything with an `is_set()` method which returns a bool, e.g. `threading.Event()`. rust_timeout: maximum time in milliseconds to wait in the rust code for changes, `0` means no timeout. yield_on_timeout: if `True`, the generator will yield upon timeout in rust even if no changes are detected. - debug: whether to print information about all filesystem changes in rust to stdout. + debug: whether to print information about all filesystem changes in rust to stdout, if `None` will use the + `WATCHFILES_DEBUG` environment variable. raise_interrupt: whether to re-raise `KeyboardInterrupt`s, or suppress the error and just stop iterating. force_polling: See [Force polling](#force-polling) above. poll_delay_ms: delay between polling for changes, only used if `force_polling=True`. @@ -114,6 +115,7 @@ def watch( """ force_polling = _default_force_polling(force_polling) ignore_permission_denied = _default_ignore_permission_denied(ignore_permission_denied) + debug = _default_debug(debug) with RustNotify( [str(p) for p in paths], debug, force_polling, poll_delay_ms, recursive, ignore_permission_denied ) as watcher: @@ -149,7 +151,7 @@ async def awatch( # C901 stop_event: Optional['AnyEvent'] = None, rust_timeout: Optional[int] = None, yield_on_timeout: bool = False, - debug: bool = False, + debug: Optional[bool] = None, raise_interrupt: Optional[bool] = None, force_polling: Optional[bool] = None, poll_delay_ms: int = 300, @@ -241,6 +243,7 @@ async def stop_soon(): force_polling = _default_force_polling(force_polling) ignore_permission_denied = _default_ignore_permission_denied(ignore_permission_denied) + debug = _default_debug(debug) with RustNotify( [str(p) for p in paths], debug, force_polling, poll_delay_ms, recursive, ignore_permission_denied ) as watcher: @@ -324,6 +327,13 @@ def _default_force_polling(force_polling: Optional[bool]) -> bool: return _auto_force_polling() +def _default_debug(debug: Optional[bool]) -> bool: + if debug is not None: + return debug + env_var = os.getenv('WATCHFILES_DEBUG') + return bool(env_var) + + def _auto_force_polling() -> bool: """ Whether to auto-enable force polling, it should be enabled automatically only on WSL. diff --git a/watchfiles/run.py b/watchfiles/run.py index 5a6c8009..cd403589 100644 --- a/watchfiles/run.py +++ b/watchfiles/run.py @@ -37,7 +37,7 @@ def run_process( grace_period: float = 0, debounce: int = 1_600, step: int = 50, - debug: bool = False, + debug: Optional[bool] = None, sigint_timeout: int = 5, sigkill_timeout: int = 1, recursive: bool = True, @@ -165,7 +165,7 @@ async def arun_process( grace_period: float = 0, debounce: int = 1_600, step: int = 50, - debug: bool = False, + debug: Optional[bool] = None, recursive: bool = True, ignore_permission_denied: bool = False, ) -> int: