A Beginner's Guide to Packaging Python Code
A step by step tutorial on packaging your Python Code
Level Up Your Python Code: A Beginner’s Guide to Packaging
So, you’ve written some awesome Python code! Maybe it’s a helpful utility script, a set of reusable functions, or even a small library. You can run it on your machine, which is great, but what if you want to:
- Easily reuse it across different projects?
- Share it with colleagues or friends?
- Contribute it to the open-source community?
- Install it reliably using
pip
?
The answer is packaging! Packaging bundles your code, metadata (like version, author, dependencies), and instructions so it can be easily distributed, installed, and managed.
This guide will walk you through the modern, standard way to package your Python code using pyproject.toml
and standard tools like build
and twine
. Let’s get started!
Prerequisites:
- Python installed (including
pip
). - Basic familiarity with the command line/terminal.
- Some Python code you want to package!
Our Example Project: mypackage
Let’s imagine we have a simple project called mypackage
with a single function that greets users.
Step 1: Structure Your Project
A standard project structure helps tools find your code and metadata. Here’s a recommended layout:
1
2
3
4
5
6
7
8
mypackage_project/
├── src/
│ └── mypackage/
│ ├── __init__.py
│ └── greeter.py
├── pyproject.toml
├── README.md
└── LICENSE
mypackage_project/
: The root directory containing everything related to your package.src/
: A directory containing the actual Python source code. Usingsrc/
prevents common import issues.mypackage/
: This is your actual Python package directory. The name you import (e.g.,import mypackage
).__init__.py
: Makes Python treat themypackage
directory as a package. Can be empty or used to define package-level items.greeter.py
: A Python module within your package containing our example code.pyproject.toml
: The most important file! This defines your project metadata and build system requirements. (We’ll create this next).README.md
: Describes your project (what it does, how to install, how to use). Written in Markdown.LICENSE
: Contains the license under which you distribute your code (e.g., MIT, Apache 2.0). Choose one using sites like choosealicense.com.
Step 2: Write Your Code
Let’s put our simple greeting function into src/mypackage/greeter.py
:
1
2
3
4
# src/mypackage/greeter.py
def say_hello(name="World"):
"""Greets the given name."""
return f"Hello, {name}!"
And make it easily accessible from the package level in src/mypackage/__init__.py
:
1
2
3
4
# src/mypackage/__init__.py
from .greeter import say_hello
__version__ = "0.1.0" # Define package version here
Step 3: Create pyproject.toml
This file is the heart of modern Python packaging. It tells build tools how to build your package and provides metadata for package indexes like PyPI (the Python Package Index).
Create pyproject.toml
in your project root (mypackage_project/
) with the following content:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
# pyproject.toml
[build-system]
requires = ["setuptools>=61.0"] # Minimum version of the build backend
build-backend = "setuptools.build_meta"
[project]
name = "mypackage-tutorial" # Distribution name (must be unique on PyPI)
version = "0.1.0" # Your package version
authors = [
{ name="Your Name", email="your.email@example.com" },
]
description = "A simple example package for a tutorial"
readme = "README.md" # Path to your README file
requires-python = ">=3.8" # Minimum Python version required
classifiers = [
"Programming Language :: Python :: 3",
"License :: OSI Approved :: MIT License", # Match your LICENSE file
"Operating System :: OS Independent",
]
dependencies = [
# List any external packages your code needs here, e.g.:
# "requests>=2.20.0",
# "numpy",
]
[project.urls] # Optional: Links for users
"Homepage" = "https://github.com/yourusername/mypackage_project"
"Bug Tracker" = "https://github.com/yourusername/mypackage_project/issues"
Key pyproject.toml
sections:
[build-system]
: Specifies the tools needed to build your package.setuptools
is the most common build backend.[project]
: Contains metadata about your package:name
: The distribution name used on PyPI (hyphens are common). Must be unique!version
: Your package version (see PEP 440 for versioning guidelines). Often synced with__version__
in__init__.py
.authors
: Your name and email.description
: A short summary.readme
: Points to your README file for the long description on PyPI.requires-python
: Specifies compatible Python versions.classifiers
: Helps users find your package on PyPI (browse classifiers here).dependencies
: Crucial! Lists other packages your code needs to run. Pip will automatically install these when your package is installed.
[project.urls]
: Optional links displayed on your PyPI project page.
Step 4: Write Your README.md
Create a README.md
file in the project root. Explain what your package does, how to install it (pip install mypackage-tutorial
), and provide a basic usage example.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
# My Package Tutorial
A simple example package created for a tutorial.
## Installation
pip install mypackage-tutorial
## Usage
```python
import mypackage
print(mypackage.say_hello("Pythonista"))
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
---
**Step 5: Add a `LICENSE` File**
Choose an open-source license (MIT is common and permissive) from [choosealicense.com](https://choosealicense.com/), copy the text into a file named `LICENSE` in your project root, and update the year and copyright holder name. Make sure the `classifiers` list in `pyproject.toml` reflects your chosen license.
---
**Step 6: Set Up a Virtual Environment (Highly Recommended!)**
Always work within a virtual environment to isolate project dependencies.
```bash
# Navigate to your project root directory
cd mypackage_project
# Create a virtual environment (named .venv here)
python -m venv .venv
# Activate it
# On Windows (cmd.exe)
# .venv\Scripts\activate.bat
# On Windows (PowerShell)
# .venv\Scripts\Activate.ps1
# On macOS/Linux (bash/zsh)
source .venv/bin/activate
# Your terminal prompt should now show (.venv)
Step 7: Install Build Tools
You need tools to build your package distribution files and upload them.
1
pip install build twine
build
: The tool that actually builds your package from your source code andpyproject.toml
.twine
: The tool used to securely upload your built package to PyPI.
Step 8: Build Your Package
Now, run the build
command in your project root directory:
1
python -m build
This command will:
- Read your
pyproject.toml
. - Create a
dist/
directory if it doesn’t exist. - Generate two files inside
dist/
:mypackage_tutorial-0.1.0-py3-none-any.whl
: A wheel file. This is the standard, pre-built distribution format. Pip prefers installing wheels as they are faster.mypackage_tutorial-0.1.0.tar.gz
: A source distribution (sdist). This contains your source code and instructions on how to build it. It’s a fallback if a wheel isn’t available for the user’s system.
Step 9: (Optional but Recommended) Test Local Installation
Before uploading, test if you can install your package locally from the built wheel file:
1
2
3
4
5
6
7
8
9
10
11
12
# Make sure you are in your project root with the virtual env active
pip install dist/mypackage_tutorial-0.1.0-py3-none-any.whl
# Now try importing and using it in a Python interpreter
python
>>> import mypackage
>>> mypackage.say_hello("Developer")
'Hello, Developer!'
>>> exit()
# Uninstall it before proceeding to upload
pip uninstall mypackage-tutorial
Step 10: Upload to TestPyPI (Practice First!)
It’s crucial to test the upload process using TestPyPI, a separate instance of the package index for testing.
- Register Accounts: Create free accounts on both TestPyPI and the real PyPI.
- Create API Tokens: For uploading, use API tokens instead of your username and password.
- Go to your Account Settings on TestPyPI.
- Scroll down to “API tokens” and click “Add API token”.
- Give it a name (e.g.,
mypackage-upload
) and set the Scope to “Entire account”. - Copy the token immediately! You won’t see it again.
Upload to TestPyPI: Use
twine
to upload the files from yourdist/
directory. When prompted for the username, enter__token__
. When prompted for the password, paste your TestPyPI API token.1
twine upload --repository testpypi dist/*
Step 11: Install from TestPyPI
Verify your package works by installing it from TestPyPI:
1
2
3
4
5
6
7
8
9
10
11
12
pip install --index-url https://test.pypi.org/simple/ --no-deps mypackage-tutorial
# Use --no-deps to avoid pulling dependencies from TestPyPI if they exist on real PyPI
# Test it again in Python
python
>>> import mypackage
>>> mypackage.say_hello("Tester")
'Hello, Tester!'
>>> exit()
# Uninstall
pip uninstall mypackage-tutorial
Step 12: Upload to Real PyPI
Once you’re confident everything works correctly on TestPyPI, you can upload to the real PyPI.
- Get a Real PyPI API Token: Repeat Step 10.2, but on the main PyPI site.
Upload: Run
twine upload
, this time without specifying the repository (it defaults to PyPI). Use__token__
as the username and your real PyPI API token as the password.1
twine upload dist/*
Congratulations! Your package is now live on PyPI, and anyone can install it using pip install mypackage-tutorial
.
Next Steps & Maintenance
- Versioning: When you update your code, increment the
version
inpyproject.toml
(and potentially__init__.py
) following semantic versioning (semver.org) principles. Re-build and re-upload. - Dependencies: Keep your
dependencies
list inpyproject.toml
up-to-date. - Testing: Add automated tests (using
pytest
,unittest
) to ensure your code works correctly. - Advanced Features: Explore adding command-line scripts (
[project.scripts]
), including data files, or using different build backends if needed.
Packaging might seem daunting initially, but following these steps makes it manageable. It’s a fundamental skill for any serious Python developer, enabling code reuse, collaboration, and contribution to the vibrant Python ecosystem. Happy packaging!