Packaging and distributing python apps and modules
Published Jan 31, 2019 08:10 UTC 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:
- Make an account on pypi.org
- Come up with an original name, that hasn't been taken on pypi (arguably the hardest part of these instructions).
- Prepare a setup py file to package your app/module, choose a license, maybe (preferably) write a readme.
- Package it.
- 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.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
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', email@example.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/