Code 28: Python Project Packaging & Distribution

How to Package & Distribute a Python Project
python
Author

Tony Phung

Published

May 7, 2025

0. Goals

Final Product - A Python Project Packaged and Distributed to PiPY (Test):

  • https://test.pypi.org/project/example-package-tonyjustdevs/0.0.1/

How to package a simple Python project:

  • how to add the necessary files and structure to create the package,
  • how to build the package, and
  • how to upload it to the Python Package Index (PyPI).

Firstly, update pip:

  • python3 -m pip install --upgrade pip

1. A Simple Project

Project name: example_package_YOUR_USERNAME_HERE

  • If username is me, then example_package_me
  • or example_package_tonyjustdevs

2. File Stucture

Create the following file structure locally:

packaging_tutorial/
└── src/
    └── example_package_YOUR_USERNAME_HERE/
        ├── __init__.py
        └── example.py

or

packaging_tutorial/
└── src/
    └── example_package_tonyjustdevs/
        ├── __init__.py
        └── example.py

The directory of files be same as the project name in toml file

Creating __init__.py:

  • allows users to import directory as a regular package

example.py is a module:

  • module it may contain:
  • logic (functions, classes, constants, etc.) of package
# example.py
def add_one(number):
    return number + 1

3. Creating Package Files

packaging_tutorial/
├── LICENSE
├── pyproject.toml
├── README.md
├── src/
│   └── example_package_YOUR_USERNAME_HERE/
│       ├── __init__.py
│       └── example.py
└── tests/

or

packaging_tutorial/
├── LICENSE
├── pyproject.toml
├── README.md
├── src/
│   └── example_package_tonyjustdevs/
│       ├── __init__.py
│       └── example.py
└── tests/

4. Choosing a Build-Backend

4.1 pip & build versus build-backend

Convert sources into distribution packages:

  • build-backend does
  • pip & build does not

The build-backend determines:

  • Project configuration specifics
    • metadata (name, tags etc on PyPI)
    • input files.
  • Different functionalities
    • Single purpose tools or multi-purple tools
    • Support building extension modules or not
    • Command-line interfaces
    • Project Initialisation
    • Version Management
    • Building, Uploading & Installing packages.

4.2 Hatchling Example

[build-system]
requires = ["hatchling >= 1.26"]
build-backend = "hatchling.build"

4.3 requires key is a:

List of packages that are needed to build your package:

  • backend’s package,
  • other build-time dependencies.
    • build-time errors if dependencies ommited here

4.4 build-frontend

The build-frontend (e.g. pip, build) automatically install list of packagies when building your package, is tool run by user

Input:

  • arbitrary source trees or source distributions

Builds in isolaated environments (typically):

  • source distributions or wheels from them.
  • actual building is delegated to each source tree’s build-backend.

4.5 build-backend

The build-backend key is the:

  • name of Python object
  • frontends will use to perform the build.

Values from:

  • build backend tool documentation, or
  • generated by its command line interface.
  • No need for Author to customize these settings.

4.6 Additional Configurations of Build Tool

  • tool section of pyproject.toml or in a
  • Special file defined by the build tool.
    • E.g. setuptools additional configuration added to:
    • setup.py or setup.cfg file, and specifying setuptools.build_meta in your build allows the tools to locate and use these automatically.

5. Configuring Metadata

[project]
    name = "example_package_tonyjustdevs"
    version = "0.0.1"
    authors = [
        { name="Tony JustDevs", email="tonyjustdevs@gmail.com" },
        ]
    description = "A small example package"
    readme = "README.md"
    requires-python = ">=3.9"
    classifiers = [
            "Programming Language :: Python :: 3",
            "Operating System :: OS Independent",
        ]
    license = "MIT"
    license-files = ["LICEN[CS]E*"]

[project.urls]
    Homepage = "https://github.com/pypa/sampleproject_tonyjustdevs"
    Issues = "https://github.com/pypa/sampleproject_tonyjustdevs/issues"

6. Update README.md & LICENSE

README Reference

LICENSE Reference

7. Generating Distribution Archives

These archives are:

  • uploaded to PyPI
  • installed by pip

Update build:

  • python3 -m pip install --upgrade build

Build command in pyproject.toml directory:

  • python3 -m build

Sample Files Generated:

    dist/
    ├── example_package_YOUR_USERNAME_HERE-0.0.1-py3-none-any.whl
    └── example_package_YOUR_USERNAME_HERE-0.0.1.tar.gz

tar.gz file is a source distribution .whl file is a built distribution.

Recommended:

  • Always upload a source distribution (tar.gz)
  • Provide built distributions (.whl) for the platforms project is compatible with.

8. Upload to Distribution Archives

Upgrade or Install twine:

  • python3 -m pip install --upgrade twine
  • python3 -m pip install twine --user

Register Test PyPI:

  • https://test.pypi.org/account/register/

PyPI API token:

  • https://test.pypi.org/manage/account/#api-tokens

Use API token:

  • Create $HOME/.pypirc file like this:

      [testpypi]
      username = __token__
      password = ACTUAL_API_TOKEN_FROM_TEST_PYPI

Run twine command to upload archives under dist:

  • python3 -m twine upload --repository testpypi dist/*

Update their CI configuration file (e.g. .travis.yml if you are using Travis).

Reference Link

Upgrade twine

  • python3 -m pip install --upgrade twine

Run Twine to upload archives:

  • python3 -m twine upload --repository testpypi dist/*

9. Installing your newly uploaded package

Test Package Uploaded & Created in Test PyPI:

  • https://test.pypi.org/project/example-package-tonyjustdevs/0.0.1/

Create venv and install:

  • python3 -m venv .venv && source .venv/bin/activate
  • pip install -i https://test.pypi.org/simple/ --no-deps example-package-tonyjustdevs==0.0.1

or Generically:

  • python3 -m pip install --index-url https://test.pypi.org/simple/ --no-deps example-package-YOUR-USERNAME-HERE

10. Import Package

  • from example_package_tonyjustdevs import example
  • example.add_one(11)

My Python project has been packaged and distributed successfully!

11. Development Workflow

11. Dev Process

  • git clone
  • or pip install -e git+https://github.com/numpy/numpy.git@v1.25.0#egg=numpy
  • or pip install -e git+https://github.com/owner/repo.git@branch#egg=package
  • or pip install -e git+https://@github.com/owner/repo.git@branch#egg=package_name
  • or pip install -e git+ssh://git@github.com/owner/repo.git@main#egg=private_pkg
  • add venv
  • pip install -e .
  • or pip install -e “.[dev]”
  • python -c “import my_package; print(my_package.__file__)”
  • git commit -am “Prepare release v…”
  • git tag -a v1.0.0 -m “Release v…”
  • or git push origin main –tags
  • or git push origin v…
  • git push origin main –tags
  • rm -rf dist
  • [if not in backend build] pip install build twine
  • [if not in backend build] python -m build
  • twine upload -r testpypi dist/*
  • pip install dist/my_package-v0.0.0.whl
  • python -c “import my_package; print(my_package.__file__)
  • pip install -e .