Packages will be useless without an organized way to store, upload, and download them. Python Packaging Index is the main source of open source packages in the Python community. Anyone can freely upload new packages and the only requirement is to register on the PyPI site—https://pypi.python.org/pypi.
You are not limited, of course, to only this index and all packaging tools support the usage of alternative package repositories. This is especially useful for distributing closed source code among internal organizations or for deployment purposes. Details of such packaging usage with instructions on how to create your own package index will be explained in the next chapter. Here we focus only on open-source uploads to PyPI with only a little mention on how to specify alternative repositories.
Python Package Index is, as already mentioned, the official source of open source package distributions. Downloading from it does not require any account or permission. The only thing you need is a package manager that can download new distributions from PyPI. Your preferred choice should be pip
.
Anyone can register and upload packages to PyPI provided that he or she has an account registered. Packages are bound to the user, so, by default, only the user that registered the name of the package is its admin and can upload new distributions. This could be a problem for bigger projects, so there is an option to design other users as package maintainers so that they are able to upload new distributions.
The easiest way to upload a package is to use the upload
command of the setup.py
script:
$ python setup.py <dist-commands> upload
Here, <dist-commands>
is a list of commands that creates distribution to upload. Only distributions created during the same setup.py
execution will be uploaded to the repository. So, if you would upload source distribution, built distribution, and wheel package at once, then you need to issue the following command:
$ python setup.py sdist bdist bdist_wheel upload
When uploading using setup.py
, you cannot reuse already built distributions and are forced to rebuild them on every upload. This might make some sense but can be inconvenient for large or complex projects in which creation of the distribution may actually take a considerable amount of time. Another problem of setup.py upload
is that it can use plaintext HTTP or unverified HTTPS connection on some Python versions. This is why twine
is recommended as a secure replacement for setup.py upload
.
Twine is the utility for interacting with PyPI that currently serves only one purpose—securely uploading packages to the repository. It supports any packaging format and always ensures that the connection is secure. It also allows you to upload files that were already created, so you are able to test distributions before the release. An example usage of twine
still requires invoking setup.py
for building distributions:
$ python setup.py sdist bdist_wheel $ twine upload dist/*
If you have not yet registered this package, then the upload will fail because you need to register it first. This can also be done using twine
:
$ twine register dist/*
.pypirc
is a configuration file that stores information about Python packages repositories. It should be located in your home directory. The format for this file is as follows:
[distutils] index-servers = pypi other [pypi] repository: <repository-url> username: <username> password: <password> [other] repository: https://example.com/pypi username: <username> password: <password>
The distutils
section should have the index-servers
variable that lists all sections describing all the available repositories and credentials to them. There are only three variables that can be modified for each repository section:
repository
: This is the URL of the package repository (it defaults to https://www.python.org/pypi)username
: This is the username for authorization in the given repositorypassword
: This is the user password for authorization in plaintextNote that storing your repository password in plaintext may not be the wisest security choice. You can always leave it blank and you will be prompted for it whenever it is necessary.
The .pypirc
file should be respected by every packaging tool built for Python. While this may not be true for every packaging-related utility out there, it is supported by the most important ones such as pip
, twine
, distutils
, and setuptools
.
There are generally two types of distributions for Python packages:
Source distributions are the simplest and most platform independent. For pure Python packages, it is a no-brainer. Such a distribution contains only Python sources and these should be already highly portable.
A more complex situation is when your package introduces some extensions written, for example, in C. Source distributions will still work provided that the package user has a proper development toolchain in his/her environment. This consists mostly of the compiler and proper C header files. For such cases, the built distribution format may be better suited because it may provide already built extensions for specific platforms.
The sdist
command is the simplest command available. It creates a release tree where everything needed to run the package is copied. This tree is then archived in one or many archive files (often, it just creates one tarball). The archive is basically a copy of the source tree.
This command is the easiest way to distribute a package from the target system independently. It creates a dist
folder with the archives in it that can be distributed. To be able to use it, an extra argument has to be passed to setup
to provide a version number. If you don't give it a version
value, it will use version = 0.0.0
:
from setuptools import setup setup(name='acme.sql', version='0.1.1')
This number is useful to upgrade an installation. Every time a package is released, the number is raised so that the target system knows it has changed.
Let's run the sdist
command with this extra argument:
$ python setup.py sdist running sdist ... creating dist tar -cf dist/acme.sql-0.1.1.tar acme.sql-0.1.1 gzip -f9 dist/acme.sql-0.1.1.tar removing 'acme.sql-0.1.1' (and everything under it) $ ls dist/ acme.sql-0.1.1.tar.gz
The version is used to mark the name of the archive, which can be distributed and installed on any system that has Python. In the sdist
distribution, if the package contains C libraries or extensions, the target system is responsible for compiling them. This is very common for Linux-based systems or Mac OS because they commonly provide a compiler, but it is less usual to have it under Windows. That's why a package should always be distributed with a prebuilt distribution as well, when it is intended to run under several platforms.
To be able to distribute a prebuilt distribution, distutils
provides the build
command, which compiles the package in four steps:
build_py
: This builds pure Python modules by byte-compiling them and copying them into the build folder.build_clib
: This builds C libraries, when the package contains any, using C compiler and creating a static library in the build folder.build_ext
: This builds C extensions and puts the result in the build folder like build_clib
.build_scripts
: This builds the modules that are marked as scripts. It also changes the interpreter path when the first line was set (!#
) and fixes the file mode so that it is executable.Each of these steps is a command that can be called independently. The result of the compilation process is a build folder that contains everything needed for the package to be installed. There's no cross-compiler option yet in the distutils
package. This means that the result of the command is always specific to the system it was built on.
When some C extensions have to be created, the build process uses the system compiler and the Python header file (Python.h
). This include file is available from the time Python was built from the sources. For a packaged distribution, an extra package for your system distribution is probably required. At least in popular Linux distributions, it is often named python-dev
. It contains all the necessary header files for building Python extensions.
The C compiler used is the system compiler. For a Linux-based system or Mac OS X, this would be gcc or clang respectively. For Windows, Microsoft Visual C++ can be used (there's a free command-line version available) and the open-source project MinGW can be used as well. This can be configured in distutils
.
The build
command is used by the bdist
command to build a binary distribution. It calls build
and all the dependent commands, and then creates an archive in the same way as sdist
does.
Let's create a binary distribution for acme.sql
under Mac OS X:
$ python setup.py bdist running bdist running bdist_dumb running build ... running install_scripts tar -cf dist/acme.sql-0.1.1.macosx-10.3-fat.tar . gzip -f9 acme.sql-0.1.1.macosx-10.3-fat.tar removing 'build/bdist.macosx-10.3-fat/dumb' (and everything under it) $ ls dist/ acme.sql-0.1.1.macosx-10.3-fat.tar.gz acme.sql-0.1.1.tar.gz
Notice that the newly created archive's name contains the name of the system and the distribution it was built under (Mac OS X 10.3).
The same command called under Windows will create a specific distribution archive:
C:acme.sql> python.exe setup.py bdist ... C:acme.sql> dir dist 25/02/2008 08:18 <DIR> . 25/02/2008 08:18 <DIR> .. 25/02/2008 08:24 16 055 acme.sql-0.1.win32.zip 1 File(s) 16 055 bytes 2 Dir(s) 22 239 752 192 bytes free
If a package contains C code, apart from a source distribution, it's important to release as many different binary distributions as possible. At the very least, a Windows binary distribution is important for those who don't have a C compiler installed.
A binary release contains a tree that can be copied directly into the Python tree. It mainly contains a folder that is copied into Python's site-packages
folder. It may also contain cached bytecode files (*.pyc
files on Python 2 and __pycache__/*.pyc
on Python 3).
The other kind of built distributions are "wheels" provided by the wheel
package. When installed (for example, using pip
), wheel
adds a new bdist_wheel
command to the distutils
. It allows creating platform-specific distributions (currently only for Windows and Mac OS X) that provides alternatives to normal bdist
distributions. It was designed to replace another distribution introduced earlier by setuptools
—eggs. Eggs are now obsolete so won't be featured here. The list of advantages of using wheels is quite long. Here are the ones that are mentioned in the Python Wheels page (http://pythonwheels.com/):
setup.py
).pyc
files as part of the installation to ensure they match the Python interpreter usedAccording to PyPA recommendation, wheels should be your default distribution format. Unfortunately, platform-specific wheels for Linux are not available yet so if you have to distribute packages with C extensions, then you need to create sdist
distribution for Linux users.