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.tomlas the primary source of packaging configuration. Declare your project’s build settings and core metadata inpyproject.tomlrather than in imperativesetup.pyscripts or scattered configuration files, and avoid invokingsetup.pydirectly as a command-line tool, such as withpython setup.py install. This practice aligns with modern packaging standards like PEP 517 and thepyproject.tomlspecification, 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 yourpyproject.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:
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:
$ 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:
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:
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:
(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.
Related Resources
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.
For additional information on related topics, take a look at the following resources:
- How to Manage Python Projects With pyproject.toml (Tutorial)
- What Are Python Wheels and Why Should You Care? (Tutorial)
- Everyday Project Packaging With pyproject.toml (Course)
- Python's zipapp: Build Executable Zip Applications (Tutorial)
- Using PyInstaller to Easily Distribute Python Applications (Tutorial)
- Managing Python Projects With uv: An All-in-One Solution (Tutorial)
- Using Python's pip to Manage Your Projects' Dependencies (Tutorial)
- How to Publish Your Own Python Package to PyPI (Course)
- Publishing Python Packages to PyPI (Course)
- How to Publish an Open-Source Python Package to PyPI (Quiz)
- How to Manage Python Projects With pyproject.toml (Quiz)
- Python Project Management With uv (Course)
- Managing Python Projects With uv: An All-in-One Solution (Quiz)
- A Beginner's Guide to pip (Course)
- Using Python's pip to Manage Your Projects' Dependencies (Quiz)