diff --git a/entangled/io/filedb.py b/entangled/io/filedb.py index 13605f5..01ecbb2 100644 --- a/entangled/io/filedb.py +++ b/entangled/io/filedb.py @@ -50,8 +50,11 @@ def managed_files(self) -> set[Path]: return {Path(p) for p in self.targets} def changed_files(self, fs: AbstractFileCache) -> Generator[Path]: + # A tracked file that no longer exists (e.g. a source that was moved or + # deleted) counts as changed. Without this guard `fs[Path(p)]` would + # raise `FileNotFoundError` and crash, see issue #88. return (Path(p) for p, known_stat in self.files.items() - if fs[Path(p)].stat != known_stat) + if Path(p) not in fs or fs[Path(p)].stat != known_stat) def create_target(self, fs: AbstractFileCache, path: Path): if path.is_absolute(): diff --git a/test/io/test_filedb.py b/test/io/test_filedb.py index e192c21..1824bab 100644 --- a/test/io/test_filedb.py +++ b/test/io/test_filedb.py @@ -50,3 +50,24 @@ def test_filedb(example_files: Path): assert list(db.changed_files(fs)) == [Path("d")] db.update(fs, Path("d")) assert list(db.changed_files(fs)) == [] + + +def test_changed_files_missing(tmp_path: Path): + """Regression test for issue #88: a tracked file that no longer exists on + disk (e.g. a source markdown file that was moved or renamed) should be + reported as changed rather than crashing with `FileNotFoundError`.""" + with chdir(tmp_path): + with open("source", "w") as f: + _ = f.write("hello") + + fs = FileCache() + with filedb(fs=fs) as db: + db.update(fs, Path("source")) + assert list(db.changed_files(fs)) == [] + + # the file is moved/removed while still tracked in the db + Path("source").unlink() + fs.reset() + + with filedb(fs=fs) as db: + assert list(db.changed_files(fs)) == [Path("source")]