Making Python Packages Part 1: How to Publish A Python Package to PyPI

Skylar Kerzner
7 min readNov 29, 2021

--

Hey! Welcome to this quick, slightly fun guide on how to publish a python package to PyPI.

PyPI is the Python Package Index, from which we can pip install open source libraries.

PyPI is the Python Package Index, from which we can pip install open source libraries

Alright so here’s a quick overview of what we will do:

  1. Install a couple packages with pip
  2. Make an account on PyPI
  3. Compose your python package with 5 boilerplate files, plus your package code
  4. Upload it to PyPI

And with that, you can put anything you want up on PyPI for everyone to see and use. (Yikes!)

I. Install a couple of packages with pip

In order to create and publish your python package you’ll need to pip install two libraries

  1. setuptools
  2. twine

First, hopefully you have pip. You can try typing pip into the command line (Terminal on macOS) to see if it’s recognized.

You might also try replacing pip with pip3. Pip3 is just pip for python3.

Pip should have come with your distribution of python. It definitely does if you install python with homebrew, as in

brew install python3

If you dont have pip or brew, here’s some installation instructions for Homebrew: https://docs.brew.sh/Installation

Now assuming you have pip, let’s install those packages.

pip install setuptools twine

Or replace pip with pip3.

And yep — listing any number of packages after pip install will install them all. Fun facts.

II. Make an account on PyPI

Here’s the website: https://pypi.org/

Go make an account. And know that you’ll be typing your clever password into the command line when you’re uploading packages to PyPI.

III. Compose your Python Package

Alrighty, let’s make the package. There are tools like poetry for this, but I’m here to show you the old fashioned way. Do as you wish.

A. In the terminal, use cd and mkdir to navigate to wherever you want this package to live. Maybe something like Users/[your-user-name]/personal-code/libraries

B. Use mkdir to create the folder that your code will live in, and cd right in. So now we’re in personal-code/libraries/project-name. What a nice little home.

You’re going to need 6 files in this project to make a python package:

  1. README.txt
  2. CHANGELOG.txt
  3. LICENSE.txt
  4. MANIFEST.in
  5. setup.py
  6. package_name/__init__.py

And here’s what goes in them:

README.txt

This is a description of your project. It takes some numbers or arrays or whatever and does a thing, then returns some things. Tell the world. Just a text sentence like these.

CHANGELOG.txt

Change Log============0.0.1 (Nov 29, 2021) — — — — — — — — — -- First Release

That works. Note that if you don’t do enough dashes, it will be rejected when the description is generated. Fun. Facts.

LICENSE.txt

Copyright [Year] [Your name]Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

Just use this MIT license. Fill in the year and your name.

MANIFEST.in

global-include *.txt *.py

Those are the file types that will be included. Ironically, just not this one.

setup.py

from setuptools import setup, find_packagesclassifiers = [“Development Status :: 5 — Production/Stable”,“Intended Audience :: Education”,“Operating System :: MacOS”,“License :: OSI Approved :: MIT License”,“Programming Language :: Python :: 3”,]setup(name=””,version=”0.0.1",description=”a calculator with some advanced functions”,long_description=open(“README.txt”).read() + “\n\n” + open(“CHANGELOG.txt”).read(),url=””,author=””,author_email=””,license=”MIT”,classifiers=classifiers,keywords=””,packages=find_packages(),)

For this, you’ll have to change the Operating System and Intended Audience if necessary.

But you can only use the accepted list of classifiers here: https://pypi.org/pypi?%3Aaction=list_classifiers

Then just fill in the name (the name of the PACKAGE), the author (you) and the email (yours). You can also provide the URL to your package’s URL on github.

package_name/__init__.py

First, note that this is going in a further subdirectory called, well, whatever your package name is. Yes, a folder named for your package inside another folder thats probably also named for your package. That’s just how it goes.

This package_name is what people will import in your python code to use your package. As in:

import package_name

So don’t use any hyphens because python can’t import that.

Inside that folder, as in personal-code/libraries/project-name/package_name, we shall create __init__.py

Every package needs an __init__.py file. To quote the Python documentation:

Regular packages are traditional packages as they existed in Python 3.2 and earlier. A regular package is typically implemented as a directory containing an __init__.py file. When a regular package is imported, this __init__.py file is implicitly executed, and the objects it defines are bound to names in the package’s namespace. The __init__.py file can contain the same Python code that any other module can contain, and Python will add some additional attributes to the module when it is imported.

So for this guide, we will put your library code in the init file.

If you’re making a nice little calculator, your functions go there. If you’re making some classes about a dog that says bark, by all means drop it in.

And thus, a package was born.

IV. Upload it to PyPI.

To do that, your newborn package is going to get wrapped up into a little tar.gz. Awww.

A tar. gz file is achieved by archiving files into a TAR file and then compress it with GNU zip utility. This file type is commonly used for delivering package files or program installers on UNIX based operating systems.

But don’t worry, if you’re on Windows or whatever operating system, you don’t change anything about this process. It just makes a zip file or whatever file type works for your OS!

While in your package directory, run the following code on the command line:

python3 setup.py sdist

Beep boop. Now you have a dist folder in your current directory, which contains your python package as a tar.gz or other compressed file.

Next, you can optionally check whether PyPI is going to be happy with this creation of yours. On the command line, try:

twine check dist/[filename]

Just type twine check dist/ and then hit tab to autocomplete with your compressed package name.

If there’s a problem, maybe you’ll get a nice clear error message about it. If you get some messy error… good luck googling it and sorry I failed you.

Finally, upload it to PyPI!

twine upload — repository-url https://upload.pypi.org/legacy dist/*

And yes this URL with the word “legacy” is right — the pypi server switched from pypi.python.org to pypi.org in 2018, and “legacy” in this URL refers to the fact that this is an emulation of the legacy server’s API.

Here you’ll need that username and password. Hope you didn’t make your life difficult with your ingenious passwords. Better go change it.

You’ll also have to verify the email that PyPI sent you.

Welp, hopefully you see something like this:

100%|██████████████████████████████████████| 4.88k/4.88k [00:02<00:00, 2.39kB/s]View at:https://pypi.org/project/[your file name]

Your package is now on PyPI. Click the link, tell your friends! Hopefully you work too hard and you’ve forgotten your friends. Python is your friend now.

Either way, you’re now capable of putting a package out for public use.

People (or you) can pip(3) install that package from their command line, as shown on the top of your package’s pypi.org page.

Then they can import your package_name in their Python code.

Thanks for reading!

Bonus: Updating your Package on PyPI

The project is never over! There is always more to do!

When it’s time to update your project to version 0.0.2, go ahead and change the version number in the setup.py file to

version='0.0.2'

and add the change to the change log, which should now look like:

Change Log================0.0.1 (Nov 29, 2021) — — — — — — — — — — — — — — — -- First Release0.0.2 (Nov 30, 2021) — — — — — — — — — — — — — — — -- Added more functionality.

That’ll look nice when it’s rendered on pypi.org

Now you’ll run python3 setup.py sdist on the command line again, and it’ll create a new compressed file in your dist folder.

You’ll also have to delete the old compressed file so that you dont try uploading the same file to pypi, and get a charming red error.

Now the twine upload will work, and your package is upgraded :)

If you enjoyed this guide, please Follow me on Medium. I promise it’ll be helpful and fun :)

--

--

Skylar Kerzner

Senior Data Scientist @ Snapchat | Product Causal Inference | ML | Statistics | NLP | Twitter @another_DataSci | linkedin.com/in/skerzner