Distributing your own package on PyPi

In Regular Expressions Demystified I developed a little python package and distributed it via PyPi.

I wanted to publish my second self-written package as well, but coming back after almost a year, some things have changed in the world of PyPi, i.e. the old tutorials aren’t working anymore.

So I wrote this article to bring some clarity into this topic.

Distutils vs Setuptools


Distutils is still the standard tool for packaging in Python. It comes pre-installed with Python2 and Python3. Although it is still ‘standard’ it has some disadvantages tbc

for the case you stumble upon Distutils2: It was an attempt to combine the best from distutils and setuptools. tl;dr; development of distutils2 has stopped, you shouldn’t use it!


As far as I can see setuptools is becoming the new de facto standard for packaging Python projects.


A wheel is a

  1. setuptools extension for building wheels that provides the bdist_wheel setuptools command
  2. A command line tool for working with wheel files

The Advantages of wheels

  • Faster installation for pure Python and native C extension packages.
  • Avoids arbitrary code execution for installation. (Avoids setup.py)
  • Installation of a C extension does not require a compiler on Linux, Windows or macOS.
  • Allows better caching for testing and continuous integration.
  • Creates .pyc files as part of installation to ensure they match the Python interpreter used.
  • More consistent installs across platforms and machines.


Twine is a library / tool for publishing Python packages on PyPI.

You should favor using twine over python setup.py upload for the following reasons:

  • Verified HTTPS connections
  • Uploading doesn’t require executing setup.py
  • Uploading files that have already been created, allowing testing of distributions before release
  • Supports uploading any packaging format (including wheels)

For the next steps we are using all three – setuptools, wheel and twine.

Setup a project for packaging

Depending if your using pip or pipenv you have to either

pip install setuptools wheel twine


pipenv install setuptools wheel twine

The most basic project setup for a distribution looks like this:


The setup.py contains the following lines:

import setuptools

    author='Joern Boegeholz',
    description='An example Package',

Source Distribution

Having created the above setup, you can now build a distribution with the following command:

python setup.py sdist

Your project folder will now look like this:


This command will create a example_pkg-0.0.1.tar.gz in a dist folder file which contains the source code of your package hence source distribution or sdist.

You can now distribute your package as a zip file. A user can download and unpack it. With

python setup.py install

from inside the package folder, it will be installed into your Python site-packages.

Built Distribution with Wheel


python setup.py bdist_wheel

you can create a wheel.

Your dist folder now contains a  example_pkg-0.0.1-py3-none-any.whl

That’s a funny filename py3-none-any.whl,  so let’s decompose it:

  • py3 -> meant for usage with Python 3
  • none -> not OS-specific
  • any -> suitable to run on any processor architecture

If your package doesn’t contain any C extension and is compatible with Python2 as well, you can add a setup.cfg file to your project folder and insert these lines:

universal = 1

After running python setup.py bdist_wheel again the file

example_pkg-0.0.1-py2.py3-none-any.whl is created.

It is recommende to always create both: sdist and bdist_wheel

Uploading to PyPi

twine upload dist/*

The complete guide can be found here.

Migrating from distutils to setuptools

If You are using distutils and want to migrate here is the workflow in a nutshell:

Step 1 – Install requirements

pip install setuptools wheel twine

Step 2 – Change setup.py

Instead of

from distutils.core import setup


import setuptools

Step 3 – Adding / updating setup.cfg

universal = 1


Further readings


Leave a Reply

Your email address will not be published. Required fields are marked *