How to Build a Python Package

Search for a command to run...

No comments yet. Be the first to comment.
I want to tell a story of an amazing community called Bioinformatics Community but texts wouldn't be enough. Anyhoo, I blogged about my experience at BCC 2020 on OBF's website. This event was one of my highlights of the summer.

Recently, I needed to combine 2 or more PDF files into one. Merging these PDF files could be due to the company's demand or a need to track all files in one file. In this tutorial, I'd show you how I accomplished this particular task on my machine. T...

TL;DR This article uses Docker Compose to create and manage multiple container applications. It's entirely hands-on. Docker has made it easier to develop and package applications in reproducible environments. With Docker, you worry less about the inf...

GraphQL and Python Series (Expressive Introduction) This is part 1 of an ongoing series to introduce you to using GraphQL and Python. Hopefully, at the end of this series, you will be able to build a GraphQL Backend with Django and Graphene. GraphQL ...
Python, Golang, Javascript, etc libraries require packaging. It's done to easily distribute code among users thereby avoiding problems in future development.
In Python, a library is distributed through the Python Package Index (PyPI), a public hosting instance for packages.
The Python application we will push to PyPI will be a simple hello world (or hello user) application. It will be executable from the command line with a simple hello-world command or hello-world --name YOUR_NAME command.
First, let's create the initial files needed. We do this with the following:
mkdir -p hello-user/hello_user
touch hello-user/hello_user/main.py hello-user/hello_user/__init__.py
This will result in the following structure:
.
hello-user
└── hello_user
├── __init__.py
└── main.py
The project has a top-level directory called hello-world and a subdirectory, hello_world which contains two files.
Open the main.py and fill it up with the code below:
#!/usr/bin/env python
"""A CLI tool built with Click """
import click
@click.command()
@click.option("--greeting", default='Hello',help="How do you want to greet?")
@click.option("--name", default="World", help="Who to greet?")
def greet(greeting, name):
print(f"{greeting} {name}")
if __name__ == '__main__':
greet()
First, we're making use of an easy-to-use library called click. Click helps us to easily create command-line programs.
click.command shows that a function should be exposed to command-line access. click.option adds an argument to the command-line, automatically linking it to the function parameter of the same name (--greeting to greet and --name to name). click does some work behind the scenes so that we can call our greet method in our main block without parameters that are covered by the options decorators.
These decorators handle parsing command-line arguments and automatically produce help messages.
pip install setuptools twine
Now we've developed a simple application to be deployed to PyPI, the next thing to do is to configure the package. To keep things simple (like our python application), we will focus on the minimum amount of files needed to produce a package.
First, let's create setup.py file in the top-level directory. This is how this file looks:
from setuptools import setup, find_packages
setup(
name="edeediong-hello-user",
version="0.0.1",
author="Example Author",
author_email="author@example.com",
url="https://edeediong.me",
description="A hello user package",
packages=find_packages(),
classifiers=[
"Programming Language :: Python :: 3",
"License :: OSI Approved :: MIT License",
"Operating System :: OS Independent",
],
install_requires=["click"],
entry_points={
"console_scripts": ["hello-world = hello_user.main:greet"]
}
)
The setup.py imports two helpers from the setuptools module: setup and find_packages. The find_package discovers all subpackages (Python files) in the code automatically. The setup is what defines the package.
The documentation explains some of the parameters defined in the setup function and most are self-explanatory. I choose to explain the parameters that make our application special:
hello-world on the terminal.To produce a source distribution from the packages built, run the following command, and compare the results:
python setup.py sdist
A dist folder is created at the top-level directory with its source distribution. Details below:
.
├── dist
│ └── edeediong-hello-user-0.0.1.tar.gz
├── edeediong_hello_user.egg-info
│ ├── dependency_links.txt
│ ├── entry_points.txt
│ ├── PKG-INFO
│ ├── requires.txt
│ ├── SOURCES.txt
│ └── top_level.txt
├── hello_user
│ ├── __init__.py
│ └── main.py
├── README
└── setup.py
Python Package Index (PyPI) is a repository of Python software that allows users to host Python packages and also install it. A Python package called twine is used to deploy packages from the local environment to the PyPI repository.
First, we upload the package to TestPyPI to ensure everything works as expected:
twine upload --repository-url https://test.pypi.org/legacy/ dist/edeediong-hello-user-0.0.1.tar.gz
Registration for the test instance of PyPI is important as Twine will ask for username and password to upload packages.
Then we test the package, we install it with pip locally:
pip install -i https://test.pypi.org/simple/ edeediong-hello-user
Finally, we test our application locally to see if it works fine:
$ hello-world
Hello World
$ hello-world --name Eddie
Hello Eddie
We don't want to mess with the Python Package Index which is why we won't upload our package to the actual PyPI. But if we have a live package that needs deployment, we deploy to PyPI with the command below:
twine upload dist/edeediong-hello-user-0.0.1.tar.gz
The difference here is we don't specify a repository url, we deploy straight to PyPI.
At the end of this tutorial, we have built a python application, packaged it, and deployed it to PyPI. I hope this guide helps anyone in the future when building their Python packages.
edeediong: this is my name and I used it to avoid naming issues as PyPI needs unique names.