- Home
- Learn Linux
- Learn Electronics
- Raspberry Pi
- Programming
- Projects
- LPI certification
- News & Reviews
This video is a guide on creating and publishing your very own Python library to PyPI. PyPI is the definitive and most popular way to distribute Python libraries, allowing users to easily install your code using the pip command.
This guide, uses the PyVLCB library (a tool for communicating with CBUS and VLCB model railway systems) as a case study to demonstrate the process.
The first step is shifting your mindset. You are moving from writing code just for yourself to creating a reliable toolkit that others can use in their own software.
Start by separating your core logic into reusable modules placed in a dedicated folder.
You must rename one of your files to __init__.py.
Note: That is two double underscores on either side of the word "init".
This file designs the folder as a package.
Consistency in naming is crucial for user experience, and you must maintain stability so user programs don't break when you add features.
While Python variables are dynamically typed (meaning you can put any data into any variable), adding type hints improves code readability.
Hints are particularly useful for IDEs, which can warn you if you try to pass the wrong data to a method.
class VLCBFormat :
""" Handles a single VLCB packet
Attributes:
priority: CAN priority
can_id: CAN ID
data: Remaining data as a hex str
"""
def __init__ (self, priority: int, can_id: int, data: str) -> None:
self.priority = priority # Priority is actually high and low priority (2bit high / 2bit low) but just treated as single value
self.can_id = can_id
self.data = data # Data is left as hex string
Extract from vlcbformat.py
In the VLCBFormat class shown above, the priority argument is typed as an integer, as is can_id. The __init__ constructor returns None.
It is crucial to implement robust error handling so your library can be used safely.
Avoid Print Statements: While useful for personal debugging, when implemented in a library print statements can confuse command-line output or be hidden entirely in GUI applications.
Use Exceptions: Instead of printing errors, use robust exception handling to manage unexpected inputs or situations gracefully.
For example the following snippet from the __init.py file raises a ValueError exception if the string is missing the mandatory start charactor ':'.
if (input_string[0] != ":"):
raise ValueError(f"No start frame in '{input_string}'")
Where possible, reduce the number of external dependencies. This makes installation easier and minimizes conflicts for your users. If dependencies are required, include them in the package details so they install automatically.
The PyVLCB library needs pyserial for the USB communications. The following extract from pyproject.html will cause pip to install pyserial, if it's not already included.
# 'install_requires' becomes 'dependencies'
dependencies = [
"pyserial>=3.4",
]
You should create and run unit tests for critical functions. This ensures your code works as expected and maintains stability over time.
Good documentation is an essential part of a library; it is how others learn to use your toolkit.
Adding comprehensive docstrings to classes and methods allows for automatic documentation generation.
The following example shows the docstring for the parse_input method in the __init.py file.
"""Parse a log entry and return as a list of string values
Args:
input_string (string): String consisting of of num, date, direction, message as a single string
Returns (list[str]): List of strings
This docstring includes the argument and return types, but if using Python type hints then the argument types can be generated based on those instead.
If using GitHub, you can set up a document workflow to update documentation every time new code is pushed.
Below is my file .github/workflows/docs.yml file, used to generate the documents.
name: Deploy Documentation
# Trigger the workflow on push to the main branch
on:
push:
branches:
- main
# Grant GITHUB_TOKEN the permissions to write to the gh-pages branch
permissions:
contents: write
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.x'
- name: Install dependencies
run: |
pip install -r docs/requirements.txt
# If your library is needed for the docs to build (to read docstrings), install it too:
pip install .
- name: Configure Git User
run: |
git config --global user.name "github-actions[bot]"
git config --global user.email "github-actions[bot]@users.noreply.github.com"
- name: Build and Deploy
run: mkdocs gh-deploy --force
Include a detailed README file. These are often written using markdown and include information on installing and getting started.
Also consider adding guides or tutorials, which may be on a separate website to the auto-generated library.
Once your code is ready, follow these final steps to publish to PyPI.
Configure Metadata: Create or update your pyproject.toml file to include all necessary metadata and configuration details.
PyPI Account: Create a login on the PyPI website and enable multi-factor authentication.
GitHub Actions: Create a new publisher project and add a publish.yml file to your Git repository. This is stored in .github/workflows/publish.yml
Publishing: Update the version number in the pyproject.toml file; the lbrary will be published from your GitHub repository automatically.
Tip: You may want to test this workflow first using TestPyPI.
The process isn't complete until you have verified everything works as intended.
Install the package locally using pip to confirm the installation is successful.
Depending on your system, you may need to create a virtual environment first.
See my other programming guides at: