From 62f06ab7409327bc8be7b2fa3bb7abbb76aac672 Mon Sep 17 00:00:00 2001 From: SurprisedDuck Date: Tue, 2 Jun 2026 13:35:44 +0200 Subject: [PATCH] Docs: respect path boundary when clearing exclusions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit add_directory cleared exclusions with a raw path.startswith(directory) test, which also matched sibling directories sharing a name prefix — adding /docs would silently un-exclude files under /docs2. Match the directory itself or paths under it (directory + os.sep) instead. --- src/personal_docs.py | 11 +++++- tests/test_personal_docs_exclusions.py | 51 ++++++++++++++++++++++++++ 2 files changed, 60 insertions(+), 2 deletions(-) create mode 100644 tests/test_personal_docs_exclusions.py diff --git a/src/personal_docs.py b/src/personal_docs.py index c7106d2..3eebdd6 100644 --- a/src/personal_docs.py +++ b/src/personal_docs.py @@ -246,8 +246,15 @@ class PersonalDocsManager: # Normalize the path directory = os.path.abspath(directory) - # Clear any exclusions for files in this directory - self.excluded_files = {p for p in self.excluded_files if not p.startswith(directory)} + # Clear any exclusions for files in this directory. Match on a path + # boundary (the directory itself or paths under it) rather than a raw + # string prefix: a bare ``startswith(directory)`` also matches sibling + # directories that merely share a name prefix (e.g. adding ``/docs`` + # would wrongly un-exclude files under ``/docs2``). + self.excluded_files = { + p for p in self.excluded_files + if not (p == directory or p.startswith(directory + os.sep)) + } self._save_excluded() if directory not in self.indexed_directories: diff --git a/tests/test_personal_docs_exclusions.py b/tests/test_personal_docs_exclusions.py new file mode 100644 index 0000000..7c775de --- /dev/null +++ b/tests/test_personal_docs_exclusions.py @@ -0,0 +1,51 @@ +"""Regression: add_directory must not un-exclude files in sibling directories. + +``add_directory`` clears exclusions for files inside the directory being added. +It previously used a raw ``path.startswith(directory)`` test, which also matched +sibling directories sharing a name prefix — so adding ``/docs`` would silently +drop exclusions for files under ``/docs2``. The match must respect a path +boundary. +""" +import os + +from src import personal_docs + + +def _make_manager(tmp_path): + mgr = personal_docs.PersonalDocsManager(str(tmp_path)) + # Pre-seed the directory as already tracked so add_directory takes the + # cheap "already indexed" branch (no indexing / refresh side effects); the + # exclusion-clearing logic under test runs unconditionally before that. + return mgr + + +def test_sibling_directory_exclusions_survive(tmp_path): + docs = tmp_path / "docs" + docs2 = tmp_path / "docs2" + docs.mkdir() + docs2.mkdir() + + sibling_excluded = os.path.abspath(str(docs2 / "secret.txt")) + mgr = _make_manager(tmp_path) + mgr.indexed_directories = [os.path.abspath(str(docs))] + mgr.excluded_files = {sibling_excluded} + + mgr.add_directory(str(docs)) + + # The sibling-directory exclusion must remain — /docs2 is not under /docs. + assert sibling_excluded in mgr.excluded_files + + +def test_own_directory_exclusions_are_cleared(tmp_path): + docs = tmp_path / "docs" + docs.mkdir() + + own_excluded = os.path.abspath(str(docs / "old.txt")) + mgr = _make_manager(tmp_path) + mgr.indexed_directories = [os.path.abspath(str(docs))] + mgr.excluded_files = {own_excluded} + + mgr.add_directory(str(docs)) + + # A file genuinely inside the added directory should be un-excluded. + assert own_excluded not in mgr.excluded_files