# 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
1. Scans every `.py` file in the project's source directories.
2. Extracts the top-level module name from each `import` / `from` statement.
3. Drops imports that resolve to a local project file (intra-project imports).
4. Drops imports that are part of the Python standard library.
5. Drops imports listed in `exclude`.
6. Maps each remaining import name to its PyPI distribution name using the
built-in curated table (e.g. `cv2` → `opencv-python`, `yaml` → `PyYAML`).
User-supplied `mapping` entries win over the built-in table.
7. Appends any distributions listed in `extra` (see below).
8. 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`:
```toml
[processor.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:
| `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:
```toml
[processor.requirements.mapping]
internal_tools = "acme-internal-tools"
```
## Limitations
- **No version pinning.** The generated file lists bare distribution names.
Running `pip freeze > requirements.txt` is the right tool if you need
pinned versions.
- **Static analysis only.** Conditional imports inside `try` blocks, 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
`mapping` when needed.
## Source Files
- Input: `**/*.py` (configurable via `src_dirs` / `src_extensions`)
- Output: `requirements.txt` (configurable via `output`)
## Configuration
```toml
[processor.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.requirements.mapping]
# Per-project overrides: import_name = "pypi-distribution-name"
# These win over the built-in curated table.
```
| `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](../processors.md#clean-behavior) and [`rsconstruct clean`](../commands.md#rsconstruct-clean).