Python Package Distribution
08 May 2016In a previous post, I introduced the basics of Python modules and packages. Let’s suppose you’ve written a nice package that you think others will find useful in their projects. How should you go about distributing it?
Project Structure
Typically, a Python package is distributed along with other file such as a README or installation instructions. You might have a directory structure like the following:
Very commonly, when the project is a single package, myproject
and mypackage
will be the same name.
It’s also very common for the project directory to also be a repository of a version control system such as git. This not only allows you to easily track changes in the code, but when hosted on a public service such as GitHub, allows others to both retrieve and contribute to the source code.
setup.py
In order to use your package on their system, it needs to be added to the Python
path. This is best done by the use of a setup script, distributed in the project
along with the package. The Python setuptools
standard library module provides
functionality to do this with minimal boilerplate and in a platform-independent
way.
The following script, which would be called setup.py
and would be placed in
the top level of the project alongside the README
in the above example,
demonstrates how to use setuptools
to write a minimal setup script:
When executing this script, setuptools
will provide all the command line
interfaces you need to build, install, package and distribute the project. For
example, to install it to your own system, run the following in the top level
directory of the project:
You may need to run this command with sudo
, or with the --user
option, to
overcome file system permissions issues.
The develop
option is also handy when you are continuing to work on the code;
instead of copying the package to your system packages folder, it makes links to
the source files instead, so that changes have immediate effect instead of
having to re-install after each change:
Distribution on PyPI
The Python Package Index (PyPI) is the primary repository of software
for the language, and is where most Python users get up to date packages.
Distributing a project on PyPI is quite straightforward; once you have completed
your setup.py
(see the section on advanced features below), register your
project with PyPI with the following (you will need a PyPI account):
You can then bundle and upload a source code distribution with:
Be careful that everything is correct before doing the upload; it’s only possible to do the upload of a given version and distribution type once.
Once the distribution is uploaded, other users will be able to quickly and
easily install it with pip
, the Python package manager:
I recently uploaded my pyhome project, which is a dotfile management and
synchronsiation tool that I wrote a recent blog post about, to PyPI.
This not only allows easier use of the tool by others, but allows me to get it
up and running quickly and easily on any new system with a single pip install
command.
Advanced setup.py
Features
setup.py
allows you to specify a lot of extra information about your project,
which can further aid in its distribution and installation. This example
setup.py shows the primary fields that you should seek to complete with your
distributed project, however I would like to draw particular attention to a few
particularly useful ones:
version
- Specifying the version of the project (in a manner compatible with PEP440) aids with distribution and for users to track updates to the code.install_requires
- When your project depends on modules not in the standard library, this is used bypip
to find and install any dependencies automatically.entry_points
andscripts
- Commonly projects will not just be simple libraries, but will also provide command line utilities or other programs. These options tellsetup.py
how to install these to your system.scripts
allows you to have a traditional Unix setup with simple executable files in a decidcatedbin
folder in the top level of your project, however usingentry_points
, while working differently, is recommended since it is more platform-independent.entry_points
essentially maps a name to a function in a module of your project, and on installation executing that name in the shell will cause that function to be executed.long_description
- This field is interpreted by PyPI as reStructuredText, and is used to generate its project page. This can be easily done by making your README a reStructuredText document, and loading it insetup.py
: