Fix discover() crash when file is deleted during stat()
A file deleted between os.walk() and path.stat() raises FileNotFoundError, which propagated uncaught and failed the entire discover() sweep. With enough failures this trips the circuit breaker, silencing the poller. Catch FileNotFoundError around the stat() call and skip the file. The next sweep (or watchfiles) will emit the DELETE event.
This commit is contained in:
parent
d5e5733f67
commit
7d80eb4f83
2 changed files with 25 additions and 1 deletions
|
|
@ -139,7 +139,12 @@ class FSSource:
|
|||
if not self.filter.include_file(str(path)):
|
||||
continue
|
||||
uri = path.as_uri()
|
||||
revision = str(path.stat().st_mtime_ns)
|
||||
try:
|
||||
revision = str(path.stat().st_mtime_ns)
|
||||
except FileNotFoundError:
|
||||
# File was deleted between the os.walk() and stat() call.
|
||||
# Skip it — the next sweep (or watchfiles) will emit DELETE.
|
||||
continue
|
||||
seen.add(uri)
|
||||
previous = snapshot.get(uri)
|
||||
kind = (
|
||||
|
|
|
|||
|
|
@ -131,6 +131,25 @@ async def test_fs_source_discover_respects_extension_filter(fs_root: Path):
|
|||
assert (fs_root / "b.txt").as_uri() not in uris
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_fs_source_discover_skips_file_deleted_during_stat(fs_root: Path):
|
||||
"""A file deleted between os.walk() and stat() should be silently
|
||||
skipped instead of crashing the entire discover() sweep."""
|
||||
src = FSSource(root=fs_root, supported_extensions=[".md", ".txt"])
|
||||
events = []
|
||||
async for event in src.discover(since=None):
|
||||
events.append(event)
|
||||
# Delete a file mid-iteration so the next stat() hits a missing file.
|
||||
victim = fs_root / "b.txt"
|
||||
if victim.exists():
|
||||
victim.unlink()
|
||||
uris = {e.uri for e in events}
|
||||
# a.md and sub/c.md should still appear; b.txt may or may not depending
|
||||
# on iteration order, but the key assertion is no exception was raised.
|
||||
assert (fs_root / "a.md").as_uri() in uris
|
||||
assert (fs_root / "sub" / "c.md").as_uri() in uris
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_fs_source_discover_respects_ignore_patterns(fs_root: Path):
|
||||
src = FSSource(
|
||||
|
|
|
|||
Loading…
Reference in a new issue