Setuptools Compatibility
Reference documentation on hatchling's compatibility with the setuptools ecosystem. Use this when helping users understand how hatchling interoperates with setuptools and the broader Python packaging ecosystem.
Ecosystem Interoperability
Setuptools Not Required
Hatchling is a standalone build backend. You do not need setuptools installed to:
- Build wheels and sdists
- Install packages
- Use virtual environments
- Run tests
However, some tools may recommend or depend on setuptools. That's fine - they can coexist.
When Setuptools Still Appears
Even with hatchling, you might see setuptools because:
- Transitive dependency: Some packages list setuptools as a build requirement
- pip requirements: Older pip versions recommend setuptools
- Tool ecosystem: Some development tools assume setuptools
This is normal and doesn't interfere with hatchling.
Configuration Equivalence
Setuptools setup.py → Hatchling pyproject.toml
| Setuptools Pattern | Hatchling Equivalent | Notes |
|---|---|---|
packages=find_packages() |
Auto-discovery | Works with std/src layout |
package_dir={"": "src"} |
packages = ["src/pkg"] |
Explicit configuration |
install_requires=[...] |
dependencies = [...] |
PEP 508 format |
extras_require={...} |
[project.optional-dependencies] |
Same structure |
entry_points={...} |
[project.scripts] / [project.entry-points.*] |
PEP 621 standard |
include_package_data=True |
Default behavior | VCS files included automatically |
package_data={...} |
force-include |
More explicit control |
data_files=[(...) |
[tool.hatch.build.targets.wheel] |
Wheel-specific placement |
zip_safe=False |
Default | Wheels never zipped |
setup_requires=[...] |
[build-system] requires |
Isolated at build time |
Build Artifact Compatibility
Wheels (Platform Independent)
Hatchling-built wheels are 100% compatible with wheels built by setuptools:
# Both produce identical wheels in structure
pip install mypackage-1.0.0-py3-none-any.whl
Wheel format defined by PEP 427 - format agnostic to builder.
Source Distributions
Hatchling sdists follow the standard format:
mypackage-1.0.0.tar.gz
└── mypackage-1.0.0/
├── pyproject.toml
├── PKG-INFO
├── README.md
├── LICENSE
└── src/mypackage/
├── __init__.py
└── module.py
Critical: Each sdist contains pyproject.toml with build instructions. Frontends (pip) use this to build wheels.
Dependency Resolution
PEP 508 Compliance
Both setuptools and hatchling use PEP 508 for dependency specifications:
# Identical dependencies, regardless of builder
dependencies = [
"requests>=2.28.0",
"click>=8.0,<9.0",
'pathlib2; python_version<"3.4"',
]
Dependency resolution happens in the frontend (pip), not the builder. Both builders produce identical results.
Optional Dependencies (Extras)
[project.optional-dependencies]
# PEP 621 standard - both builders support equally
dev = ["pytest>=7.0", "black"]
docs = ["sphinx", "sphinx-rtd-theme"]
# Installation
pip install mypackage[dev,docs]
Metadata Compatibility
Core Metadata Format
Both builders generate standardized Core Metadata in wheels:
mypackage-1.0.0.dist-info/
├── WHEEL # Wheel format metadata
├── METADATA # Distribution metadata
├── RECORD # File hashes
├── entry_points.txt
└── top_level.txt
This metadata is identical in format regardless of builder choice.
Metadata Inspection
# Works identically for wheels from either builder
pip show mypackage
python -c "import importlib.metadata; print(importlib.metadata.version('mypackage'))"
importlib_metadata.entry_points()
Plugin & Extension Compatibility
Entry Points System
Both builders support the standardized entry points system:
[project.entry-points."flask.commands"]
my-command = "myapp.commands:cli"
[project.entry-points."console_scripts"]
myapp = "myapp.cli:main"
Entry point discovery works identically:
from importlib.metadata import entry_points
# Works the same regardless of builder
eps = entry_points(group="flask.commands")
Plugin Distribution
Plugins built with either system are interchangeable:
# Plugin built with hatchling works in app built with setuptools
pip install hatchling-plugin setuptools-app
Testing & Validation
Test Infrastructure Compatibility
Standard testing tools work identically:
# These work the same regardless of builder
pytest
python -m unittest
tox
# Package in editable mode
pip install -e .
Distribution Validation
# twine works with hatchling-built packages
pip install twine
twine check dist/*
twine upload dist/*
# PyPI accepts wheels/sdists from any PEP 517 backend
Known Incompatibilities (Rare)
Setuptools-Specific Features Not Supported
These setuptools extensions are not needed with hatchling:
| Feature | Status | Alternative |
|---|---|---|
Custom commands (cmdclass) |
Not supported | Build hooks |
setup.py scripts |
Not supported | Entry points or scripts |
| Distutils-specific behavior | Not supported | Use only standard behaviors |
setup.py test |
Deprecated | Use pytest, tox |
When You Might Need Setuptools
- Legacy projects: Pre-2020 projects may require it
- Custom build logic: Uncommon setups with
setup.pyscripts - Distutils dependencies: Very rare; most moved to setuptools
- C extension modules: Use
scikit-build-corewith hatchling instead
Migration Recommendations
Why Switch from Setuptools
✓ Modern standards (PEP 517, 621, 660) ✓ Simpler configuration (TOML vs Python code) ✓ Smaller (hatchling ~10kb vs setuptools ~500kb) ✓ Faster builds (optimized for common cases) ✓ Better defaults (sensible out-of-box behavior) ✓ Active development (setuptools is maintenance-focused)
When to Keep Setuptools
- Project has complex custom build logic
- Team/organization standardized on setuptools
- Requires setuptools-specific plugins
- Building C extensions (though scikit-build-core is better)
Coexistence Patterns
Hybrid Approach (Not Recommended)
[build-system]
# Using hatchling as primary backend
requires = ["hatchling", "setuptools"]
build-backend = "hatchling.build"
Why not: Confusing, no benefit, increases complexity.
Clean Transition Path
Phase 1: Add hatchling
[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"
Phase 2: Remove setup.py (if present)
rm setup.py setup.cfg
Phase 3: Verify everything works
pip install -e .
python -m build
pip install .
Cross-Builder Packages
Packages Depend on Builder, Not Each Other
A package installed from a wheel doesn't care how it was built:
# This package was built with hatchling
import requests # Works regardless of how requests was built
# This works whether requests was built with:
# - hatchling
# - setuptools
# - poetry
# - flit
Key principle: Builder choice is private implementation detail.
Verification Commands
# Check what builder was used to build a package
# (Usually not visible, but you can inspect the wheel)
# Extract WHEEL metadata
unzip -p mypackage-1.0.0-py3-none-any.whl mypackage-1.0.0.dist-info/WHEEL
# For installed packages, metadata is already in site-packages
python -c "import importlib.metadata; print(importlib.metadata.version('mypackage'))"
# Test that all standard operations work
pip install .
pip install -e .
pip install .[dev]
python -m pytest
Resources
- Setuptools Documentation
- Hatchling Documentation
- PEP 517 Build Systems
- PyPA Packaging Specifications
Key Takeaway: Hatchling is a drop-in replacement for setuptools that implements standard PEPs. The ecosystem is builder-agnostic at runtime.