Skip to content

pipreqs: Generate requirements.txt From Imports

pipreqs reads a project’s import statements and writes a requirements.txt containing only the distributions those imports resolve to. It works from the source tree, not from an installed environment.

Note

This is the opposite approach to pip freeze, which reports the environment. Imports are evidence of what the code needs; an environment records what resolution and years of drift produced. For recovering a dependency list from an undocumented project, the import scan is the better starting point, with the environment used afterwards to supply versions and to catch dependencies that are never imported.

Import Scanning

pipreqs parses each .py file for top-level imports, discards standard-library modules, and maps the rest to PyPI distribution names. Given a file importing flask, requests, jinja2, and os:

$ pipreqs --print src
Flask==3.1.3
Jinja2==3.1.6
Requests==2.34.2

os is dropped as standard library. The names are capitalized because they come from PyPI metadata rather than the import statement, and pip install treats them case-insensitively.

Import names and distribution names often differ, so pipreqs resolves them against a bundled mapping and falls back to a PyPI lookup, which it reports:

WARNING: Import named "Requests" not found locally. Trying to resolve it at the PyPI server.
WARNING: Import named "Requests" was resolved to "requests:2.34.2" package (https://pypi.org/project/requests/).
Please, verify manually the final list of requirements.txt to avoid possible dependency confusions.

The warning is worth heeding. A wrong mapping produces a plausible package name that installs something the project never used.

Version Schemes

--mode controls how versions are written. The default pins with ==, which manufactures constraints the original author may never have declared:

Mode Output
default Flask==3.1.3
compat Flask~=3.1.3
gt Flask>=3.1.3
no-pin Flask
pipreqs --print --mode no-pin src

The --help text lists this mode as non-pin, but that value raises ValueError: Invalid argument for mode flag, use 'compat', 'gt' or 'no-pin' instead. Use no-pin.

Auditing an Existing File

--diff compares a requirements.txt against the imports without modifying it, which finds declared packages the code stopped using:

$ pipreqs --diff requirements.txt src
INFO: The following modules are in requirements.txt but do not seem to be imported: boto3

--clean rewrites the file in place, dropping unimported entries:

pipreqs --clean requirements.txt src

Both operate on direct imports only, so a package required at runtime but never imported is reported as unused. Read the diff before running --clean.

Installation

# Run without installing
uvx pipreqs --print src

# Install once as a user-level tool
uv tool install pipreqs

# Install with pip
pip install pipreqs

Release 0.5.0 declares python_requires >=3.8.1,<3.13. On a newer interpreter, resolvers fall back to 0.4.13 from 2023, so uvx pipreqs reports 0.4.13 on Python 3.13 and later. Both releases scan imports correctly; the older one emits a SyntaxWarning from its yarg dependency.

Pros

  • Reports what the code imports instead of what the environment contains
  • --mode no-pin and --mode gt avoid inventing exact pins
  • --diff and --clean audit an existing requirements.txt against real usage
  • Resolves import names to distribution names, including via PyPI lookup
  • Needs no working virtual environment, only the source tree

Cons

  • Misses every dependency that is never imported: gunicorn, database drivers, pytest plugins, importlib loads
  • Import-to-distribution mapping can resolve to the wrong package, which the tool warns about but cannot settle
  • No release since February 2024, and 0.5.0 excludes Python 3.13 and later, so new interpreters silently get 0.4.13
  • Reports no dependency groups, extras, or environment markers
  • pigar releases more regularly and carries no upper Python bound

Learn More

Handbook guides

Official documentation

Last updated on