pyproject.toml declares a Python project’s build backend and distribution metadata, which an installed import package does not provide by itself.
Python pyproject.toml: build a wheel and inspect its metadata
Operation contract
The fixture writes a small src-layout package and project configuration, builds a wheel offline through the installed backend, then inspects the archive for its import module and METADATA file. Distribution name and import package name differ deliberately. The metadata describes Python requirements; it does not prove that a package is safe or that every claimed interpreter was tested.
Failure and ownership boundary
The build uses --no-isolation only because this teaching environment already pins its frontend and backend. Normal build isolation lets declared backend requirements be installed in a separate environment. Building an untrusted project can execute backend code. This fixture does not publish to a registry or install into system Python. Python package layout: owned imports and module entrypoints and Python interpreter and virtual environments: run the intended executable come first.
Tested environment
Dependency check: this program was executed on CPython 3.14.6 with build==1.6.1, setuptools==84.0.0. Install these versions in a separate virtual environment. The download includes the recorded environment snapshot; no third-party package is part of the website runtime.
Working program
import subprocess
import sys
import tempfile
import zipfile
from pathlib import Path
with tempfile.TemporaryDirectory() as directory:
project = Path(directory)
package = project / "src" / "aitrove_receipts"
package.mkdir(parents=True)
(package / "__init__.py").write_text('def amount_unit():\n return "minor"\n', encoding="utf-8")
(project / "pyproject.toml").write_text("""[build-system]
requires = ["setuptools==84.0.0"]
build-backend = "setuptools.build_meta"
[project]
name = "aitrove-receipt-fixture"
version = "0.1.0"
requires-python = ">=3.11"
[tool.setuptools.packages.find]
where = ["src"]
""", encoding="utf-8")
subprocess.run([sys.executable, "-I", "-m", "build", "--wheel", "--no-isolation", str(project)],
check=True, capture_output=True, text=True, timeout=10)
wheel = next((project / "dist").glob("*.whl"))
with zipfile.ZipFile(wheel) as archive:
names = archive.namelist()
metadata = archive.read(next(name for name in names if name.endswith("/METADATA"))).decode("utf-8")
print("module included:", "aitrove_receipts/__init__.py" in names)
print("Python metadata:", "Requires-Python: >=3.11" in metadata)Output
module included: True
Python metadata: TrueCosts and limits
A wheel build reads source and creates a compressed archive. Its cost depends on package size and backend work; build configuration may execute code, so metadata parsing and building have different trust boundaries.
Common Mistakes
- Distribution names and import names are not necessarily identical.
- Do not use --no-isolation without owning the build environment’s requirements.
Connected lessons
Python package layout: owned imports and module entrypoints, Python interpreter and virtual environments: run the intended executable, Python subprocess: argument vectors, exit codes and bounded fixtures.
Check the next state boundary
Python wheel installation: test the built artifact outside its source directory, Python editable installation: source changes and metadata changes have different lifetimes.
Trace the related workflow
Python package versions: test a declared range against parsed versions.
