Building and Publishing Packages in Python

Building and Publishing Packages in Python

An ultimate guide to building and publishing packages in Python using Poetry.

ยท

13 min read

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.

๐Ÿ’ก
An 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.

๐Ÿ’ก
A dependency is an external package that your program depends on to run.

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.
๐Ÿ’ก
we will discuss the 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:

  1. Depending on your operating system, open your terminal or command prompt.

  2. Run the following command:

>>> pip install poetry
  1. 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

  1. [tool.poetry]: This section is the heart of the pyproject.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.

  2. [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 the python = "^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.

  3. [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:

  1. Create a quotes.py file in the random_quotes_generator package. This file is the module that will contain the code that generates the quotes.

  2. After creating the quotes.py file, create a quotes.txt file in the random_quotes_generator package. This quotes.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.

  1. In the quotes.py file, import the random module. The random 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
    
  2. 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"
    
  3. 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 the open command. The with 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 in new_quotes. The random.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.
  4. Use the print() function to call get_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.

๐Ÿ’ก
Test will not be covered in this article, I hope to cover it in a future article. In the meantime, you can learn about testing in Python here.

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"
๐Ÿ’ก
By adding the MIT License, you make it clear that others can freely use and learn from your package.

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:

  1. Go to the PyPI developer dashboard: https://pypi.org/manage/account/.

  2. scroll down to the "API tokens" tab.

  3. Click on the "Create token" button.

  4. Enter a name for your token and select the permissions that you want to grant it.

  5. 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.

References

ย