Skip to content

Resolve absolute imports under the src/ layout (and recognize Sphinx conf.py)#621

Open
willfrey wants to merge 2 commits into
peteromallet:mainfrom
willfrey:fix/src-layout-import-resolution
Open

Resolve absolute imports under the src/ layout (and recognize Sphinx conf.py)#621
willfrey wants to merge 2 commits into
peteromallet:mainfrom
willfrey:fix/src-layout-import-resolution

Conversation

@willfrey

Copy link
Copy Markdown

Problem

Two false positives in orphaned-file detection (and the import graph that feeds coupling / single-use / facade detection) for standard Python project structures.

1. Absolute imports don't resolve under the src/ layout

resolve_absolute_import only tries <scan_root>/<pkg> and <project_root>/<pkg>. In the PyPA-recommended src/ layout the importable package lives at <root>/src/<pkg>, which is never tried. So an absolute import like from pkg.sub import x resolves to nothing, the imported module is recorded with zero importers, and:

  • it is misreported as an orphaned file, and
  • every other graph-based detector (coupling, single-use, facade) silently loses those edges.

Relative imports (from .sub import x) were unaffected, which is why this only bites packages imported via their absolute path. On one real-world src/-layout project, scanning produced ~70 false findings that disappeared once edges resolved (6 bogus "orphaned file" reports plus ~64 downstream across the other detectors).

2. Sphinx conf.py is flagged as orphaned

docs/conf.py is loaded by sphinx-build and has zero importers by design, but it was not in PY_ENTRY_PATTERNS.

Fix

  • Factor the candidate roots into candidate_source_roots() and add the src/ variants, tried after the existing flat roots. The change is strictly additive: any import that resolved before resolves to the same file; only previously-unresolved src/-layout imports gain an edge. Duplicate roots (common when scan root == project root) are collapsed.
  • Add conf.py to PY_ENTRY_PATTERNS, alongside the existing manage.py / wsgi.py / settings.py framework entry points.

Tests

  • test_absolute_import_resolves_under_src — constructs a src/-layout package and asserts the imported module's importer_count >= 1. Red before the fix (importer_count == 0), green after.
  • test_sphinx_conf_py_excluded_by_python_entry_patterns — feeds the real PY_ENTRY_PATTERNS through detect_orphaned_files and asserts a docs/conf.py is not flagged while a genuine dead file still is.
  • Full Python-language suite (268 passed) and the orphaned-detector suite pass.

willfrey added 2 commits June 10, 2026 22:31
The Python import resolver only tried <scan_root>/<pkg> and
<project_root>/<pkg> when resolving an absolute import such as
`from pkg.sub import x`. In a src/ layout (the PyPA-recommended packaging
layout) the package lives at <root>/src/pkg, which was never tried, so the
edge resolved to nothing: the imported module was recorded with zero
importers and misreported as orphaned, and every other graph-based detector
(coupling, single-use, facade) silently lost those edges too.

Factor the candidate roots into candidate_source_roots() and add the src/
variants, tried after the existing flat roots. The change is strictly
additive: any import that resolved before resolves to the same file; only
previously-unresolved src/-layout imports gain an edge.
docs/conf.py is loaded by sphinx-build and has zero importers by design, so
it was misreported as an orphaned file. Add conf.py to PY_ENTRY_PATTERNS
alongside the other framework entry points (manage.py, wsgi.py, settings.py).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant