ADR-0013: ruff_python_parser and a versioned stdlib list for Python
- Status: Accepted
- Date: 2026-09-20
- Derives from: design.md § The five stages (stage 2), § What each extractor has to get right (Python), § What stays honest across the boundary, § import-linter contracts
- Constrains: architecture.md § Extractors
- Implemented by: Wave 2 plan
Context
For Python, resolution is the whole job: absolute imports against the discovered roots, relative imports against the file's package, then the standard library, then installed distributions, then unresolved. Which modules are standard library depends on the interpreter version the repository targets.
Decision
rb-extract-pythonparses withruff_python_parser(MIT) and resolves imports against roots discovered frompyproject.toml, thesrc/layout and asetup.cfgfallback.languages.python.versionselects a bundled snapshot ofsys.stdlib_module_namesper supported interpreter version; the list is regenerated by a script inrb-extract-pythonand committed.dependencyTypesfor Python arelocal,stdlib,site,type-only(aTYPE_CHECKING-guarded import),dynamic(a literalimportlib.import_moduleor__import__),unresolved.- The code layer records classes, functions, methods, decorators, bases,
@property,@staticmethod,@classmethod,@dataclass, and underscore visibility. __init__.pystands for its package as the module identity. Default excludes are.venv/,site-packages/,__pycache__/, and*.pyiunless--stubs.- import-linter contracts map one to one onto rule kinds (
forbidden,layers,independence,protected,acyclic siblings,ignore_imports), andimport import-linterperforms that mapping.
Consequences
- The Python oracle repositories' contracts must reproduce exactly; that is the wave 2 exit criterion for Python.
- A new Python release means a new stdlib snapshot and a pull request, nothing more.
Alternatives considered
- Shelling out to an interpreter. Rejected: non-hermetic and version-dependent.
- grimp as a library. Rejected: it would require Python at run time.