Building and Publishing Packages in Python
An ultimate guide to building and publishing packages in Python using Poetry.
Do you know that any piece of Python program you create can be shared with the world, no matter how small, as long as it successfully performs a specific task? That's right! You can share your Python programs with other developers, making them readily available for them to use in their projects.
But how is this possible? Remember when you used the command:
>>> pip install something
In this command, 'something' refers to the name of what you want to install. This 'something' you want to install is known as a package. Other developers, just like you, have built and shared these packages, enabling us to leverage their functionality and build applications more efficiently.
In this article, we will explore how to build our own Python package and publish it to the Python Package Index (PyPI), the go-to hub for Python packages. By doing so, we enable other developers to seamlessly incorporate our programs into their projects.
We will build a random quote generator package and also learn how to use Poetry to manage and publish our package. If you prefer not to build, you can simply clone the package from this repository and proceed directly to publishing.
Prerequisite
Basic understanding of Python
Python version 3.7+ installed
What is a Python Package?
A Python package is a collection of Python modules that are bundled together.
A module is a Python file that contains Python code (functions, classes, e.t.c.). Modules can be imported into other Python files, allowing you to access the functionalities provided by the code written in the modules. So whenever you create a file with the .py extension, Python sees it as a module.
A Python package acts like a folder that organizes related Python files (modules) so they can be easily distributed and used by other programs. The folder must contain an __init__.py
file to be recognized as a package by Python. This file can be empty or may include the initialization code for the package. If the folder lacks an __init__.py
file, Python will not treat it as a package, and the modules inside won't be importable using the package's name.
init.py
file is used to mark a folder as a package.Setting Up the Project
To start building a Python package, you must first set up your development environment. We will use a tool called Poetry for this.
What is Poetry?
Poetry is a package and dependency management tool. It can be used to create, manage, and publish Python packages.
The primary purposes of Poetry in Python package development are:
- Dependency Management: Poetry allows developers to specify project dependencies in a simple and declarative manner. It automatically generates a
pyproject.toml
file, which lists all the required packages and their versions. This ensures that your project always uses the same versions of the packages it depends on. This is important because if the versions of the packages your project depends on change, it can break the project.
pyproject.toml
file in more detail later.Virtual Environments: Poetry automatically creates and manages a virtual environment for your project. A virtual environment is like a container that isolates the project's dependencies to ensure it does not interfere with the dependencies of other Python projects on your computer.
Project Structure: Poetry sets up a standard project structure, making it easy to organize your code. This includes generating necessary files like
__init__.py
and more.Dependency resolution: Poetry uses a variety of resources to find and install the correct versions of the dependencies that your project needs. It ensures that all dependencies are compatible, avoiding version conflicts and making sure the project builds and runs correctly.
Packaging and Publishing: Poetry streamlines the process of building and packaging your Python project into a distributable format, such as wheel. Additionally, it simplifies the publishing of your project to platforms like PyPI so that others can easily install and use your package.
๐กwe will get to Wheels later.
Installing Poetry
Follow these steps to install Poetry:
Depending on your operating system, open your terminal or command prompt.
Run the following command:
>>> pip install poetry
- Once Poetry is installed, you can verify the installation by running the following command:
>>> poetry --version
Your output should be similar to Poetry (version 1.5.1)
. It means the installation was successful.
Creating a New Project
Now that poetry has been successfully installed, it will be used to create the project structure of our package.
To create a new project with Poetry, you can use the poetry new
command, followed by the name of the project. In this case, since we want to build a package, we would specify the name of the package as the project name.
>>> poetry new random-quotes-generator
This command will create a new folder with the name of your project, in your current directory. Locate the folder and open it in your text editor, I'll be using the VsCode text editor.
The folder structure should look like this:
random-quotes-generator
โโโ random_quotes_generator
โ โโโ __init__.py
โโโ tests
โโโ __init__.py
โโโ pyproject.toml
โโโ README.md
The random-quotes-generator
contains two packages: random_quotes_generator
and tests
. In addition, it also contains two essential files: pyproject.toml
and README.md
.
The random_quotes_generator
folder contains the __init__.py
file, making it a package. The Python file (module) containing the code that generates the random quotes will be created in this package. As for the tests
package, it will contain the Python files (modules) that will be used to test the random_quotes_generator
package.
Let's break down the pyproject.toml
file and the README.md
file for a better understanding:
The pyproject.toml
file
The pyproject.toml
file is a crucial configuration file introduced by the Poetry tool for managing Python projects. It plays a central role in specifying project details, dependencies, and other essential details. This file adheres to the Tom's Obvious Minimal Language (TOML) format, which is known for its readability and simplicity.
For now, it will look like this:
[tool.poetry]
name = "random-quotes-generator"
version = "0.1.0"
description = ""
authors = ["Seun Fashina <emailexample@gmail.com>"]
readme = "README.md"
packages = [{include = "random_quotes_generator"}]
[tool.poetry.dependencies]
python = "^3.10"
[build-system]
requires = ["poetry-core"]
build-backend = "poetry.core.masonry.api"
Key sections of the pyproject.toml
file
[tool.poetry]
: This section is the heart of thepyproject.toml
file and contains essential project details:name: The name of your Python package or project.
version: The version of your package, following the Semantic Versioning scheme (MAJOR.MINOR.PATCH).
description: A brief description of the package's purpose and functionality.
authors: A list of authors' names and emails who contributed to the project.
readme: The filename of the README file to be used for project documentation.
packages: A list of directories or patterns containing Python packages to be included in the distribution.
[tool.poetry.dependencies]
: This section defines the project's dependencies and their versions. Whatever external package we install for our project to depend on, will be listed in this section. In our example, the[tool.poetry.dependencies]
section only specifies the dependency on Python. The version of Python that the project depends on is specified by thepython = "^3.10"
declaration. The^
character in the version declaration indicates that the project will work with any version of Python that is greater than or equal to 3.10.[build-system]
: This section defines the build system configuration for the project:requires: Specifies the minimum required version of Poetry's core library (
poetry-core
).build-backend: Specifies the Poetry plugin or backend to use for building the project. The
poetry.core.masonry.api
backend is the default and handles project building and packaging.
The README.md
file
- The
README.md
file is a file that contains information about the project. It is typically the first file that users will see when they open the project.
Developing the Package
With the project structure in place, we can proceed to develop the package using the following steps:
Create a
quotes.py
file in therandom_quotes_generator
package. This file is the module that will contain the code that generates the quotes.After creating the
quotes.py
file, create aquotes.txt
file in therandom_quotes_generator
package. Thisquotes.txt
file is a text file containing famous quotes. It doesn't have a .py extension, so it is not a module.For now, your file structure should look this way:
You can get the quotes here. Just copy and paste the quotes into your quotes.txt
file. Make sure you save it after pasting it.
In the
quotes.py
file, import therandom
module. Therandom
module is used to generate random numbers and sequences. In this case, we will use it to randomly select a quote from the quotes file.import random
Define a constant for the file path to the quotes file. This constant will be used to access the
quotes.txt
file.FILE_PATH = "random_quotes_generator/quotes.txt"
Define a function called
get_random_quotes()
. This function will return random quotes.def get_random_quotes(): with open(FILE_PATH, 'r') as file: new_quotes = file.readlines() return random.choice(new_quotes)
The code above defines a function called
get_random_quotes()
. This function returns a random quote from the file. The function first opens the file in read mode using theopen
command. Thewith
command ensures that the file is closed properly after reading its content.The
with open
command takes two arguments: the file path and the mode. The mode specifies how the file will be opened. In this case, the mode is 'r', meaning the file will be opened in read mode so you can't modify the file.The
file.readlines()
method returns a list of all the lines in the file and stores it innew_quotes
. Therandom.choice()
function returns a random element from a sequence. In this case, we are returning a random quote from the list of quotes.๐กThere are other modes used in opening files in Python, you can learn more here.Use the
print()
function to callget_random_quotes
, you should get a random quote printed to the terminal.print(get_random_quotes())
Putting all this together, your code should look this way:
import random
FILE_PATH = "random_quotes_generator/quotes.txt"
def get_random_quotes():
with open(FILE_PATH, 'r') as file:
new_quotes = file.readlines()
return random.choice(new_quotes)
print(get_random_quotes())
Output:
>>> Change your thoughts and you change your world. - Norman Vincent Peale
You can choose to remove print(get_random_quotes())
from quotes.py
before publishing the package.
Publishing the package
Now, it's time to share your package with the world! If you haven't created an account on PYPI, create your account here.
Update the pyproject.toml
file.
Before we use Poetry to publish the package to PYPI, we need to make a few changes to the pyproject.toml
file.
name = "random-quotes-generator"
description = ""
license = ""
The reason for updating the name is that your package name must be unique for you to publish it on PYPI. If I publish this package with the name random-quotes-generator
, it means you can't publish yours using the same name. To avoid this issue, you can add a prefix to your package name to differentiate it from others. For example:
name = "sn-random-quotes-generator"
description = "A Python package to generate random famous quotes for inspiration."
license = "MIT"
We also need to update the README.md
file so it can give users an overview of our package and how to use it. You can copy the template from here and paste it into your README.md
file. Feel free to modify it to your preference.
Create a distribution.
To create a distributable package of your project, use the following Poetry command:
poetry build
The poetry build
command creates a distribution of your Python package. A distribution is a packaged version of your package that can be uploaded to PyPI or installed locally.
The poetry build
command creates a wheel and a .tar.gz file for your package.
Wheels
Wheels are a binary format that can be installed by Python's pip
command, and they can be installed quickly. Wheels are also more portable, meaning that they can be installed on different operating systems and with different versions of Python.
The .tar.gz
file
The .tar.gz
file is a source distribution (sdist) file. It is a compressed archive of your project's source code. Source distribution files are typically larger than wheels, but they can be installed on any platform that has Python installed. They are also more versatile, meaning that they can be used to build wheels or to install your project from source.
The relationship between wheels and source distribution (sdist) files is that wheels are built from sdists. When you run the poetry build
command, Poetry will first build a sdist(.tar.gz file in this case) of your project. Then, Poetry will use the sdist to build a wheel. The wheel will be installed by Python's pip command, and the sdist will be saved in your project's directory. So when you make changes to your code, you can rebuild the sdists file using the poetry build
command. Poetry will then use this new build for the sdist file to rebuild the wheels so it can accommodate the latest changes to your code. This way, anyone installing your package will always have the latest version.
Upload the Package to PYPI.
To publish your package to PyPI, you will need to create an API token. An API token is a unique identifier that allows you to authenticate yourself to PyPI without having to enter your username and password every time you want to upload a package.
API tokens are more secure than passwords because they are not stored in plain text, making them more secure.
To create an API token, follow these steps:
Go to the PyPI developer dashboard: https://pypi.org/manage/account/.
scroll down to the "API tokens" tab.
Click on the "Create token" button.
Enter a name for your token and select the permissions that you want to grant it.
Click on the "Create token" button.
Once you have created an API token, replace <my-token>
with the API token you created and run this command in your terminal:
poetry config pypi-token.pypi <my-token>
This will authenticate you and enable you to publish your package.
To publish the package, run this command in your terminal:
poetry publish
Output:
Publishing random-quotes-generator (0.1.0) to PyPI
- Uploading random_quotes_generator-0.1.0-py3-none-any.whl 0%
- Uploading random_quotes_generator-0.1.0-py3-none-any.whl 93%
- Uploading random_quotes_generator-0.1.0-py3-none-any.whl 100%
- Uploading random_quotes_generator-0.1.0.tar.gz 0%
- Uploading random_quotes_generator-0.1.0.tar.gz 100%
Once the upload is complete, go to your PyPI account on the website to confirm that your package has been successfully published. You should see your package listed under "Packages" on your PyPI dashboard. It may take a few minutes for the package to appear in PyPI's search results.
Congratulations
You have learned how to successfully build a package in Python and publish it to the Python Package Index. By sharing your creation with the Python community, you've taken a significant step towards contributing to the open-source ecosystem and empowering developers worldwide.
Through this article, you've learned the essential steps in building, packaging, and publishing Python packages using the powerful Poetry tool. From crafting an engaging README.md
to handling authentication, you've acquired the knowledge and skills to make your packages accessible to others with ease.
Now, it's time to celebrate your achievement! Share your package with fellow developers and across your social media, encourage feedback, and invite collaboration. The world of Python development awaits your next creative endeavor.
That's it for now. If you have any feedback or question, do well to ask me in the comments.