Making a Python Package VIII - summary

This post is part 8 of the "Making a Python Package" series:

  1. Making a Python Package
  2. Making a Python Package II - writing docstrings
  3. Making a Python Package III - making an installable package
  4. Making a Python Package IV - writing unit tests
  5. Making a Python Package V - Testing with Tox
  6. Making a Python Package VI - including data files
  7. Making a Python Package VII - deploying
  8. Making a Python Package VIII - summary

Note: To get the material for this blog post, visit the master branch of Romans! Github project. To get it locally, and assuming you cloned the previous version, run

# clear last set of changes
$ git reset --hard HEAD
# checkout this version
$ git checkout master

Making a Python Package VIII: Summary

We are going to assume that you want to install roman, but you could change the package to whatever you would like

Packages to install

$ pip install pycodestyle pydocstyle setuptools pytest tox twine

Directory layout

You should have the following directory layout:

.
+-- roman/
    +-- __init__.py
    +-- (any *.py files needed)
    +-- data/
        +-- (any data files you want included)
+-- tests/
    +-- __init__.py (can be blank)
    +-- (any test_*.py files needed)
+-- README.md
+-- DESCRIPTION.rst
+-- MANIFEST.in
+-- setup.py
+-- tox.ini

Notes:

  1. The test/__init__.py can be blank (and probably should be).
  2. The roman/__init__.py should probably import other modules in the directory. There is a lot of information about how __init__.py works in the first article of this series.
  3. The roman/data/ directory is optional -- not all projects will need external data.
  4. If your project does use external data sets, you should use pkg_resources to access them. A short introduction on how to do this is given in the sixth article of this series.

Writing unit tests

  1. Place tests in the test/ subdirectory.
  2. Make sure all files containing tests follow the pattern test_*.py
  3. Make sure all functions that incorporate tests start with def test_.....
  4. Write tests based on what you think the functions should do, don't write tests tham mimic the existing implementation.

More detail on unit tests here.

Lint everything with tox

The file tox.ini should have the following format

[tox]
envlist=py36

[testenv]
deps =
  pandas
  pydocstyle
  pycodestyle
  pytest
commands =
  - pydocstyle --ignore=D100,D104,D213 roman/
  - pycodestyle roman/
  pytest

Include other depedencies as needed. You should periodically run tox while developing, and fix linting errors and address unit test failures as they occur. Note that we deliberately don't lint the unit test files.

More detail on tox and docstrings.

Create setup.py

The setup.py file tells Python how to install via the pip setup.py install command. Even if you intend to distribute your file via the Python Package Index, PyPI, you still need setup.py to generate the build directory. There are lots of options for setup.py, but this is reasonably well-featured example that you can modify for your package:

import setuptools

setuptools.setup(
    name="roman",
    version="0.1.0",
    url="https://github.com/kiwidamien/roman",
    author="Damien Martin",
    author_email="damien.j.martin@gmail.com",
    description="Allows conversion of Roman numerals to ints (and vice versa)",
    long_description=open('DESCRIPTION.rst').read(),
    packages=setuptools.find_packages(),
    install_requires=[],
    classifiers=[
        'Programming Language :: Python',
        'Programming Language :: Python :: 3',
        'Programming Language :: Python :: 3.6',
    ],
    include_package_data=True,
    package_data={'': ['data/*.csv']},
)

Note:

  1. The last two fields, include_package_data and package_data, are only useful if you are including extra data to go with your package. Most packages won't need this.

Including other files (e.g. DESCRIPTION.rst, images, LICENCE files)

Use MANIFEST.in to ensure that files outside of roman/ that you want distributed with your code are installed by twine. The format for MANIFEST.in is simple; in this project we used

include DESCRIPTION.ini

Deploying

Thre are two methods of deploying: using a Github repo, or using a Python Package Index (TestPyPI / PyPI).

Deploy via Github

If you have a github repo roman, users can use pip to install you package directory with the command

$ pip install git+https://github.com/<github username>/roman

Deploy via pip to TestPyPI

You will need to set up an account on TestPyPI before completing this step. Your project name also has to be unique across all of TestPyPI (unlike Github, where your repo names just have to be unique to your account). You can set up an account on TestPyPI via this link.

Before deploying, ensure that you have run tox and dealt with any linter errors or failing unit tests.

To do the deploy, run the following commands in the shell

# create the distribution
$ python setup.py install

# now install the newly created dist directory using twine
$ twine upload --repository-url https://test.pypi.org/legacy/ dist/*

You are done! To install this package, run the command

pip install --extra-index-url https://testpypi.python.org/simple roman

Note:

  1. This will fail if you try it, because I have already used the name roman. Find a name that is unique on TestPyPI for your package.

Deploy via pip to PyPI

This is very similar to deploying on TestPyPI. You will need to create an account on PyPI to deploy your package (you cannot just use the TestPyPI account). You should also make sure that you have run tox and ensured you have no linter errors or failing unit tests.

The commands to deploy are

# create the distribution
$ python setup.py install

# now install on the real PyPI
$ twine upload roman

To install, simply run

$ pip install roman

Congratulations! You have now installed a package on the Python Package Index, and made it available to the world.

Note

  1. This will work for at most one user. The name of your package has to be unique on PyPI.