Tutorial on Packaging and Distributing Projects

Page Status:Complete
Last Reviewed:2014-09-30

This tutorial covers the basics of how to create and distribute your own Python projects. This tutorial assumes that you are already familiar with the contents of the Tutorial on Installing Distributions.

Setup for Project Distributors

This section describes steps to follow before distributing your own Python packages.

First, make sure you have already followed the setup steps for installing packages.

In addition, you’ll need wheel (if you will be building wheels), and twine, for uploading to PyPI.

We recommend the following installation sequence:

  1. For building wheels: pip install wheel [1]
  2. For uploading distributions: pip install twine [1]

Creating your own Project

In the sections below, we’ll reference the PyPA sample project. which exists as a companion to this tutorial.

Layout

The critical requirement for creating projects using setuptools is to have a setup.py. For an example, see sampleproject/setup.py. We’ll cover the components of setup.py in the sections below.

Although it’s not required, most projects will organize the code using a single top-level package, that’s named the same as the project.

Additionally, most projects will contain the following files:

  • A README for explaining the project.
  • A setup.cfg that contains option defaults for setup.py commands.
  • A MANIFEST.in that defines additional files to be included in the project distribution when it’s packaged.

Name

from sampleproject/setup.py

name = 'sample'

This will determine how your project is listed on PyPI. For details on permitted characters, see the name section from PEP426.

Version

from sampleproject/setup.py

version = '1.2.0'

Projects should aim to comply with the version scheme specified in PEP440. Here are some examples:

1.2.0.dev1  # Development release
1.2.0a1     # Alpha Release
1.2.0b1     # Beta Release
1.2.0rc1    # RC Release
1.2.0       # Final Release
1.2.0.post1 # Post Release

If the project code itself needs run-time access to the version, the simplest way is to keep the version in both setup.py and your code. If you’d rather not duplicate the value, there are a few ways to manage this. See the “Single-sourcing the version across setup.py and your project” Advanced Topics section.

Packages

from sampleproject/setup.py

packages=find_packages(exclude=['contrib', 'docs', 'tests*']),

It’s required to list the packages to be included in your project. Although they can be listed manually, setuptools.find_packages finds them automatically. Use the exclude keyword argument to omit packages that are not intended to be released and installed.

Metadata

It’s important to include various metadata about your project.

from sampleproject/setup.py

# A description of your project
description='A sample Python project',
long_description=long_description,

# The project's main homepage
url='https://github.com/pypa/sampleproject',

# Author details
author='The Python Packaging Authority',
author_email='pypa-dev@googlegroups.com',

# Choose your license
license='MIT',

# See https://pypi.python.org/pypi?%3Aaction=list_classifiers
classifiers=[
    # How mature is this project? Common values are
    #   3 - Alpha
    #   4 - Beta
    #   5 - Production/Stable
    'Development Status :: 3 - Alpha',

    # Indicate who your project is intended for
    'Intended Audience :: Developers',
    'Topic :: Software Development :: Build Tools',

    # Pick your license as you wish (should match "license" above)
    'License :: OSI Approved :: MIT License',

    # Specify the Python versions you support here. In particular, ensure
    # that you indicate whether you support Python 2, Python 3 or both.
    'Programming Language :: Python :: 2',
    'Programming Language :: Python :: 2.6',
    'Programming Language :: Python :: 2.7',
    'Programming Language :: Python :: 3',
    'Programming Language :: Python :: 3.2',
    'Programming Language :: Python :: 3.3',
    'Programming Language :: Python :: 3.4',
],

# What does your project relate to?
keywords='sample setuptools development',

Dependencies

from sampleproject/setup.py

install_requires = ['peppercorn']

“install_requires” should be used to specify what dependences a project minimally needs to run. When the project is installed by pip, this is the specification that is used to install its dependencies.

For more on using “install_requires” see install_requires vs Requirements files.

Package Data

Often, additional files need to be installed into a package. These files are often data that’s closely related to the package’s implementation, or text files containing documentation that might be of interest to programmers using the package. These files are called “package data”.

from sampleproject/setup.py

package_data={
    'sample': ['package_data.dat'],
}

The value must be a mapping from package name to a list of relative path names that should be copied into the package. The paths are interpreted as relative to the directory containing the package.

For more information, see Including Data Files from the setuptools docs.

Data Files

Although configuring Package Data is sufficient for most needs, in some cases you may need to place data files outside of your packages. The data_files directive allows you to do that.

from sampleproject/setup.py

data_files=[('my_data', ['data/data_file'])],

Each (directory, files) pair in the sequence specifies the installation directory and the files to install there. If directory is a relative path, it is interpreted relative to the installation prefix (Python’s sys.prefix for pure-Python distributions, sys.exec_prefix for distributions that contain extension modules). Each file name in files is interpreted relative to the setup.py script at the top of the project source distribution.

For more information see the distutils section on Installing Additional Files.

Note

setuptools allows absolute “data_files” paths, and pip honors them as absolute, when installing from sdist. This is not true, when installing from wheel distributions. Wheels don’t support absolute paths, and they end up being installed relative to “site-packages”. For discussion see wheel Issue #92.

Scripts

from sampleproject/setup.py

entry_points={
    'console_scripts': [
        'sample=sample:main',
    ],
},

Although setup.py supports a scripts keyword for pointing to pre-made scripts, the recommended approach to achieve cross-platform compatibility, is to use “console_script” entry points that register your script interfaces, and let the toolchain handle the work of turning these interfaces into actual scripts [2]. The scripts will be generated during the install of your distribution.

For more information, see Automatic Script Creation from the setuptools docs.

MANIFEST.in

A MANIFEST.in file is needed in certain cases where you need to package additional files that python setup.py sdist (or bdist_wheel) don’t automatically include.

To see a list of what’s included by default, see the Specifying the files to distribute section from the distutils documentation.

For details on writing a MANIFEST.in file, see the The MANIFEST.in template section from the distutils documentation.

Developing your project

Although not required, it’s common to locally install your project in “develop” or “editable” mode, while you’re working on it. This allows the project to be both installed and editable in project form.

cd myproject
python setup.py develop    # the setuptools way
pip install -e .           # the pip way (which just calls "setup.py develop")

For more information, see the Development Mode section of the setuptools docs.

Packaging your Project

To have your project installable from a Package Index like PyPI, you’ll need to create a Distribution (aka “Package” ) for your project.

Source Distributions

Minimally, you should create a Source Distribution:

python setup.py sdist

A “source distribution” is unbuilt (i.e, it’s not a Built Distribution), and requires a build step when installed by pip. Even if the distribution is pure python (i.e. contains no extensions), it still involves a build step to build out the installation metadata from “setup.py”.

Universal Wheels

Additionally, if your project is pure python (i.e. contains no compiled extensions) and is version agnostic, then you should also create what’s called a “Universal Wheel”. This is a wheel that can be installed anywhere by pip.

To build a Universal Wheel:

python setup.py bdist_wheel --universal

You can also permanently set the --universal flag in “setup.cfg” (e.g., see sampleproject/setup.cfg)

[bdist_wheel]
universal=1

Only use the --universal setting, if:

  1. Your project runs on Python 2 and 3 with no changes (i.e. it does not require 2to3).
  2. Your project does not have any C extensions.

Beware that bdist_wheel does not currently have any checks to warn you if use the setting inappropriately.

If your project has optional C extensions, it is recommended not to publish a universal wheel, because pip will prefer the wheel over a source installation, and prevent the possibility of building the extension.

Platform Wheels

“Platform Wheels” are wheels that are specific to a certain platform like linux, OSX, or Windows, usually due to containing compiled extensions.

“Platform Wheels” are built the same as “Universal Wheels”, but without the --universal flag:

python setup.py bdist_wheel

Note

PyPI currently only allows uploads of platform wheels for Windows and OS X, NOT linux. Currently, the wheel tag specification (PEP425) does not handle the variation that can exist across linux distros.

Uploading your Project to PyPI

First, you need a PyPI user account. There are two options:

  1. Create an account manually using the form on the PyPI website.
  2. Have an account created as part of registering your first project (see option #2 below).

Next, you need to register your project. There are two ways to do this:

  1. (Recommended): Use the form on the PyPI website. Although the form is cumbersome, it’s a secure option over using #2 below, which passes your credentials over plaintext.
  2. Run python setup.py register. If you don’t have a user account already, a wizard will create one for you.

If you created your account using option #1 (the form), you’ll need to manually write a ~/.pypirc file like so.

[distutils]
index-servers=pypi

[pypi]
repository = https://pypi.python.org/pypi
username = <username>
password = <password>

You can leave out the password line if below you use twine with its -p PASSWORD argument.

Finally, you can upload your distributions to PyPI. There are two options.

  1. (Recommended): Use twine

    twine upload dist/*
    

    The biggest reason to use twine is that python setup.py upload (option #2 below) uploads files over plaintext. This means anytime you use it you expose your username and password to a MITM attack. Twine uses only verified TLS to upload to PyPI protecting your credentials from theft.

    Secondly it allows you to precreate your distribution files. python setup.py upload only allows you to upload something that you’ve created in the same command invocation. This means that you cannot test the exact file you’re going to upload to PyPI to ensure that it works before uploading it.

    Finally it allows you to pre-sign your files and pass the .asc files into the command line invocation (twine upload twine-1.0.1.tar.gz twine-1.0.1.tar.gz.asc). This enables you to be assured that you’re typing your gpg passphrase into gpg itself and not anything else since you will be the one directly executing gpg --detach-sign -a <filename>.

  2. Use setuptools:

    python setup.py sdist bdist_wheel upload
    

[1](1, 2) Depending on your platform, this may require root or Administrator access. pip is currently considering changing this by making user installs the default behavior.
[2]Specifically, the “console_script” approach generates .exe files on Windows, which are necessary because the OS special-cases .exe files. Script-execution features like PATHEXT and the Python Launcher for Windows allow scripts to be used in many cases, but not all.