Skip to content

Commit 17d143f

Browse files
author
feryc@hotmail.com
committed
fix: raise FileAccessError for unreadable files in search_file, skip correctly in search_directory
1 parent 738e13d commit 17d143f

4 files changed

Lines changed: 16 additions & 10 deletions

File tree

src/search_engine.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33

44
from .config import config
55
from .data_models import SimilarMatch
6-
from .exceptions import SearchError
6+
from .exceptions import FileAccessError, SearchError
77
from .file_access import read_file_lines
88

99

@@ -183,7 +183,7 @@ def search_file(
183183
try:
184184
lines = read_file_lines(file_path)
185185
except Exception as e:
186-
raise SearchError(f"Cannot read {file_path}: {e}") from e
186+
raise FileAccessError(f"Cannot read {file_path}: {e}") from e
187187

188188
# Route to appropriate matcher
189189
if regex:

src/tools.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -938,7 +938,7 @@ def search_directory(
938938
matches = search_file(
939939
abs_path, pattern, fuzzy, regex, case_sensitive, invert
940940
)
941-
except (OSError, UnicodeDecodeError):
941+
except FileAccessError:
942942
continue # skip unreadable / binary files silently
943943

944944
files_searched += 1

tests/unit/test_search_engine.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
import pytest
1010

1111
from src.data_models import SimilarMatch
12-
from src.exceptions import SearchError
12+
from src.exceptions import FileAccessError, SearchError
1313
from src.search_engine import (
1414
find_exact_matches,
1515
find_fuzzy_matches,
@@ -234,11 +234,17 @@ def temp_file(self):
234234
Path(f.name).unlink(missing_ok=True)
235235

236236
def test_regex_and_fuzzy_error(self, temp_file):
237-
"""Regex and fuzzy together raises error."""
237+
"""Regex and fuzzy together raises SearchError."""
238238
with pytest.raises(SearchError) as exc_info:
239239
search_file(temp_file, "pattern", fuzzy=True, regex=True)
240240
assert "Cannot use regex and fuzzy together" in str(exc_info.value)
241241

242+
def test_search_file_unreadable_raises_file_access_error(self):
243+
"""Unreadable / nonexistent file raises FileAccessError, not SearchError."""
244+
with pytest.raises(FileAccessError) as exc_info:
245+
search_file("/nonexistent/path/file.txt", "pattern")
246+
assert "Cannot read" in str(exc_info.value)
247+
242248
def test_search_file_exact_mode(self, temp_file):
243249
"""Search with fuzzy=False uses exact matching."""
244250
matches = search_file(temp_file, "ERROR", fuzzy=False)

tests/unit/test_tools_search_directory.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
from pathlib import Path
55
from unittest.mock import patch
66

7-
from src.exceptions import SearchError
7+
from src.exceptions import FileAccessError, SearchError
88
from src.tools import search_directory
99

1010
# ---------------------------------------------------------------------------
@@ -323,7 +323,7 @@ def test_fuzzy_mode_finds_approximate_match(self, tmp_path: Path) -> None:
323323

324324
class TestSearchDirectoryErrorPaths:
325325
def test_unreadable_file_is_skipped_silently(self, tmp_path: Path) -> None:
326-
"""OSError from search_file (unreadable / binary) causes the file to be skipped."""
326+
"""FileAccessError from search_file (unreadable / binary) causes the file to be skipped."""
327327
(tmp_path / "good.txt").write_text("needle\n")
328328
(tmp_path / "bad.txt").write_text("needle\n")
329329

@@ -335,7 +335,7 @@ def test_unreadable_file_is_skipped_silently(self, tmp_path: Path) -> None:
335335
def patched_search(path: str, *args, **kwargs): # type: ignore[no-untyped-def]
336336
call_count["n"] += 1
337337
if "bad.txt" in path:
338-
raise OSError("access denied")
338+
raise FileAccessError("Cannot read bad.txt: access denied")
339339
return real_search(path, *args, **kwargs)
340340

341341
with patch("src.tools.search_file", side_effect=patched_search):
@@ -349,13 +349,13 @@ def patched_search(path: str, *args, **kwargs): # type: ignore[no-untyped-def]
349349
assert "bad.txt" not in files
350350

351351
def test_files_searched_does_not_count_skipped_files(self, tmp_path: Path) -> None:
352-
"""Files that throw on search_file are not counted in files_searched."""
352+
"""Files that raise FileAccessError on search_file are not counted in files_searched."""
353353
(tmp_path / "a.txt").write_text("x\n")
354354
(tmp_path / "b.txt").write_text("x\n")
355355

356356
def patched_search(path: str, *args, **kwargs): # type: ignore[no-untyped-def]
357357
if "b.txt" in path:
358-
raise PermissionError("denied")
358+
raise FileAccessError("Cannot read b.txt: denied")
359359
return []
360360

361361
with patch("src.tools.search_file", side_effect=patched_search):

0 commit comments

Comments
 (0)