Packaging turns your Python code into installable, shareable modules that others can pip install.

Project Structure

A standard layout:

  myproject/
├── pyproject.toml
├── README.md
├── LICENSE
├── src/
│   └── mypackage/
│       ├── __init__.py
│       └── core.py
└── tests/
    └── test_core.py
  

The src/ layout prevents accidentally importing from the local directory instead of the installed package.

pyproject.toml

Modern Python projects use pyproject.toml as the single configuration file:

  [build-system]
requires = ["setuptools>=68.0", "wheel"]
build-backend = "setuptools.build_meta"

[project]
name = "mypackage"
version = "0.1.0"
description = "A useful Python package"
readme = "README.md"
requires-python = ">=3.10"
license = {text = "MIT"}
authors = [{name = "Your Name", email = "[email protected]"}]
dependencies = [
    "requests>=2.28",
]

[project.optional-dependencies]
dev = ["pytest>=7.0", "mypy>=1.0"]

[project.scripts]
mycli = "mypackage.cli:main"

[tool.setuptools.packages.find]
where = ["src"]
  

Building the Package

  pip install build
python -m build
  

This creates dist/mypackage-0.1.0.tar.gz and dist/mypackage-0.1.0-py3-none-any.whl.

Installing Locally

  # Editable install (changes reflect immediately)
pip install -e ".[dev]"

# From a built wheel
pip install dist/mypackage-0.1.0-py3-none-any.whl
  

Publishing to PyPI

  1. Create accounts at PyPI and TestPyPI.
  2. Install twine: pip install twine
  3. Upload:
  # Test first
twine upload --repository testpypi dist/*

# Production
twine upload dist/*
  

Use API tokens instead of passwords for authentication.

Project Metadata and Classifiers

Help users discover your package on PyPI with classifiers:

  [project]
classifiers = [
    "Development Status :: 4 - Beta",
    "Intended Audience :: Developers",
    "License :: OSI Approved :: MIT License",
    "Programming Language :: Python :: 3",
    "Programming Language :: Python :: 3.10",
    "Programming Language :: Python :: 3.11",
    "Programming Language :: Python :: 3.12",
    "Topic :: Software Development :: Libraries",
]
keywords = ["utilities", "cli", "automation"]
  

CHANGELOG and Version Bumping

Maintain a CHANGELOG.md following Keep a Changelog:

  ## [0.2.0] - 2026-06-13
### Added
- New `fetch_all()` function for batch requests

### Fixed
- Handle empty response bodies
  

Bump version in pyproject.toml before each release. Automate with bump2version or Poetry’s version command.

Including Non-Python Files

Package data files (templates, config defaults) via pyproject.toml:

  [tool.setuptools.package-data]
mypackage = ["templates/*.html", "data/*.json"]
  

Or use MANIFEST.in for setuptools:

  include LICENSE
include README.md
recursive-include src/mypackage/templates *.html
  

CI Publishing with GitHub Actions

  # .github/workflows/publish.yml
name: Publish to PyPI
on:
  release:
    types: [published]
jobs:
  publish:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-python@v5
        with:
          python-version: "3.12"
      - run: pip install build twine
      - run: python -m build
      - run: twine upload dist/*
        env:
          TWINE_USERNAME: __token__
          TWINE_PASSWORD: ${{ secrets.PYPI_API_TOKEN }}
  

Always test on TestPyPI before your first production release.

Semantic Versioning

Follow SemVer: MAJOR.MINOR.PATCH

  • MAJOR — breaking API changes
  • MINOR — new features, backward compatible
  • PATCH — bug fixes, backward compatible

Entry Points and CLI Tools

Define command-line scripts in pyproject.toml:

  [project.scripts]
mytool = "mypackage.cli:main"
  
  # src/mypackage/cli.py
def main():
    print("Hello from mytool!")
  

After pip install, users can run mytool from anywhere.

Checklist Before Publishing

  • Tests pass (pytest)
  • Type checks pass (mypy)
  • README has install and usage instructions
  • LICENSE file included
  • Version bumped in pyproject.toml
  • No secrets or credentials in the code
  • Tested on TestPyPI first

Publishing packages is how you contribute reusable tools to the Python ecosystem.