Verified against ChatGPT · 2026-07-27
Package a Python project for real distribution with pyproject.toml
A prompt for producing a complete, modern src-layout package with a real pyproject.toml, dev/test dependency groups, and a verified build step, instead of a bare setup.py or an incomplete pyproject stub.
The prompt
Ready to copy — highlighted parts are example details you can swap.
Package the project below for real distribution — not a script that only runs from inside its own folder because of an accident of sys.path, not a setup.py from a five-year-old tutorial. PROJECT A small library called "orderkit" for parsing and validating e-commerce order exports. RUNTIME DEPENDENCIES pydantic>=2.9, pandas>=2.2 DISTRIBUTION TARGET internal use via a private index, not public PyPI VERSIONING SCHEME Semantic versioning starting at 0.1.0, bumped manually per release, no pre-1.0 API stability guarantee. REQUIREMENTS 1. Use a src/ layout (src/<package_name>/..., inferring the actual package name from the project description) so the package can't accidentally be imported from the repo root during development, hiding a packaging bug that only shows up after a real install elsewhere. 2. A complete pyproject.toml: [build-system] naming a specific backend (hatchling or setuptools>=68 — say which and why), [project] with name, version (matching Semantic versioning starting at 0.1.0, bumped manually per release, no pre-1.0 API stability guarantee.), requires-python, dependencies pinned with sensible lower bounds, and [project.optional-dependencies] for dev/test tooling (pytest, ruff, mypy) kept separate from runtime deps. 3. If this needs a CLI entry point, define it under [project.scripts]. 4. A .gitignore covering build artifacts (dist/, *.egg-info, __pycache__/) and a minimal README.md with install and usage instructions matching internal use via a private index, not public PyPI. 5. If internal use via a private index, not public PyPI is public PyPI, flag what else is needed before publishing — a unique name check, a license file, a long_description sourced from the README, a decision on whether Semantic versioning starting at 0.1.0, bumped manually per release, no pre-1.0 API stability guarantee. needs a pre-release tag for the first publish — rather than assuming it's ready to go. 6. Decide explicitly whether package data beyond .py files (a bundled JSON schema, a template file, static assets) needs to ship inside the wheel — if A small library called "orderkit" for parsing and validating e-commerce order exports. mentions any non-code file the package reads at runtime, configure include-package-data or the backend's equivalent, since a file that exists in the source tree but isn't declared as package data silently vanishes from the built wheel and only fails the first time a real install tries to read it. 7. Confirm requires-python is set to a version floor the code can actually run on, not a value copied from habit — if any syntax or standard-library feature used in the project needs a specific minimum version (structural pattern matching needs 3.10, for instance), the floor must match or be raised, so pip refuses an incompatible install up front instead of failing at import time on an older interpreter. 8. State the exact commands to build and locally verify the package installs cleanly: build it, install the built wheel into a fresh throwaway virtual environment, and import it, before it's ever pushed anywhere. OUTPUT FORMAT 1. The file tree, including any non-code files that need to ship as package data. 2. The full pyproject.toml. 3. The build-and-verify commands, in order. 4. One sentence confirming requires-python actually matches the language features the code uses.
Customize
Optional — swap in your own details for the highlighted parts above.
Why this works
Requiring the src/ layout by name matters because it prevents the single most common packaging bug: a package that imports fine during development purely because the current working directory happens to be on sys.path, then fails on a genuinely clean install because a missing __init__.py or a broken relative import was never actually exercised until now — the bug hides precisely as long as nobody tries to use the package the way an external consumer would. Requiring the build backend to be named and justified, rather than left to the model's default, matters because pyproject.toml's [build-system] table is exactly the part that varies most across tutorials of different vintages — an unconstrained prompt is as likely to produce a stale setup.py-based flow as a modern one, and the two are not interchangeable once a CI pipeline depends on one specific build command succeeding. The versioning_scheme field earns its place because getting this wrong has a real consequence for consumers: a package that bumps its version number inconsistently, or publishes a breaking change under a patch version, breaks every downstream project that pinned a version range trusting semver, and that trust is expensive to rebuild once broken — naming the scheme up front is what keeps the [project] version field meaningful rather than an arbitrary string. Requiring the fresh-venv install-and-import step as an explicit output, not just the config file, is what catches "works on my machine" packaging bugs — a dependency that was actually already installed globally, or a file that exists locally but was never added to the package manifest — before they reach a real install anywhere else, which is exactly the class of bug a packaging task exists to prevent. The package-data requirement targets a related but distinct trap: a file sitting right next to the Python source in the repository is not automatically included in a built wheel unless the build backend is explicitly told to include it, so a template or schema file the code reads at import time can work perfectly in every local test — because the source tree is right there on disk — and then raise FileNotFoundError the moment the package is actually installed from its built artifact somewhere else, which is precisely the "works everywhere except the one place that matters" bug a packaging pass exists to catch before a user does.
Verified against
ChatGPT GPT-5.1 · 2026-07-27
Claude Code Sonnet 4.6 · 2026-07-28
Changelog
- 2026-07-28 — Initial publish, verified against ChatGPT (GPT-5.1) and Claude Code (Sonnet 4.6) using uv 0.5 for the build/verify step.
Need this built into your business?
If a prompt isn't enough — custom software, built and maintained for you — that's Scult's day job.
EXPLORE CUSTOM SOFTWARE
