Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

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:

[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:

ImportDistribution
cv2opencv-python
yamlPyYAML
PILPillow
sklearnscikit-learn
bs4beautifulsoup4
dateutilpython-dateutil
dotenvpython-dotenv
jwtPyJWT

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.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

[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.
KeyTypeDefaultDescription
outputstring"requirements.txt"Output file path
excludestring[][]Import names to never emit
sortedbooltrueSort entries alphabetically (false preserves first-seen order)
headerbooltrueInclude a comment header line
mappingmap{}Per-project import→distribution overrides
extrastring[][]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.