Requirements Processor
Purpose
Generates a requirements.txt file for a Python project by scanning the
project’s .py source files for import statements and listing the
third-party PyPI distributions they reference.
How It Works
- Scans every
.pyfile in the project’s source directories. - Extracts the top-level module name from each
import/fromstatement. - Drops imports that resolve to a local project file (intra-project imports).
- Drops imports that are part of the Python standard library.
- Drops imports listed in
exclude. - Maps each remaining import name to its PyPI distribution name using the
built-in curated table (e.g.
cv2→opencv-python,yaml→PyYAML). User-suppliedmappingentries win over the built-in table. - Appends any distributions listed in
extra(see below). - Writes the deduplicated result to
requirements.txt.
Extra distributions
Some packages do import x at module top level for a runtime helper they
do not declare as an install_requires dependency. The classic case is
manim-voiceover doing import pkg_resources, which lives in
setuptools. When that package’s metadata omits setuptools, pip will
not install it, and import manim_voiceover will fail at runtime.
Static AST scanning cannot see those transitive needs, so the extra
field lets you list distributions that should always appear in
requirements.txt:
[processor.generator.requirements]
extra = ["setuptools"]
Entries in extra bypass the exclude and stdlib filters — they are
distribution names the user is explicitly asserting must ship.
Import → Distribution Mapping
Most Python packages publish under the same name as their top-level import,
so the default is identity (import requests → requests). A curated table
handles the common exceptions:
| Import | Distribution |
|---|---|
cv2 | opencv-python |
yaml | PyYAML |
PIL | Pillow |
sklearn | scikit-learn |
bs4 | beautifulsoup4 |
dateutil | python-dateutil |
dotenv | python-dotenv |
jwt | PyJWT |
Projects that import an unusual name should add an override:
[processor.generator.requirements.mapping]
internal_tools = "acme-internal-tools"
Limitations
- No version pinning. The generated file lists bare distribution names.
Running
pip freeze > requirements.txtis the right tool if you need pinned versions. - Static analysis only. Conditional imports inside
tryblocks, runtime__import__calls, and string-based imports are not detected. - Curated mapping is finite. Packages with import/distribution name
mismatches not in the built-in table default to identity; add them to
mappingwhen needed.
Source Files
- Input:
**/*.py(configurable viasrc_dirs/src_extensions) - Output:
requirements.txt(configurable viaoutput)
Configuration
[processor.generator.requirements]
output = "requirements.txt" # Output file path
exclude = [] # Import names to never emit
sorted = true # Sort entries alphabetically
header = true # Include a "# Generated by rsconstruct" header
extra = [] # Distribution names to always include
[processor.generator.requirements.mapping]
# Per-project overrides: import_name = "pypi-distribution-name"
# These win over the built-in curated table.
| Key | Type | Default | Description |
|---|---|---|---|
output | string | "requirements.txt" | Output file path |
exclude | string[] | [] | Import names to never emit |
sorted | bool | true | Sort entries alphabetically (false preserves first-seen order) |
header | bool | true | Include a comment header line |
mapping | map | {} | Per-project import→distribution overrides |
extra | string[] | [] | Distribution names to always include (transitive deps undeclared by upstream) |
Batch support
Runs as a single whole-project operation — all .py files feed into one
requirements.txt output.
Clean behavior
This processor is a Generator — rsconstruct clean outputs removes each declared output file individually with no directory recursion. After all per-product cleans complete, the orchestrator removes any parent directories that are now empty. Pass --no-empty-dirs to keep them. See Clean behavior and rsconstruct clean.