Python packaging in a nutshell

Vasyl Dizhak

Small intro to Python packaging

Vasyl Dizhak @rootart

MoneyPark, vasyl.dizhak@moneypark.ch

UAPycon 2014, www.meetup.com/uapycon/

Packages are a way of structuring Python's module namespace by using "dotted module names".

An Introduction to Python by Guido van Rossum and Fred L. Drake, Jr

Why I should use it?

  1. It is the most convenient way to spread and share your work

Software is like s*x: it's better when it's free.

Linus Torvalds

What we are going to do?

  1. Have a discussion about what do you think are good and bad parts in python packaging ecosystem?

pypi.python.org

The Python Package Index is a repository of software for the Python programming language. 45876

Official guidelines?

Python Packaging Authority (PyPA)

PyPA is a working group that maintains many of the relevant projects in Python packaging. They host projects on github and bitbucket, and discuss issues on the pypa-dev mailing list.
The Hitchhiker's Guide to Packaging 1.0 documentation link

Minimal structure

        
          ➜  development $ tree -L 2 VotePackage
          VotePackage
          ├── MANIFEST
          ├── README.txt
          ├── setup.py
          └── votepackage
              └── __init__.py
          2 directories, 6 files
        
      

Minimal setup.py data

        
          from distutils.core import setup
          setup(
              name='VotePackage',
              version='0.1dev',
              author='Vasyl Dizhak',
              author_email='vasyl.dizhak@djangostars.com',
              description='Centralized system for python package voting',
              packages=['votepackage',],
              license='Simplified BSD License',
              long_description=open('README.txt').read(),
          )
        
      

Minimal setup.py data: name

        
          ...
          name='VotePackage'
          ...
        
      
Has to be unique name.
        
          http://pypi.python.org/pypi/<name>
        
      

Minimal setup.py data: packages


packages=['votepackage', 'votepackage.tests']
        
      
Looking for path relative to the directory where your setup script lives.

                package_dir = {'': 'src/lib'}
            

Minimal setup.py data: package data


setup(...,
      package_data={'votepackage': ['stats/*.json']},
      ...
     )
        
      
All the files that match package_data will be added to the MANIFEST file if no template is provided.

Installing Additional Files


setup(...,
      data_files=[
          ('/etc/init.d', ['pypivote-server']),
          ]
      ...
     )
        
      
Recommended way in case you may need to place data files outside of you package.

Minimal setup.py data: version

PEP 386 -- Changing the version comparison module in Distutils PEP 386/
        
 >>> from distutils.version import LooseVersion as V
 >>> v1 = V('1.0dev')
 >>> v2 = V('1.0.9alpha')
 >>> v2 > v1
 >>> False
        
      
        
$ pip install -U name
        
      

Minimal setup.py data: author

People may buy you a beer www.gittip.com
        
  author='Vasyl Dizhak',
  author_email='vasyl.dizhak@djangostars.com',
  
  
  maintainer='Vasyl Dizhak',
  maintainer_email='vasyl.dizhak@djangostars.com',
 
        
      

Minimal setup.py data: description

May be a first or even the only place where users will look and decide whether they will use your package. PYPI will render long_description with docutils reStructuredText renderer

long_description=open('README.md').read(),
        
      

Minimal setup.py data: MANIFEST.in

Only Python modules and packages are included in the package by default. To include additional files, we’ll need to create a MANIFEST.in file

include LICENSE
include README.rst
recursive-include votepackage/static *
recursive-include votepackage/templates *
        
      

setup: requirements


        install_requires = [
            'requests',
            'pycli'
        ],

        
      

Rely on platform or python version


        if sys.version_info[:2] in ((2, 6), (3, 1)):
            # argparse has been added in Python 3.2 / 2.7
            requirements.append('argparse>=1.2.1')
        if 'win32' in str(sys.platform).lower():
            # Terminal colors for Windows
            requirements.append('colorama>=0.2.4')
        
      

Classifiers

PEP 301 -- Package Index and Metadata for Distutils PEP 0301

        classifiers=[
            'Development Status :: 1 - Planning',
            'Intended Audience :: Developers',
            'Environment :: Console',
            'Programming Language :: Python :: 2.5',
            'Programming Language :: Python :: 2.6',
            'Programming Language :: Python :: 2.7',
            'Programming Language :: Python :: 3.3'
            'Framework :: Django'
        ]
        python setup.py register --list-classifiers
        
      

Console scripts


        setup(...,
            scripts=['django/bin/django-admin.py']
         )

        entry_points={
            'console_scripts': [
                'pypivote=votepackage:main',
           ],
        
      

Lean release

$ python setup.py register

$ python setup.py sdist upload

What have been done?

  1. Build distributition VotePackage-0.1dev.tar.gz and upload it to PYPI

Testing

Tox

Use tox to automate package testing against different python versions

      
        import sys
        from setuptools.command.test import test as TestCommand
        class Tox(TestCommand):
            def finalize_options(self):
                TestCommand.finalize_options(self)
                self.test_args = []
                self.test_suite = True
            def run_tests(self):
                #import here, cause outside the eggs aren't loaded
                import tox
                errno = tox.cmdline(self.test_args)
                sys.exit(errno)
      
    

Tox.ini & setup.py

      
        # content of: tox.ini , put in same dir as setup.py
        [tox]
        envlist = py25,py26,py27,py33
        [testenv]
        deps=pytest       # install pytest in the venvs
        commands=py.test  # or 'nosetests' or ...

      
    
      
        # setup.py
        ...
        tests_require=['tox'], #install tox package
        cmdclass = {'test': Tox},
      
      
    

Test your package

      
        $ python setup.py test
        ...
        py27 inst-nodeps: /Users/rootart/development/
          VotePackage/.tox/dist/VotePackage-0.1dev.zip
        py27 runtests: commands[0]
        ...
        py25: commands succeeded
        py26: commands succeeded
        py27: commands succeeded
        py33: commands succeeded
        congratulations :)
      
    

Use CI system for constant builds

As a user

PIP & virtualenv

      
        $ virtualenv env
        $ pip install/uninstall package
        $ pip freeze > requirements-dev.txt
        $ pip install -r requirements-dev.txt
        $ pip install ... -M (--mirrors) #PEP-381
        $ pip install -e git/hg/svn
        $ pip list -o #get outdated packages
      
    

PIP useful configuration options

      
        # $HOME/.pip/pip.conf
        [global]
        timeout = 60
        index-url = http://local.cheeseshop/
        download_cache = ~/.cache/pip
      
    

The Wheel Binary Package Format

PEP 427, pythonwheels.com/
      
python setup.py bdist_wheel --universal
          ....
pip wheel --wheel-dir=/local/wheels -r requirements.txt
pip install --no-index --find-links=/local/wheels \
          -r requirements.txt
      
    

Twine is a utility for interacting with PyPI

pypi/twine
  1. Verified HTTPS Connections
  2. Uploading doesn't require executing setup.py
  3. Uploading files that have already been created, allowing testing of distributions before release
  4. Supports uploading any packaging format (including wheels)
twine upload dist/*

Future of python packaging

Future of python packaging

  1. standalone setup tool that install and uninstall packages

Some of the distutils2 features

PEP 426
      
        Requires-External: C
        Requires-External: libpng (>=1.5) #PIL :)
        
        Requires-Dist package; python_version == '2.5'
        
        Obsoletes-Dist: OtherProject (<3.0)
      
    

Say hello to PIP (PEP 453)

PIP is a part of standard library from python 3.4

Python Warehouse

  1. KISS

Thank you!

Question?

Fork me on Github