Packaging and distributing python apps and modules

Published 2019-01-31 on Yaroslav's weblog

There may come a time after some hacking and playing around with python that you feel like the piece of code you just created needs to be shared with the world, and so you might be thinking "Man, wouldn't it be sweet if anyone could install my app/module just by typing pip install stuffimade'. Well, it is actually easier than you might think (it certainly was in my case).

It basically boils down to these five things:

  1. Make an account on pypi.org
  2. Come up with an original name, that hasn't been taken on pypi (arguably the hardest part of these instructions).
  3. Prepare a setup py file to package your app/module, choose a license, maybe (preferably) write a readme.
  4. Package it.
  5. Upload it.

The first step is really simple, just go the website (pypi.org) and click on the link that says "Register" on the top right corner. Be sure to not forget your password, especially after uploading your package, since you will need everything that you want to upload a new version of your package.

The second step is all up to you, just use the search function in the website to make sure that your package name hasn't been taken.

After having created your account on pypi.org, and decided on a name for your app/package, you'll proceed to create the necessary package files. Your package should consist of a folder with your actual module or app, inside which should also be a __init__.py file (it can be empty if you don't need to set any variables or anything), a LICENSE file with your license of choice (e.g. MIT, BSD, GPL, etc.), and a README.rst or README.md file containing a detailed description of your app/module.

Your directory structure should look something like this

/my-app-package
  /my-app
    __init__.py
    (other files and directories)
  setup.py
  LICENSE
  README.md

Now, if your app is going to have files other than python files, and the aforementioned files, you might to add a MANIFEST.in in the root of you package directory specifying the files to include. This is true, for example, for Django apps, since they might contain template and static files (html, css, js, etc.) that the packaging tool we are going to use might not pick up. As an example I'll show you the MANIFEST.in file of my w3blog package

include LICENSE
include README.md
recursive-include weblog/static *
recursive-include weblog/templates *
recursive-include weblog/locale *

There I specified to, just in case, include the LICENSE and README.md files, and also include static files, templates, and the message files for localization, by recursively searching the directories of said files.

Now on to the setup.py file. For this I am also going to use my project's file as an example

#!/usr/bin/env python3
import os
from setuptools import find_packages, setup

with open(os.path.join(os.path.dirname(__file__), 'README.md')) as readme:
    README = readme.read()

# allow setup.py to be run from any path
os.chdir(os.path.normpath(os.path.join(os.path.abspath(__file__), os.pardir)))

setup(
    name='w3blog',
    version='0.5.2',
    packages=find_packages(),
    include_package_data=True,
    license='BSD License',
    description='A simple blog engine for Django with multilingual capabilities.',
    long_description=README,
    url='https://www.yaroslavps.com/',
    author='Yaroslav de la Peña Smirnov',
    author_email='contact@yaroslavps.com',
    classifiers=[
        'Environment :: Web Environment',
        'Framework :: Django',
        'Framework :: Django :: 1.11',
        'Framework :: Django :: 2.0',
        'Framework :: Django :: 2.1',
        'Intended Audience :: Developers',
        'License :: OSI Approved :: BSD License',
        'Operating System :: OS Independent',
        'Programming Language :: Python',
        'Programming Language :: Python :: 3',
        'Programming Language :: Python :: 3.4',
        'Programming Language :: Python :: 3.5',
        'Programming Language :: Python :: 3.6',
        'Programming Language :: Python :: 3.7',
        'Topic :: Internet :: WWW/HTTP',
        'Topic :: Internet :: WWW/HTTP :: Dynamic Content',
    ],
)

Of course, you should customize this file according to your app's details, for example, your classifiers list might look shorter, like this

    classifiers=[
        'License :: OSI Approved :: BSD License',
        'Operating System :: OS Independent',
        'Programming Language :: Python :: 3',
        'Programming Language :: Python :: 3.4',
        'Programming Language :: Python :: 3.5',
        'Programming Language :: Python :: 3.6',
        'Programming Language :: Python :: 3.7',
    ],

It will depend on which version of python you want to support, your license, some other required packages, etc. The rest of the setup function parameters should be self explanatory.

Once you have you package files ready (don't forget about your readme and license), you'll need to proceed to package them, i.e. generate the distribution files. For that, we'll need to install a couple of packages, we cant install them inside a virtual environment, or you could install them system wide

$ sudo pip3 install --upgrade setuptools wheel twine

Now just run the following command from the same directory where setup.py is located

$ python3 setup.py sdist bdist_wheel

After running that command, you should now have a dist subdirectory with a .tar.gz archive and a .whl file. Those are the files that you'll need to upload. The twine package that we just installed is going to take care of that for us. Remember that you already should have an account on pypi.org to upload your package.

So for example, when I just uploaded the first version of w3blog, and each time that I upload a new version I run a command (from the same directory as setup.py) that looks like this

$ python3 twine upload dist/w3blog-0.5.2*

Just replace "w3blog-0.5.2*" with your appropriate package name-version. After running that command, twine is going to ask you for your username and password, thence it is going to upload it to pypi. Once it is successfully uploaded, it should be a matter of seconds or minutes before you, and everybody else in the world with an internet connection (and capable of running python, of course), can install your package!

If you need more detailed information about this process, check out the official documentation at https://packaging.python.org/tutorials/packaging-projects/

© 2018—2024 Yaroslav de la Peña Smirnov.