ruk·si

pip
Releasing a Package

Updated at 2020-06-05 15:20

Decide library name; I'll use magillan here as an example.

# venv shortcut functions, but I'm just making and activating a venv here
vmk magillan 3
v magillan

Create setup.py; note that, contrary to what requirements.txt does, all dependencies are listed without versions. This is important because you won't know what exact versions of libraries will be installed alongside of this libraries so you must be flexible and fix any problems that might raise.

import ast
import os
import re

import setuptools

# read library version from the main package so it's in one place
with open(os.path.join(os.path.dirname(__file__), 'magillan', '__init__.py')) as infp:
    version = ast.literal_eval(re.search('__version__ = (.+?)$', infp.read(), re.M).group(1))

# read full page library descrption shown on PyPI website from README like GitHub
with open('README.md', 'r') as fp:
    long_description = fp.read()

dev_dependencies = [
    'flake8',
    'isort',
    'pydocstyle',
    'pytest',
    'pytest-cov',
    'pytest-mock',
    'tox',
]

if __name__ == '__main__':
    setuptools.setup(
        name='magillan',
        description='Awesome library for Python',
        long_description=long_description,
        long_description_content_type='text/markdown',
        version=version,
        author='Ruksi',
        author_email='me@ruk.si',
        url='https://github.com/ruksi/magillan',
        license='MIT',
        packages=setuptools.find_packages(
            '.', exclude=('magillan_tests', 'magillan_tests.*',)
        ),
        install_requires=[],
        tests_require=dev_dependencies,
        extras_require={'dev': dev_dependencies},
        classifiers=[
            "Programming Language :: Python :: 3",
            "License :: OSI Approved :: MIT License",
            "Operating System :: OS Independent",
        ],
    )

After the setup.py, you can install development version to current virtual enviroment:

pip install -e .[dev]  # where . can be a path to the library source root

Based on the above configuration, you have a single module called magillan. This should look something like this:

magillan/
    magillan/
        __init__.py
    magillan_tests/
        __init__.py
    setup.py
    README.md

Where magillan/__init__.py contains something like:

__version__ = '0.0.1'

And magillan_tests/__ini__.py something like:

import magillan


def test_for_smoke():
    assert magillan.__version__

Readme

Add README.md. Shown on GitHub and PyPI project home pages.

License

Add LICENSE. For example MIT from https://opensource.org/licenses/MIT

Development Tools

Create setup.cfg:

[flake8]
ignore = E741
max-line-length = 79
max-complexity = 10

[pydocstyle]
ignore = D100,D104,D203,D212

[tool:pytest]
norecursedirs = .git .tox

[isort]
atomic = true
combine_as_imports = false
indent = 4
length_sort = false
line_length = 79
multi_line_output = 5
not_skip = __init__.py
order_by_type = false
wrap_length = 79

Create tox.ini:

[tox]
envlist = {py35,py36,py37,py38}

[testenv]
commands = pytest -v {posargs}
deps =
    pytest

Create .coveragerc:

[run]
branch = True
omit =
    *site-packages*
    setup.py

Running the various tools:

isort -y                # automatically enforce import style rules
flake8                  # run code style checker
pydocstyle              # run documentation style checker
tox                     # run tests with multiple Python versions
pytest --cov            # run unit tests and print test coverage

If you are doing open source, create .travis.yml for free Travis CI and Codecov:

sudo: false
dist: xenial
language: python
python:
  - "3.5"
  - "3.6"
  - "3.7"
  - "3.8"
install:
  - pip install -U pip
  - "pip install -e .[dev]"
script:
  - flake8
  - pydocstyle
  - pytest -vvv --cov .
after_success:
  - bash <(curl -s https://codecov.io/bash)

Release

Create .gitignore, here is a good starting point:

*.egg-info
*.py[cod]
.cache
.coverage
.eggs
.pytest_cache
.tox
.venv
.idea
__pycache__
build
dist
sdist
venv
env
htmlcov
pip-delete-this-directory.txt
pip-log.txt
coverage.xml
# check that the tar archive looks good after source distribution build
python setup.py sdist
tar tzvf dist/magillan-0.1.tar.gz

# create local git repository
git init
git status
git add -A
git commit -m "Initial commit"
git clean -ndx -e .idea/        # lists which files would be deleted by clean
git clean -fdx -e .idea/        # deletes them

# create project in GitHub and then...
git remote add origin git@github.com:ruksi/magillan.git
git push -u origin master

Create PyPI account at PyPI registration page. You will use this to upload and manage your pip packages.

# create a build distribution and upload it to PyPI
python setup.py sdist bdist_wheel
pip install twine
twine upload dist/*
# ... add your username and password
# and how you should have your library visible on https://pypi.org/

Now the following should work anywhere with Python and pip installed:

pip install magillan

python
>> import magillan; print(magillan.__version__)