From 9589c5b614bfc8dc166a3aa69537dee6eaa219d8 Mon Sep 17 00:00:00 2001 From: Andre Lustosa Date: Thu, 23 Apr 2026 08:08:43 -0400 Subject: [PATCH] fix(sources): catch FileNotFoundError for broken symlinks Add FileNotFoundError to except clause and add test for broken symlinks. Closes: #1082 Co-Authored-By: Claude Opus 4.6 (1M context) Signed-off-by: Andre Lustosa --- src/fromager/sources.py | 3 ++- tests/test_sources.py | 8 ++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/src/fromager/sources.py b/src/fromager/sources.py index e2a75b9b..bc875efd 100644 --- a/src/fromager/sources.py +++ b/src/fromager/sources.py @@ -883,10 +883,11 @@ def scan_compiled_extensions( elif suffix not in ignore_suffixes: # Path.walk() lists symlinks to directories as filenames # rather than dirnames, causing IsADirectoryError on open(). + # Broken symlinks raise FileNotFoundError on open(). try: with filepath.open("rb") as f: header = f.read(_MAGIC_HEADERS_READ) - except IsADirectoryError: + except (IsADirectoryError, FileNotFoundError): continue if header.startswith(magic_headers): relpath = filepath.relative_to(root_dir) diff --git a/tests/test_sources.py b/tests/test_sources.py index 808f1136..19ad7f06 100644 --- a/tests/test_sources.py +++ b/tests/test_sources.py @@ -285,3 +285,11 @@ def test_scan_compiled_extensions( assert matches == [pathlib.Path(filename)] else: assert matches == [] + + +def test_scan_compiled_extensions_broken_symlink(tmp_path: pathlib.Path) -> None: + """Verify broken symlinks are skipped without raising an error.""" + broken_link = tmp_path / "broken_link" + broken_link.symlink_to(tmp_path / "nonexistent_target") + matches = sources.scan_compiled_extensions(tmp_path) + assert matches == []