Image of Deploy a python package to PyPI

ADVERTISEMENT

Introduction

If you're like me, you've used Python to write scripts for years either for personal projects, games, local scripts, or work. You've almost certainly downloaded and installed Python packages using Pip, Python's most popular package manager.

Maybe you've thought about creating your own packages and making them available for others to install using Pip. Luckily, this task is fairly simple using the Python Package Index (PyPI).

In this article, we explain how Pip and PyPI work, how to structure a Python package, and how to deploy a Python package to PyPI.

Pip

Pip is the most popular package manager used by Python developers and teams. It enables developers to easily download and install Python packages created by others into their local environments.

These packages can either be executable tools that are meant to be run from the command line, or Python libraries (modules) that are meant to be imported into code projects as dependencies.

Pip is installed by default with Python version 3.4 and greater. You can check that Pip is installed (and check Pip's version) by running the following command at the command line:

pip --version

If Pip is not found, follow these steps to install it. Note that if any of the below commands fails, you may need to add sudo before it and provide your credentials.

To check the location of the Pip executable that is mapped to the pip command, run:

which pip

To install a package using Pip, use the following command:

pip install <package-name>

To upgrade a package already installed with Pip, use:

pip install --upgrade <package-name>

To list all installed packages and package versions, use:

pip list

To uninstall a package with Pip, use (note that this will not install package dependencies, to do that you can use the package pip-autoremove):

pip uninstall <package-name>

We can learn more about how Pip works by diving into the Python Package Index.

The Python Package Index (PyPI)

The Python Package Index (PyPI) is a repository that stores Python packages along with a history of their versions and other associated information, such as a README file, authors, descriptions, publish dates, and more.

You can think of PyPI as the "backend" for the Pip package manager. When you install a package using Pip, it searches the PyPI for the name of the specified package, and installs it if it is found. Since package lookups are done by name, no two packages in the PyPI can have the same name.

Here's an example of a Python package called git-tagup that I published to the PyPI:

https://pypi.org/project/git-tagup/

As you can see from the URL above, PyPI automatically creates a web page for your package after you deploy it. This enables other users to find it and learn about it. I recommend you poke around the URL above to get familiar with the layout. Especially note the Project description and Release history sections.

Before learning how to deploy a package, let's discuss the proper structure for a Python package.

Python Package Structure

Here are the basic components of a simple Python package:

  1. A Python package folder (just a regular directory named with your project name) containing your code files (.py files) along with an empty file called __init__.py. This file tells Python that the folder should be treated as a Python package.

  2. A build script called setup.py. We will expand on this in the next section.

  3. A readme file, usually called README.md that describes the project and it's usage.

  4. A license file, usually called just license or license.md, containing the project's copyright license.

  5. A unit test file test.py file, containing the project's unit tests.

Here is the structure for the git-tagup project I created:

./
|---- README.md
|
|---- git_tagup/
|      |---- __init__.py
|      |---- __main__.py
|      |---- util.py
|
|---- license
|---- setup.py
|---- test.py

The application code in the above package lives in the __main__.py and util.py modules. The __main__.py module is specially named so that the code inside it executes when the program is run from the command line executable. The util.py code contains some help functions that are imported into the other module.

Next, we'll see how to configure our build file setup.py.

Python Build Script "setup.py"

The setup.py file contains a build script that is used by the Python package setuptools to build and package the Python program. It provides metadata like the application name, version, author, description, required Python version, dependencies, search keywords, and more customizable options.

Note that the setup.py file contains actual Python code that gets executed by we run the command to build our program.

Here is the setup.py file for my git-tagup program:

import setuptools

with open("README.md", "r") as fh:
    long_description = fh.read()

setuptools.setup(
    name="git-tagup",
    version="0.1.2",
    author="Jacob Stopak",
    author_email="jacob@initialcommit.io",
    description="Find and tag Git commits based on version numbers in commit messages.",
    long_description=long_description,
    long_description_content_type="text/markdown",
    url="https://initialcommit.com/projects/git-tagup",
    packages=setuptools.find_packages(),
    classifiers=[
        "Programming Language :: Python :: 3",
        "License :: OSI Approved :: MIT License",
        "Operating System :: OS Independent",
    ],
    python_requires='>=3.6',
    install_requires=[
        'gitpython'
    ],
    keywords='git tag git-tagup tagup tag-up version autotag auto-tag commit message',
    project_urls={
        'Homepage': 'https://initialcommit.com/projects/git-tagup',
    },
    entry_points={
        'console_scripts': [
            'git-tagup=git_tagup.__main__:main',
            'gtu=git_tagup.__main__:main',
        ],
    },
)

In the sample above, there is a parameter called entry_points which contains an entry called console_scripts:

entry_points={
        'console_scripts': [
            'git-tagup=git_tagup.__main__:main',
            'gtu=git_tagup.__main__:main',
        ],
    },

This is used to configure the build to create executable binaries and install them in the local user's Python bin directory (something like .../Python/3.7/bin/git-tagup) after the package is installed using PyPI.

In the example above, two binaries called git-tagup and gtu will be created, both of which will execute the main() function in the __main__ module of the git_tagup package. This means the program can be run by simply typing git-tagup or gtu from the command line, assuming the Python bin folder is on the path.

Now that we have our package fully configured, let's learn how to deploy it to the Python Package Index.

Deploying a Package to PyPI

The first step is to build the package using the command:

python setup.py sdist bdist_wheel

This invokes the Python build tools to run the setup.py file, build the application, and place the outputs in a new folder called /dist. The outputs are a .tar.gz zip file and a .whl wheel file, which are two different formats for packaging Python programs.

Next, we need to install a Python package called twine, which is a convenient way to upload our built package to PyPI. Install twine with the command:

pip install twine

Finally, we can deploy both formats of our built package to PyPI using twine like this:

twine upload dist/*

Congratulations! Your Python package is now a part of the PyPI and ready to be shared with the Python community!

Installing your Package from PyPI

Now that the package is deployed, you should be able to view it at a URL like:

https://pypi.org/project/your-project-name/

To install your package locally, any users with Python and Pip can simply run:

pip install <your-package-name>

Conclusion

In this article, we discussed the tools necessary to create a Python package and how to upload it to the Python Package Index for others to use.

If you're interested in a great beginner book on learning Python, I highly recommend Beginning Python: From Novice to Professional. I have had this book within arm's reach for years and it will give you a great start or refresher on programming in Python.