Hi maldoinc — found four edge-case crashes in wireup/_discovery.py while running adversarial test generation against the repo via tailtest. All four make _find_objects_in_module crash on legitimate Python project layouts; all four have small fixes.
1. module.__file__ AttributeError on PEP 420 namespace packages
Line 62: module.__file__ is accessed unconditionally. PEP 420 namespace packages don't have __file__ and crash with AttributeError.
Fix: getattr(module, '__file__', None)
2. endswith("__init__.py") false positives
Line 63: f.endswith("__init__.py") matches not__init__.py, treating it as a package and scanning its parent directory.
Fix: Path(f).name == "__init__.py"
3. Hidden directories traversed
Line 47: only __pycache__ is filtered. Dirs like .git, .mypy_cache, .pytest_cache, .hidden are traversed and importlib.import_module is called with invalid dot-prefixed names → ModuleNotFoundError.
Fix: also skip entry.name.startswith(".")
4. One broken submodule crashes entire discovery
Line 59: importlib.import_module is not wrapped in try/except. A single sub-module with an import error (e.g., missing optional dep) kills the whole discovery scan.
Fix: wrap in try/except (ImportError, Exception) and skip-with-warning instead of crashing.
Reproduction
Failing pytest cases for all four are below — drop into tests/test_discovery.py. Each test is ~10-15 LoC, fully isolated.
import os
import sys
import tempfile
import types
import pytest
from wireup._discovery import _find_objects_in_module
# Bug 1: namespace packages crash
def test_namespace_package_module_without_file_attr():
"""PEP 420 namespace packages have no __file__; should be handled."""
mod = types.ModuleType("ns_pkg")
if hasattr(mod, "__file__"):
del mod.__file__
# Should not raise AttributeError
list(_find_objects_in_module(mod, predicate=lambda _: False))
# Bug 2: false-positive endswith("__init__.py")
def test_file_named_like_init_but_not_init(tmp_path):
"""File named not__init__.py should NOT be treated as a package."""
pkg = tmp_path / "my_pkg"
pkg.mkdir()
(pkg / "not__init__.py").write_text("X = 1\n")
sys.path.insert(0, str(tmp_path))
try:
import importlib
mod = importlib.import_module("my_pkg.not__init__")
list(_find_objects_in_module(mod, predicate=lambda _: False))
finally:
sys.path.remove(str(tmp_path))
# Bug 3: hidden dirs traversed
def test_dotdir_in_package_causes_import_attempt(tmp_path):
"""Dirs starting with '.' should be skipped during discovery."""
pkg = tmp_path / "hidden_pkg"
pkg.mkdir()
(pkg / "__init__.py").write_text("")
(pkg / ".hidden").mkdir()
(pkg / ".hidden" / "__init__.py").write_text("")
sys.path.insert(0, str(tmp_path))
try:
import importlib
mod = importlib.import_module("hidden_pkg")
# Should not raise ModuleNotFoundError on hidden_pkg..hidden
list(_find_objects_in_module(mod, predicate=lambda _: False))
finally:
sys.path.remove(str(tmp_path))
# Bug 4: broken submodule crashes discovery
def test_broken_submodule_crashes_discovery(tmp_path):
"""One sub-module with an ImportError should not kill all discovery."""
pkg = tmp_path / "brokenpkg"
pkg.mkdir()
(pkg / "__init__.py").write_text("")
(pkg / "good.py").write_text("class A: ...")
(pkg / "bad.py").write_text("import nonexistent_dep")
sys.path.insert(0, str(tmp_path))
try:
import importlib
mod = importlib.import_module("brokenpkg")
# Should not raise; should return objects from good.py
result = list(_find_objects_in_module(mod, predicate=lambda _: True))
finally:
sys.path.remove(str(tmp_path))
Happy to open a PR if useful — the changes are small enough to be a single commit. Found via tailtest's adversarial test generation.
Hi maldoinc — found four edge-case crashes in
wireup/_discovery.pywhile running adversarial test generation against the repo via tailtest. All four make_find_objects_in_modulecrash on legitimate Python project layouts; all four have small fixes.1.
module.__file__AttributeError on PEP 420 namespace packagesLine 62:
module.__file__is accessed unconditionally. PEP 420 namespace packages don't have__file__and crash withAttributeError.Fix:
getattr(module, '__file__', None)2.
endswith("__init__.py")false positivesLine 63:
f.endswith("__init__.py")matchesnot__init__.py, treating it as a package and scanning its parent directory.Fix:
Path(f).name == "__init__.py"3. Hidden directories traversed
Line 47: only
__pycache__is filtered. Dirs like.git,.mypy_cache,.pytest_cache,.hiddenare traversed andimportlib.import_moduleis called with invalid dot-prefixed names →ModuleNotFoundError.Fix: also skip
entry.name.startswith(".")4. One broken submodule crashes entire discovery
Line 59:
importlib.import_moduleis not wrapped in try/except. A single sub-module with an import error (e.g., missing optional dep) kills the whole discovery scan.Fix: wrap in
try/except (ImportError, Exception)and skip-with-warning instead of crashing.Reproduction
Failing pytest cases for all four are below — drop into
tests/test_discovery.py. Each test is ~10-15 LoC, fully isolated.Happy to open a PR if useful — the changes are small enough to be a single commit. Found via tailtest's adversarial test generation.