Please note that package “pkgexample” is live and available to play along with from pypi.org

Register An Account At pypi.org

If you haven’t done so already, head over to pypi.org and register an account as soon as you can. Pypi.org is the official third-party software repository for python.

We will have you creating, uploading and sharing your packages through pypi.org in no time.

Package Up Your Code

A Simple HelloWorld Class

To demonstrate packaging I’m going to need a some python code, here I have created a simple python project consisting of just one HelloWorld class. Your projects may be a bit more complex and contain more that just the one class.

class HelloWorld:
    def __init__(self):
        self.a_simple_message="Hello World"

    def print_message(self):
        print(self.a_simple_message)

The SRC/Source Package Layout

I’m going to wrap up the helloworld.py class file inside a python packaging structure known as the “src/source layout”.

[email protected]:~/myrepos/rex$ tree -a pkgexample/

pkgexample/
├── .gitignore
├── LICENCE
├── pyproject.toml
├── README.md
└── src
    └── pkgexample
        ├── helloworld.py
        └── __init__.py

2 directories, 6 files
[email protected]:~/myrepos/rex$

The SRC Subdirectory

It is named the “source layout” as any package you want to distribute is placed under the inner src/ subdirectory.

Since I have decided on the package name “pkgexample” to contain our simple python project you will see a directory of that name in the inner src/ directory.

The SRC Package Root Directory Name

The root directory of the “src/source layout” package structure can be named absolutely anything, go ahead and name it “bananas” if you wish – but it is more convenient for everyone if you give it the same name as the package you want to distribute.

I have named the root directory “pkgexample” to match the package found at “src/pkgexample”.

Python Package And Module Definitions

Regular Python Package

The definition of a regular python package is any directory that contains an “__init__.py” file. This file is a marker to let python know it can import code modules from that directory.

The “__init__.py” file is usually empty, but as your projects grow you can make entries inside the “__init__.py” file to export parts of your complex package with more convenient names.

If there is no “__init__.py” file present, python will skip over a directory and you will not be able to import your package and modules.

[email protected]:~/myrepos/rex$ tree -a pkgexample/

pkgexample/
├── .gitignore
├── LICENCE
├── pyproject.toml
├── README.md
└── src
    └── pkgexample
        ├── helloworld.py
        └── __init__.py

2 directories, 6 files
[email protected]:~/myrepos/rex$
Python Modules

A “python module” is just a name that refers to any file that contains python code.

Our simple helloworld.py python file is a python module and is contained in our python package directory pkgexample/ , which is contained within the src/ directory.

Package Documentation Files “README.md”, “LICENCE”

Tell me you provide high quality documentation for you code. You do document don’t you?

Inside the first level of the root directory you should have the files README.md and LICENCE.

README.md

The README.md file contains all of your great documentation.

It is usually written in markdown. More information on markdown here, or the GitHub flavored markdown here.

LICENCE

Look in to licensing for your specific needs. GitHub does provide license templates for your project repositories. I usually go for the MIT license.

Project Configuration File “pyproject.toml”

This is a configuration file written in TOML, more information on TOML here.

This is the most important file, apart from your project code, in the packaging process. It tells the python packager what build system to use, information about your project, and where to look for further information.

The “pyproject.toml” configuration file has the following key/value pair tables: [build-system], [project] and [project.urls] .

[build-system]
requires = ["setuptools>=65.0"]
build-backend = "setuptools.build_meta"

[project]
name = "pkgexample"
version = "1.0.2"
authors = [
  { name="Good Boy", email="[email protected]" },
]
description = "A simple package example"
readme = "README.md"
license = { file="LICENSE" }
requires-python = ">=3.7"
classifiers = [
    "Programming Language :: Python :: 3",
    "License :: OSI Approved :: MIT License",
    "Operating System :: OS Independent",
]


[project.urls]
"Homepage" = "https://github.com/RexBytes/pkgexample"
"Bug Tracker" = "https://github.com/RexBytes/pkgexample/issues"
"Download URL" ="https://github.com/RexBytes/pkgexample/archive/refs/tags/v1.0.2.tar.gz"
[build-system]

This is where you state the build system you want to use for your python packaging. I’m going to use setup tools, and have required version greater than or equal to 65.

Other build systems are available such as, Hatching, Flit and PDM.

[project ]

In this table you provide information about your python project. At the bare minimum you should include the above. Most of the values are self explanatory, such as “name”, “version”, “authors”, “description”, “readme”, and “license”. Other fields need a little explaining.

Field “classifiers”

These field classifiers are used to help people find your project when searching on pypi.org. A full list of classifiers you can copy and past from can be found here.

Field “required-python”

If you have build your project to target specific python versions, it is here where you state the compatible versions of python for your project.

[project.url]

Here you can place any, and all URLs you want for your project.

For full details on these fields and others, look here.

The “.gitignore” File

Although this isn’t part of the python packaging process, and is not a required file, it is very useful to include.

When building python packages a few intermediary files are created, egg files, wheel files and tar files. These are your pypi.org package files and shouldn’t really be committed to your GitHub repository – it will get messy and disorganized fast.

*.egg-info
*.whl
*.tar.gz

At the very least you should have the above entries in your “.gitignore” file.
There are many general “.gitignore” templates available out there, here is one designed to cover almost any python project you create.

GitHub Repository

For convenience the package “pkgexample” and its files are available at the following GitHub repository.


https://github.com/RexBytes/pkgexample
0 forks.
0 stars.
0 open issues.

Recent commits:

Building And Uploading Your Distribution Archives

Now that you have a valid package directory structure containing your python package and modules, the fun starts.

Software Requirements

Let’s get these software requirements out of the way quick, these are all needed to package, check and upload your package to pypi.org

Python3 pip

We need the latest version of pip installed for python 3.
Run the following command to install and upgrade your pip.

python3 -m pip install --upgrade pip

PyPA build

We need the latest PyPA’s build installed to build your package.

python3 -m pip install --upgrade build

Twine Package Uploader

Twine is used to check, and upload your package to your pypi.org account.

python3 -m pip install --upgrade twine

Building Your Package

This part is going to be really short. We’re just going to run the packaging commands, and your package will soon be ready to be installed by your mates.

Here, from the root directory, we run the build command and as you can see, it finished successfully.

You will find that a dist/ directory magically appears in your root directory, this is where your distribution has been packaged to.

[email protected]:~/myrepos/rex/pkgexample$ python3 -m build
* Creating venv isolated environment...
* Installing packages in isolated environment... (setuptools>=61.0)
* Getting dependencies for sdist...
...
...
...
Successfully built pkgexample-1.0.2.tar.gz and pkgexample-1.0.2-py3-none-any.whl

Check Your Distribution Before Uploading

Twine only checks that your package is packaged correctly – it doesn’t test your code, that is entirely your responsibility.

Again from the root directory, ask twine to check your newly compiled distribution inside your ./dist/ directory.

Our simple package “pkgexample” has passed all the tests and is ready to be published.

[email protected]:~/myrepos/rex/pkgexample$ python3 -m twine check ./dist/*
Checking ./dist/pkgexample-1.0.2-py3-none-any.whl: PASSED
Checking ./dist/pkgexample-1.0.2.tar.gz: PASSED

Upload And Publish Your Package

Finally, again from your root directory, use the following command to upload and publish your python package.

You will be asked for your pypi.org username and password.

[email protected]:~/myrepos/rex/pkgexample$ python3 -m twine upload ./dist/*
Uploading distributions to https://upload.pypi.org/legacy/
Enter your username: rexbytes
Enter your password:
Uploading pkgexample-1.0.2-py3-none-any.whl
100% ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 8.4/8.4 kB • 00:00 • 82.3 kB/s
Uploading pkgexample-1.0.2.tar.gz
100% ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 8.1/8.1 kB • 00:00 • ?
View at:
https://pypi.org/project/pkgexample/1.0.2/
[email protected]:~/myrepos/rex/pkgexample$

Head over to https://pypi.org/project/pkgexample/ where you should see the “pkgexample” package live and ready to be used in other peoples projects.

Using Your Package

You should try all of the following commands, “pkgexample” is now a real package hosted on PyPi.org

Open up a fresh ubuntu terminal to install the “pkgexample” package.

[email protected]:~$ python3 -m pip install pkgexample
Defaulting to user installation because normal site-packages is not writeable
Collecting pkgexample
  Downloading pkgexample-1.0.2-py3-none-any.whl (3.3 kB)
Installing collected packages: pkgexample
Successfully installed pkgexample-1.0.2

It is now installed on your system.

Let’s test it. Open up an interactive Python3 shell and play along with the following code. It should work as expected.

[email protected]:~$ python3
Python 3.10.4 (main, Jun 29 2022, 12:14:53) [GCC 11.2.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> from pkgexample.helloworld import HelloWorld
>>> my_helloworld_object=HelloWorld()
>>> my_helloworld_object.print_message()
Hello World
>>>

Exit, exit(), your python shell and let’s uninstall the package for now.

[email protected]:~$ python3 -m pip uninstall pkgexample
Found existing installation: pkgexample 1.0.2
Uninstalling pkgexample-1.0.2:
  Would remove:
    /home/ubuntu/.local/lib/python3.10/site-packages/pkgexample-1.0.2.dist-info/*
    /home/ubuntu/.local/lib/python3.10/site-packages/pkgexample/*
Proceed (Y/n)? y
  Successfully uninstalled pkgexample-1.0.2
[email protected]:~$

Keep it installed if you wish.

Further Reading

https://packaging.python.org/en/latest/tutorials/packaging-projects/

https://setuptools.pypa.io/en/latest/

https://hatch.pypa.io/latest/

https://flit.pypa.io/en/latest/

https://pdm.fming.dev/latest/

https://toml.io/en/

https://www.markdownguide.org/getting-started/

https://github.github.com/gfm/

https://linuxhint.com/bash-tree-command/

3 thoughts on “Python Packaging – Using “setuptools”, “pyproject.toml””
%d bloggers like this: