Skip to content

Referencing requirements.txt in pyproject.toml for setuptools

Problem Statement

When migrating setuptools-based Python projects from setup.py to modern pyproject.toml configuration, developers often face a challenge: How to maintain established pip-compile workflows using requirements.txt files while configuring dependencies directly through pyproject.toml.

The core conflict exists between: 🔒 Needing pinned dependencies (via requirements.txt) for reproducible installations and dependency conflict resolution 📦 Wanting modern configuration without maintaining a legacy setup.py script

This leaves developers wondering if they can reference external requirements.txt files directly in pyproject.toml while retaining setuptools as their build backend.

Modern setuptools (v62.6+) provides native support for external dependency files through dynamic metadata declarations. This is currently the most robust solution for referencing requirements.txt in pyproject.toml.

Implementation Steps

  1. Declare dependencies as dynamic in the [project] section
  2. Reference requirements.txt in the [tool.setuptools.dynamic] section
toml
[project]
name = "my-project"
version = "1.0.0"
dynamic = ["dependencies"]

[tool.setuptools.dynamic]
dependencies = { file = ["requirements.txt"] }

Important Limitations

  • The requirements.txt file must follow PEP 508 standards
  • These flags/commands are NOT supported in the referenced file:
    • -r (recursive requirements)
    • -c (constraints files)
    • -e (editable installs)

Handling Optional Dependencies

For development dependencies (e.g., requirements-dev.txt), declare them as an optional dependency group:

toml
[project]
dynamic = ["dependencies", "optional-dependencies"]

[tool.setuptools.dynamic]
dependencies = { file = ["requirements.txt"] }
optional-dependencies = { 
    dev = { file = ["requirements-dev.txt"] } 
}

Version Caveats

  • Requires setuptools ≥ 62.6
  • Automatic inclusion in source distributions (sdist) requires setuptools ≥ 66.1.0 (older versions need manual MANIFEST.in configuration)
  • All optional dependency groups must be declared dynamically if any are used

Compatibility Handling

To ensure compatibility across environments, explicitly declare setuptools version requirements in your build system:

toml
[build-system]
requires = ["setuptools>=66.1.0"]
build-backend = "setuptools.build_meta"

Alternative Approaches

1. Using Hatch (Alternative Build System)

If you can migrate to Hatch, leverage the hatch-requirements-txt plugin:

toml
[build-system]
requires = ["hatchling", "hatch-requirements-txt"]
build-backend = "hatchling.build"

[project]
name = "my-project"
version = "1.0.0"
dynamic = ["dependencies"]

[tool.hatch.metadata.hooks.requirements_txt]
files = ["requirements.txt"]

2. Using uv (Modern Pip Alternative)

The uv tool by Astral provides streamlined workflow integration:

bash
uv pip compile requirements.in -o requirements.txt
uv add -r requirements.txt

When to Consider Alternatives

  • If your dependency files require special flags (-r, -c, -e)
  • If your project can adopt newer tools like Hatch or uv
  • If you need functionality beyond current setuptools capabilities

Best Practices and Recommendations

  1. Validate requirement syntax using packaging.requirements.Requirement before deployment
  2. Maintain setuptools ≥ 66.1.0 to avoid manual file inclusion in source distributions
  3. Keep requirement files clean by removing non-PEP 508 elements
  4. Use separate base files like requirements.in for pip-compile input, compiling to PEP 508-compliant output

Migration Workflow

Conclusion

The dynamic metadata capability in modern setuptools provides the most straightforward solution for referencing requirements.txt in pyproject.toml. While alternative tools like Hatch and uv offer different approaches, the setuptools-native method ensures backward compatibility with existing workflows when using version 66.1.0 or newer.

For maximum stability:

  1. Pin setuptools to ≥66.1.0
  2. Ensure your requirements.txt follows strict PEP 508 format
  3. Validate your build using python -m build before deployment

This approach maintains transparency in dependency management while fully embracing modern Python packaging standards.