Packaging & Project Setup
1. pyproject.toml is the single source of truth
- There is no
setup.pyorsetup.cfg. The only exception is when a compiled extension needs one (multimark uses a CFFI build). - Project metadata, dependencies, extras, and the ruff, pytest, coverage and pyright settings all live in
pyproject.toml.
3. Flat layout with explicit package discovery
[tool.setuptools.packages.find]
include = ["mypkg", "mypkg.*"]- Explicit discovery means stray folders such as
tests/,scripts/oruser_guide/never get shipped by accident.
4. Support a modern Python floor and test the whole range
requires-python = ">=3.10"is the norm.- Classifiers list every supported minor version, 3.10 through 3.14.
- The CI matrix tests that same range.
- Code uses modern syntax (
X | None, built-in generics) throughout.
5. Keep hard dependencies small, with lower bounds only
Dependencies get
>=lower bounds and no upper caps. Caps cause resolver conflicts for downstream users.When an upper bound or pin is truly necessary, it gets a comment saying why:
"sqlglot>=23.4,<30.18", # sqlglot 30.18+ breaks DROP statementsHeavy or optional functionality goes into extras. pandas and polars are never hard dependencies when they can be avoided. The current template’s core set is just
narwhals,numpy,scipyandtyping_extensions.
6. Short, predictable extras names
pd,pl,painstall the pandas, polars and pyarrow backends.- Feature extras are named after what they enable:
altair,tables,formula,fast(numba),mcp. docsinstalls Great Docs.devinstalls pytest, pytest-cov, ruff, pyright, build, and the backend extras.allis self-referencing:all = ["mypkg[pd,pl,pa,altair,tables]"].- Error messages tell users which extra to install, e.g.
pip install "mypkg[pl]".
7. Complete, accurate metadata
authorsandmaintainers(with email),license = { file = "LICENSE" },keywords, and classifiers covering Development Status, Intended Audience, Topic, Python versions andTyping :: Typed.[project.urls]uses a consistent four-key set:[project.urls] Homepage = "..." Repository = "..." Documentation = "..." "Bug Tracker" = "..."The license is MIT, the same choice in every project.
8. Ship type information
Packages include a
py.typedmarker and list it explicitly in package data:[tool.setuptools.package-data] mypkg = ["py.typed"]pyright runs in strict mode, configured in
pyproject.toml. Only the noisy “unknown type” family of checks is relaxed, because numpy and scipy stubs trigger it constantly.pyright, run from the command line, is the source of truth:
make type-checkand CI run it, with every setting inpyproject.toml. Editors (Pylance in VS Code) read the same settings and show the same results, so a problem that only shows up in the editor means the config needs fixing, not the code.The package is checked in strict mode. Tests are type-checked too, but test function parameters (fixtures and parametrize arguments) don’t need annotations, using an execution environment for
tests/:include = ["mypkg", "tests"] executionEnvironments = [ { root = "tests", extraPaths = ["."], reportMissingParameterType = false, reportUnknownParameterType = false, reportUnknownLambdaType = false }, ]
9. Use ruff for both linting and formatting, at line length 100
[tool.ruff]
line-length = 100
target-version = "py310"
[tool.ruff.lint]
select = ["E", "F", "I", "UP", "B", "SIM"]- A single tool replaces black, isort, flake8 and pyupgrade.
- pre-commit runs ruff and the basic hygiene hooks (trailing whitespace, end-of-file). The ruff version in pre-commit is pinned to match the one in the
devextra, with a comment telling you to bump both together.
10. A self-documenting Makefile as the developer interface
Every project has the same targets, so moving between repos needs no re-learning:
help: ## Show this help message (awk over the "## " comments) install: ## pip install -e ".[dev]" test: ## pytest with coverage lint: ## ruff format + ruff check --fix check-format:## ruff format --check + ruff check type-check: ## pyright mypkg check: lint type-check test ## the pre-push gate build: ## python -m build docs: ## great-docs build docs-preview:## great-docs preview clean: ## clean-build clean-pyc clean-testmake helplists all targets, generated from the##comments on each line.