Der Eierkocher

Das distutils & setuptools Rezeptbuch

Autor: Christopher Arndt
Datum: 2010-09-09
Revision: 466
Abstract:

Dies ist eine Sammlung von nützlichen Rezepten, mit Hilfe derer man seine Pythonprojekte für die Verteilung und Installation via distutils und setuptools einrichtet.

This is a collection of useful recipes to make your Python projects ready for distribution and installation via distutils and setuptools.

Inhalt

Einführung

Funktionen von distutils:

Funktionen von setuptools:

Ein Pythonprojekt für distutils einrichten

Rezept Nr. 1: paketlose Pythonmodule #1

Beispielprojekt:
recipes/recipe01/

setup.py von Beispielprojekt recipe01:

#!/usr/bin/env python

from distutils.core import setup

setup(
    name = 'datehelper',
    version = '1.0',
    description = 'A simple module with helper functions for handling dates',
    author = 'Christopher Arndt',
    author_email = 'chris@chrisarndt.de',
    url = 'http://chrisarndt.de/talks/cooking-eggs/',
    py_modules = ['date'],
)

Das Argument py_modules listet alle Pythonmodule auf, die zur Distribution gehören. Diese werden bei der Installation normalerweise direkt in das site-packages-Verzeichnis der systemweiten Pythoninstallation kopiert.

Rezept Nr. 2: paketlose Pythonmodule #2

Beispielprojekt:
recipes/recipe02/

Für jedes aufgelistete Modul muss eine entsprechende Pythonmoduldatei relativ zum Verzeichnis von setup.py vorhanden sein. Oder das Verzeichnis, in dem die Moduldateien liegen, muss mit dem package_dir-Argument angegeben werden.

Weitere Beispiele:

py_modules = ['foo', 'util', 'versioninfo']
package_dir = {'': 'src'}
py_modules = ['foo']
Weiterführende Dokumentation:

Rezept Nr. 3: Pythonpakete

Beispielprojekt:
recipes/recipe03/

Sobald das Projekt mehrere Pythonmodule hat, ist es sinnvoll, diese in einem Pythonpaket zu organisieren. Danach muss setup.py angepasst werden.

Man kann entweder einzelne Module separat auflisten:

py_modules = ['pkg1.foo', 'pkg2.bar', 'api']

Bei diesem Beispiel müssen die Module in folgender Verzeichnisstruktur vorliegen:

<project>/
    pkg1/
        __init__.py
        foo.py
    pkg2/
        __init__.py
        bar.py
    api.py
    setup.py

Oder man kann einfach alle Pakete mit dem packages-Argument auflisten:

packages = ['pkg1', 'pkg2']

Jedes aufgelistete Paketverzeichnis muss eine __init__.py Datei enthalten. Auch hier kann wieder package_dir verwendet werden, um die Verzeichnisstruktur des Projekts anzupassen:

package_dir = {'': 'src'}
packages = ['pkg1', 'pkg2']

setup.py von Beispielprojekt recipe03:

#!/usr/bin/env python

from distutils.core import setup

setup(
    name = 'datehelper',
    version = '1.2',
    description = 'A simple module with helper functions for handling dates',
    author = 'Christopher Arndt',
    author_email = 'chris@chrisarndt.de',
    url = 'http://chrisarndt.de/talks/cooking-eggs/',
    packages = ['date'],
)
Weiterführende Dokumentation:

Rezept Nr. 4: C-Extensionmodule für distutils einrichten

Beispielprojekt:
recipes/recipe04/

C-Extensionmodule, die als Sourcecode vorliegen, können von distutils bei der Installation automatisch kompiliert werden. Dazu müssen die Extensionmodule in setup.py folgendermaßen deklariert werden.

setup.py von Beispielprojekt recipe04:

from distutils.core import Extension, setup

setup(
    name = 'datehelper',
    version = '1.3',
    description = 'A simple module with helper functions for handling dates',
    author = 'Christopher Arndt',
    author_email = 'chris@chrisarndt.de',
    url = 'http://chrisarndt.de/talks/cooking-eggs/',
    packages = ['date'],
    ext_modules = [Extension('date.time', ['date/time.c'])]
)

Beachte, dass die Extension-Klasse zusätzlich importiertwerden muss und dass das ext_modules-Argument eine Liste von Instanzen dieser Klasse erwartet.

Werden für die Kompilierung zusätzlich (Pre-)Compiler- oder Linkerflags benötigt, können diese auf folgende Weise angegeben werden:

setup(
    ...,
    ext_modules = [
        Extension(....,
            define_macros=[('NDEBUG', '1'), ('POSIX', 'None')],
            include_dirs=['/usr/include/X11'],
            library_dirs=['/usr/X11R6/lib'],
            libraries=['X11', 'Xt'],
            extra_compile_args = ['-03'],
            extra_link_args = ['...']
        )
    ]
)
Weiterführende Dokumentation:

Rezept Nr. 5: Ein Projekt auf PyPI registrieren

Beispielprojekt:
recipes/recipe05/

Der Registrierungvorgang ist einfach. Im Verzeichnis, in dem die setup.py-Datei des Projekts liegt, folgende Shell-Komamndos ausführen:

$ python setup.py register

Danach sollte das Projekt unter http://pypi.python.org/pypi/<project> mit Beschreibung, URLs usw. aufgeführt sein.

Weiterführende Dokumentation:

Rezept Nr. 6: Ein Pythonprojekt für setuptools einrichten

Beispielprojekt:
recipes/recipe06/

Wenn ein Projektzusätzlich für setuptools eingerichtet wird, lassen sich u.a. auch Egg-Distributionen erstellen und Abhängigkeiten zu anderen Distributionen deklarieren.

setup.py von Beispielprojekt recipe04 (Auszug):

#!/usr/bin/env python

try:
    import ez_setup
    ez_setup.use_setuptools()
    from setuptools import setup
except:
    from distutils.core import setup

from distutils.core import Extension

setup(
    name = 'datehelper',
    version = '1.5',
    ...
)

Wenn die Distribution nun von den Sourcen mit ` python setup.py install` installiert wird, wird setuptools, falls es beim Benutzer noch nicht vorhanden ist, automatisch heruntergeladen. Falls dies auch schiefläuft, wird weiterhin distutils benutzt, so dass der Beutzer die Distribution auch notfalls ohne setuptools installieren kann. Dies funktioniert natürlich nur so lange wie der Aufruf von setup() kompatibel mit distutils bleibt.

Die Instalation erzeugt nun ein versioniertes Python-Egg, dass in das Python site-packages-Verzeichnis installiert wird. Falls die Distribution C-Extensionmodule enthält, ist das Egg plattformspezisch, da es vorkompilierte Binärmodule enthält. Dadurch bracuht der Benutzer zur Installtion keinen Compiler.

Weiterführende Dokumentation:

Sourcedistributionen und Eggs erstellen

Rezept Nr. 7: Sourcedistributionen

Für ein für distutils vorbereitetes Projekt kann folgendermaßen eine Sourcedistribution in verschiedenen Archivformaten erstellt werden:

$ python setup.py sdist --formats=zip,gztar,bztar

Die Distributionsarchive werden im Unterverzeichnis dist erstellt. Die Dateinamen der Archive setzen sich aus dem Projektnamen und der Versionsnummer, die in setup.py eingetragen sind, zusammen.

Rezept Nr. 8: Egg-Distributionen

Will man eine Egg-Dsitribution erstellen, ohne sie zu installieren kann man folgendes Kommando benutzen:

$ python setup.py bdist_egg

Die Egg-Dateien werden ebenso im dist-Unterverzeichnis abgelegt.

Rezept Nr. 9: Sourcedistributionen und Eggs auf PyPI hochladen

Egg-Distribution erstellen und hochladen:
python setup.py bdist_egg upload
Source-Distributionen erstellen und hochladen:
python setup.py sdist --formats=zip,gztar,bztar upload

Rezept Nr 10: Sonstige Tipps und Tricks zum Umgang mit setuptools

Beispielprojekt:
recipes/recipe10/

Rezept nr. 11: Einbindung von Testsuites in setup.py

Noch nicht ganz fertig...

setup(
    ...,
    test_suite = 'nose.collector',
)

Zukünftige Rezepte

Verweise

SVN-Repository mit den Beispielprojekten und die ReST-Sourcen für diesen Text ausschecken:

$ svn co svn://svn.chrisarndt.de/talks/cooking-eggs

Online-Version dieses Texts und die Distributionspakete für die Beispielprojekte:

http://chrisarndt.de/talks/cooking-eggs