After this lesson, you will be able to: Structure a Python project, configure pyproject.toml, and publish to PyPI.
Shipping a package is the final test of mastery. PyPI accepts in 10 minutes once you know the recipe.
src/ layout + pyproject.toml is the 2026 standard.
myproject/├── pyproject.toml├── README.md├── LICENSE├── src/│ └── myproject/│ ├── __init__.py│ ├── core.py│ └── cli.py└── tests/└── test_core.py# pyproject.toml[project]name = "myproject"version = "0.1.0"description = "A demo package"requires-python = ">=3.10"dependencies = ["httpx", "click"][project.scripts]myproject = "myproject.cli:main"[project.optional-dependencies]dev = ["pytest", "ruff", "mypy"][build-system]requires = ["hatchling"]build-backend = "hatchling.build"
Two commands once configured.
# Install build toolspip install build twine# Build the distributionpython -m build# Generates dist/myproject-0.1.0.tar.gz + dist/myproject-0.1.0-py3-none-any.whl# Test install locallypip install -e .# Upload to TEST PyPI first (sanity check)twine upload --repository testpypi dist/*# Then real PyPItwine upload dist/*# Get a PyPI account at pypi.org, generate an API token, save to ~/.pypirc or pass with --user/--password
Use semantic versioning: MAJOR.MINOR.PATCH. 0.x.y for unreleased / exploratory. 1.0.0 when API is stable. Bump MAJOR for breaking changes. Tools like hatch or bump-my-version automate the bump + tag + push.
Putting code in the project root instead of src/. Causes import shadowing during tests. Publishing without a LICENSE. PyPI accepts but nobody can legally use it. Skipping TEST PyPI. First real publish is final; mistakes are public. Hardcoded version strings in 5 places. Single source: pyproject.toml.
Pick the cleanest answer.
Sign in and purchase access to unlock this lesson.