A small example package
Project description
Packaging Python Projects
This tutorial walks you through how to package a simple Python project. It will show you 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).
Tip
If you have trouble running the commands in this tutorial, please copy the command and its output, then open an issue on the packaging-problems repository on GitHub. We'll do our best to help you!
Some of the commands require a newer version of pip, so start by making sure you have the latest version installed:
Unix/macOS:
python3 -m pip install --upgrade pip
Windows:
py -m pip install --upgrade pip
A simple project
This tutorial uses a simple project named example_package_YOUR_USERNAME_HERE. If your username is me, then the package would be example_package_me; this ensures that you have a unique package name that doesn't conflict with packages uploaded by other people following this tutorial. We recommend following this tutorial as-is using this project, before packaging your own project.
Create the following file structure locally:
packaging_tutorial/
└── src/
└── example_package_YOUR_USERNAME_HERE/
├── __init__.py
└── example.py
The directory containing the Python files should match the project name. This simplifies the configuration and is more obvious to users who install the package.
Creating the file __init__.py is recommended because the existence of an __init__.py file allows users to import the directory as a regular package, even if (as is the case in this tutorial) __init__.py is empty.
[See note on namespace packages below.]
example.py is an example of a module within the package that could contain the logic (functions, classes, constants, etc.) of your package. Open that file and enter the following content:
def add_one(number):
return number + 1
If you are unfamiliar with Python’s modules and import packages, take a few minutes to read over the Python documentation for packages and modules.
Once you create this structure, you'll want to run all of the commands in this tutorial within the packaging_tutorial directory.
Creating the package files
You will now add files that are used to prepare the project for distribution. When you're done, the project structure will look like this:
packaging_tutorial/
├── LICENSE
├── pyproject.toml
├── README.md
├── src/
│ └── example_package_YOUR_USERNAME_HERE/
│ ├── __init__.py
│ └── example.py
└── tests/
Creating a test directory
tests/ is a placeholder for test files. Leave it empty for now.
Choosing a build backend
Tools like pip and build do not actually convert your sources into a distribution package (like a wheel); that job is performed by a build backend. The build backend determines how your project will specify its configuration, including metadata (information about the project, for example, the name and tags that are displayed on PyPI) and input files.
You can choose from a number of backends; this tutorial uses Hatchling by default, but it will work identically with setuptools, Flit, PDM, and others that support the [project] table for metadata.
Note
Some build backends are part of larger tools that provide a command-line interface with additional features like project initialization and version management, as well as building, uploading, and installing packages. This tutorial uses single-purpose tools that work independently.
The pyproject.toml tells build frontend tools like pip and build which backend to use for your project. Below are some examples for common build backends, but check your backend's documentation for more details.
The requires key is a list of packages that are needed to build your package. The frontend should install them automatically when building your package. This should always include your backend’s package, and might have other build-time dependencies.
The build-backend key is the name of the Python object that frontends will use to perform the build.
Additional configuration of the build tool will either be in a [tool] section of the pyproject.toml, or in a special file defined by the build tool. For example, when using setuptools, additional configuration may be added to a setup.py or setup.cfg file.
Configuring metadata
Open pyproject.toml and enter the following content. Change the name to include your username.
[project]
name = "example_package_YOUR_USERNAME_HERE"
version = "0.0.1"
authors = [
{ name="Example Author", email="author@example.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"
Issues = "https://github.com/pypa/sampleproject/issues"
Explanation of fields
name: The distribution name of your package. Must be unique on PyPI.version: The package version.authors: A list of authors with name and email.description: Short summary of the package.readme: Path to a README file used as long description.requires-python: The range of Python versions supported.classifiers: Metadata used by PyPI and tools like pip.license: SPDX license identifier.license-files: Glob pattern to include license files.urls: Additional links shown on PyPI.
See the pyproject.toml guide for more details.
Creating README.md
Open README.md and enter:
# Example Package
This is a simple example package. You can use
[GitHub-flavored Markdown](https://guides.github.com/features/mastering-markdown/)
to write your content.
Creating a LICENSE
It’s important for every package uploaded to PyPI to include a license. For help picking a license, see https://choosealicense.com/.
For example, the MIT license:
Copyright (c) 2018 The Python Packaging Authority
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction...
Most build backends include license files automatically. If you define license-files in pyproject.toml and your backend supports PEP 639, the file
Including other files
The files listed above will be included automatically in your source distribution. If you want to include additional files, see the documentation for your build backend.
Generating distribution archives
The next step is to generate distribution packages for the package. These are archives that are uploaded to the Python Package Index and can be installed by pip.
Make sure you have the latest version of PyPA’s build installed:
Unix/macOS:
python3 -m pip install --upgrade build
Windows:
py -m pip install --upgrade build
Tip
If you have trouble installing these, see the installing packages tutorial.
Now run this command from the same directory where pyproject.toml is located:
Unix/macOS:
python3 -m build
Windows:
py -m build
This command should output a lot of text and once completed should generate two files in the dist directory:
dist/
├── example_package_YOUR_USERNAME_HERE-0.0.1-py3-none-any.whl
└── example_package_YOUR_USERNAME_HERE-0.0.1.tar.gz
The .tar.gz file is a source distribution and the .whl file is a built distribution. Newer versions of pip prefer built distributions but will fall back to source if needed.
You should always upload a source distribution and provide built distributions for the platforms your project supports. Our example package is compatible with all platforms, so only one wheel is needed.
Uploading the distribution archives
Finally, it’s time to upload your package to the Python Package Index!
First, register an account on TestPyPI. This is a test instance of PyPI used for experimentation.
Note
You must verify your email address before uploading packages.
To securely upload your project, you’ll need a PyPI API token. Create one at https://test.pypi.org/manage/account/#api-tokens with "Scope" set to "Entire account".
Copy and save the token—you won’t see it again.
Install twine:
Unix/macOS:
python3 -m pip install --upgrade twine
Windows:
py -m pip install --upgrade twine
Use Twine to upload the archives in dist:
Unix/macOS:
python3 -m twine upload --repository testpypi dist/*
Windows:
py -m twine upload --repository testpypi dist/*
You’ll be prompted for an API token. Paste the token including the pypi- prefix. The input will be hidden.
Example output:
Uploading distributions to https://test.pypi.org/legacy/
Enter your API token:
Uploading example_package_YOUR_USERNAME_HERE-0.0.1-py3-none-any.whl
100% ━━━━━━━━━━━━━━━━━━━━━━ 8.2/8.2 kB • 00:01
Uploading example_package_YOUR_USERNAME_HERE-0.0.1.tar.gz
100% ━━━━━━━━━━━━━━━━━━━━━━ 6.8/6.8 kB • 00:00
Your package should now be viewable on TestPyPI, for example:
https://test.pypi.org/project/example_package_YOUR_USERNAME_HERE
Installing your newly uploaded package
You can use pip to install your package from TestPyPI and verify that it works. Create a virtual environment and install your package:
Unix/macOS:
python3 -m pip install --index-url https://test.pypi.org/simple/ --no-deps example-package-YOUR-USERNAME-HERE
Windows:
py -m pip install --index-url https://test.pypi.org/simple/ --no-deps example-package-YOUR-USERNAME-HERE
Make sure to replace YOUR-USERNAME-HERE with your actual username.
You should see output like:
Collecting example-package-YOUR-USERNAME-HERE
Downloading https://test-files.pythonhosted.org/packages/.../example_package_YOUR_USERNAME_HERE_0.0.1-py3-none-any.whl
Installing collected packages: example_package_YOUR_USERNAME_HERE
Successfully installed example_package_YOUR_USERNAME_HERE-0.0.1
Note
The--index-urlflag tellspipto use TestPyPI. The--no-depsflag avoids installing dependencies from TestPyPI (which may not be available).
To test your installed package, start Python and import it:
Unix/macOS:
python3
Windows:
py
In the Python shell:
>>> from example_package_YOUR_USERNAME_HERE import example
>>> example.add_one(2)
3
Next steps
Congratulations, you've packaged and distributed a Python project! ✨🍰✨
Keep in mind TestPyPI is for experimentation. Packages and accounts may be deleted. When you're ready to publish to the real PyPI:
- Choose a unique, memorable name for your package.
- Register an account at https://pypi.org.
- Upload your package with
twine upload dist/*. - Install your package from real PyPI using
python3 -m pip install [your-package].
Want to learn more?
-
Advanced configuration for build backends:
-
Explore more guides:
Packaging guides,
Discussions -
Consider using a project manager like
hatch,flit,pdm, orpoetry.
Notes
Note
You can technically create packages without an__init__.pyfile, called namespace packages. These are considered an advanced topic and not covered in this tutorial. For beginners, it’s recommended to use regular packages with__init__.py, even if it's empty.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file example_package_rsmahmud-0.0.3.tar.gz.
File metadata
- Download URL: example_package_rsmahmud-0.0.3.tar.gz
- Upload date:
- Size: 6.9 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.12.6
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
624bca4403974080d03715273a845bc80c9c7bffeede5913fffad3005f178fd6
|
|
| MD5 |
2459394ae7628618a9c6cff67bc7390b
|
|
| BLAKE2b-256 |
bceb6cf9e0a90aa611f75c687fb24569c931e9dbf9afdd8a6dbe8f9c38e1d5f9
|
File details
Details for the file example_package_rsmahmud-0.0.3-py3-none-any.whl.
File metadata
- Download URL: example_package_rsmahmud-0.0.3-py3-none-any.whl
- Upload date:
- Size: 7.3 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.12.6
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
9a453b418ca7b0d6c7eab1869e061aa529e29058e72876ea0db2cc2d6e4538e2
|
|
| MD5 |
7fbfda25befffa42b479f4a5b9186afb
|
|
| BLAKE2b-256 |
1a3f59bd2cc33b5b9902e92dc63c15fa1dec94afabe142819e4c1e34066cc0fd
|