Skip to content

distribution

At some point, you’ll want to share your code, either with teammates inside your organization or with the wider Python community. A thoughtful distribution strategy makes it easy for others to install, upgrade, and use your project. It also helps you manage versions, dependencies, and metadata in a predictable manner, rather than relying on ad hoc scripts and manual instructions.

Modern Python packaging relies on the pyproject.toml file, which describes how your project is built and provides standard metadata, such as the project name, version, dependencies, entry points, and more. While some tools still support configuration in other files, pyproject.toml is now the central, standardized place for packaging configuration.

When it comes to packaging and distributing your Python projects, you should apply some of the best practices below:

  • Use pyproject.toml as the primary source of packaging configuration. Declare your project’s build settings and core metadata in pyproject.toml rather than in imperative setup.py scripts or scattered configuration files, and avoid invoking setup.py directly as a command-line tool, such as with python setup.py install. This practice aligns with modern packaging standards like PEP 517 and the pyproject.toml specification, and it makes your build configuration easier to understand and maintain.
  • Choose a modern, standard-compliant build backend. Use a backend such as setuptools, Hatch (via hatchling), or poetry-core in your pyproject.toml. These backends implement modern packaging standards and allow other developers and tools to build your project without custom scripts. Typically, you choose one backend per project.
  • Version and tag releases consistently. Follow a clear versioning scheme like semantic versioning, keep your version identifiers compliant with the version specifiers specification (originally PEP 440), and tag releases in your version control system. This practice helps users know when you’ve introduced new features, bug fixes, or breaking changes.
  • Be responsible about publishing to PyPI. Double-check metadata, dependencies, and licensing before uploading your packages to the Python Package Index (PyPI). Use a test index like TestPyPI to verify that your builds and installation instructions work as expected.
  • Document how to install your project. Provide copy-pasteable installation commands and clearly document any extra setup steps users need to take.

To see the difference that modern packaging makes, compare the following two approaches:

🔴 Avoid this:

The project layout:

resize-images/
└── resize_images.py

The source code:

Language: Python Filename: resize_images.py
import sys
from pathlib import Path

def main():
    source = Path(sys.argv[1])
    target = Path(sys.argv[2])
    # Resize images logic here...

if __name__ == "__main__":
    main()

The installation and usage instructions:

Language: Shell
$ git clone https://github.com/user/resize-tool.git
$ cd resize-tool/
$ python resize_images.py pictures/ thumbnails/

This project works, but it’s fragile. Users must clone the repository, navigate to the correct directory, and run the script directly with the python command. The project doesn’t declare standard metadata such as its name, version, or dependencies, and it can’t be installed or invoked using standard Python packaging tools or entry points.

Favor this:

The project layout:

resize-images/
├── src
│   └── image_tools
│       ├── __init__.py
│       └── cli.py
└── pyproject.toml

Using the src-layout helps prevent accidental imports from the project root during development and more closely matches how users will import your package once it’s installed.

The source code:

Language: Python
import argparse
from pathlib import Path

def main() -> None:
    parser = argparse.ArgumentParser(
        description="Resize images from source into target folder."
    )
    parser.add_argument("source", type=Path)
    parser.add_argument("target", type=Path)
    args = parser.parse_args()
    # Resize images logic here...

The project’s metadata and build configuration:

Language: TOML Filename: pyproject.toml
[build-system]
requires = ["setuptools>=61.0"]
build-backend = "setuptools.build_meta"

[project]
name = "image-tools"
version = "0.1.0"
dependencies = ["Pillow"]

[project.scripts]
resize-images = "image_tools.cli:main"

The installation and usage instructions:

Language: Shell
(venv) $ python -m pip install .
(venv) $ resize-images pictures/ thumbnails/

In this version, your project has a well-structured layout, declares its dependencies and metadata explicitly, and exposes a user-friendly command-line interface via an entry point. Users can install it into a virtual environment using standard tools and invoke it just like any other command-line program.

How to Publish an Open-Source Python Package to PyPI

Tutorial

How to Publish an Open-Source Python Package to PyPI

In this step-by-step tutorial, you’ll learn how to create a Python package for your project and how to publish it to PyPI, the Python Package Repository. Quickly get up to speed on everything from naming your package to configuring it using setup.cfg.

intermediate best-practices tools

For additional information on related topics, take a look at the following resources:


By Leodanis Pozo Ramos • Updated Sept. 5, 2026 • Reviewed by Brenda Weleschuk and Bartosz Zaczyński