Simplify your setup.py
setupmeta is a Python library designed to significantly reduce boilerplate in `setup.py` files by automatically inferring project metadata from common project conventions. It extracts information like long description from `README.rst`/`README.md`, version from `__version__.py` or Git tags, and dependencies from `requirements.txt`. It encourages a DRY (Don't Repeat Yourself) approach to Python packaging. The current version is 3.9.0, and it is actively maintained with a regular release cadence.
Warnings
- gotcha Using tag-based versioning (e.g., `versioning='distance'` or `versioning='post'`) requires Git to be installed and accessible, with a version of 1.8.4 or higher. If Git is not available or too old, version detection will fail, which can be a common issue in CI/CD environments or minimal Docker images.
- gotcha Any parameters explicitly passed to `setuptools.setup()` will *always* take precedence over metadata deduced by setupmeta. This is by design, allowing granular control, but can lead to confusion if expected behavior is overridden by inadvertently explicit (or outdated) values in `setup.py`.
- deprecated setupmeta's primary integration point is via `setup_requires` in `setup.py`. While functional, the `setup_requires` mechanism is considered legacy in modern Python packaging (PEP 517/518), which prefers specifying build dependencies in `pyproject.toml` under `build-system.requires`. Not configuring `pyproject.toml` correctly may lead to issues with modern build tools or future `setuptools` versions.
Install
-
pip install setupmeta
Imports
- setup
from setuptools import setup
Quickstart
import os
import subprocess
# Create a dummy project structure for demonstration
project_name = "myproject_example"
os.makedirs(project_name, exist_ok=True)
with open(os.path.join(project_name, "__init__.py"), "w") as f:
f.write("__version__ = '0.0.1'\n")
f.write("__title__ = 'My Example Project'\n")
with open(os.path.join(project_name, "../README.rst"), "w") as f:
f.write("My Example Project\n")
f.write("===================\n\n")
f.write("A simple project to demonstrate setupmeta.")
with open(os.path.join(project_name, "../requirements.txt"), "w") as f:
f.write("click\n")
f.write("requests>=2.0.0\n")
# Create a minimal setup.py
setup_py_content = """
from setuptools import setup
setup(
name="%s",
versioning="distance", # Optional: activates tag-based versioning based on git
setup_requires=["setupmeta"] # This tells setuptools to use setupmeta
)
""" % project_name
with open(os.path.join(project_name, "../setup.py"), "w") as f:
f.write(setup_py_content)
print(f"Project '{project_name}' and setup.py created. Now run: ")
print(f"cd {os.path.dirname(os.path.abspath(__file__))}")
print(f"python setup.py explain # To see what setupmeta deduces")
print(f"python setup.py sdist bdist_wheel # To build distributions")
# Example of running the command programmatically (requires project setup from above)
# try:
# # This command needs to be run from the directory containing setup.py
# # For this example, assume it's run in the parent directory of 'myproject_example'
# original_dir = os.getcwd()
# os.chdir(os.path.dirname(os.path.abspath(__file__)))
# subprocess.run(["python", "setup.py", "explain"], check=True)
# finally:
# os.chdir(original_dir)