Publishing a Package to Pip

PyPi - The Python Package Index

I love Python. It's easy to read, even easier to write, and best of all, the easiest language (I've used) to interact with other people's code. I spent too long being only on one end of that equation though, only consuming code other people have written as opposed to taking the things I was making and making them public so the cycle could continue. I felt it was too much hassle to get started with and for no reason because who would be using the libraries I write anyways? After finally learning how to create and publish packages to PyPi, I'm glad to report it's super easy and definitely worthwhile. Even if it's just so you can easily import code you wrote for a previous project without going into the horrors of relative imports, I feel it's worth the five minutes investment to get up and running with it.

First of all, you need to make a PyPi account and then add your credentials to ~/.pypirc. Your file should look something like this:

[distutils]
    index-servers = pypi
    
[pypi]
    repository: https://upload.pypi.org/legacy/
    username: <Your PyPi username>
    password: <Your PyPi password>


Realistically, you should set up API keys, but I am just trying to show the bare minimum to get up and running with PyPi. I am in no way an authority  on this and most often don't even follow best practices.

Next take a simple project or tool you've been working on and impose some structure. Personally, I am a fan of simple tools that do things I understand. Consequentially, I end up using tools that started falling out of fashion in the 80s. I find Makefile to be just the right level of build tool for me. I understand the file at a glance, I can hack it to get what I want done, and it is going to be the topic of another blog post so I'm not going to dwell on it too much here, suffice to say you should add a Makefile to the root of your repository/project and have these key rules:

PYTHON=python3.8

ENV_DIR=.env_$(PYTHON)
IN_ENV=. $(ENV_DIR)/bin/activate &&

upload_pip: build_dist
    twine upload --repository pypi dist/*

build:
    $(IN_ENV) $(PYTHON) -m pip install --editable .
    rm -fr dist/
    $(IN_ENV) $(PYTHON) setup.py sdist bdist_wheel

build_dist:
    rm -fr dist/
    $(IN_ENV) python setup.py sdist

setup:
    $(PYTHON) -m pip install --upgrade virtualenv
    $(PYTHON) -m virtualenv -p $(PYTHON) $(ENV_DIR)
    $(IN_ENV) $(PYTHON) -m pip install --upgrade -r requirements.txt
    $(IN_ENV) $(PYTHON) -m pip install --editable .


This will let you run make upload_pip and magically have your package arrive on PyPi once you install python3 -m pip install twine. But wait, what is it going to upload?? That's defined in your setup.py:

from setuptools import (
    find_packages,
    setup
)

INSTALL_REQUIRES = []

setup(
    name='my-first-pip-package-name',
    description='This is my first Pip package!',
    version=0.0.1,
    url='https://github.com/link/to/your/repo',
    python_requires='>=3.6',
    packages=find_packages('src'),
    package_dir={'': 'src'},
    install_requires=INSTALL_REQUIRES,
    entry_points={
        'console_scripts': []
    }
)

This should be pretty self evident for what should be where,  but examples are the best way to grok something and here are two personal repos where I use these structures for Makefile and setup.py to publish Pip packages.

I know this is quite a low quality introduction to making a Pip package, so please leave me feedback. I want to improve at my writing and hearing responses is the best way I can get there!

Show Comments