Comment exclure un module avec setuptools ?

Pour ceux ou celles, qui maintiennent (ou on l'intention de créer) des paquets Python pour leur distribution favorite, je vous livre une astuce si vous voulez exclure un, ou plusieurs modules.

Dans mon cas, je ne voulais pas installer le dossier tests/ (il dépend bien souvent de la bibliothèque nose), lors de la création de mon package.

Nous allons utiliser la fonction find_packages() du module setuptools.

Voici ce que nous dit la docstring :

[...]

FUNCTIONS

find_packages(where='.', exclude=()) Return a list all Python packages found within directory 'where'

'where' should be supplied as a "cross-platform" (i.e. URL-style) path; it will be converted to the appropriate local path syntax. 'exclude' is a sequence of package names to exclude; '*' can be used as a wildcard in the names, such that 'foo.*' will exclude all subpackages of 'foo' (but not 'foo' itself).

[...]

La documentation :

[...]

find_packages() takes a source directory, and a list of package names or patterns to exclude. If omitted, the source directory defaults to the same directory as the setup script.

Exclusion patterns are package names, optionally including wildcards.

[...]

Un exemple va nous aider à bien comprendre (on se place dans les « mêmes conditions », qu'au moment de la création d'un paquet) :

olivier@bornem:~ $ tar -xzf /usr/ports/distfiles/waitress-0.8.tar.gz
olivier@bornem:~ $ cd waitress-0.8/
olivier@bornem:~/waitress-0.8 $ python
Python 2.7.2 (default, Jul  4 2011, 20:20:01) 
[GCC 4.2.1 20070719  [FreeBSD]] on freebsd8
Type "help", "copyright", "credits" or "license" for more information.
>>> from setuptools import find_packages
>>> l = find_packages(where='.')
>>> l
['waitress', 'waitress.tests', 'waitress.tests.fixtureapps']
>>> 

on obient une liste, de tous les dossiers contenant un fichier __init__.py.

Si l'on ne souhaite pas installer le répertoire waitress/tests, il faut le mentionner :

>>> l = find_packages(where='.', exclude=['waitress.tests'])
>>> l
['waitress', 'waitress.tests.fixtureapps']
>>> 

waitress/tests/fixtureapps/ est toujours présent (la compilation échouera, car le module tests sera absent). Il faut également le préciser tous les sous-dossiers

>>> l = find_packages(where='.', exclude=['waitress.tests', 'waitress.tests.fixtureapps'])
>>> l
['waitress']
>>> 

On peut utiliser un joker (ou wildcard).

>>> l = find_packages(where='.', exclude=['waitress.test*'])
>>> l
['waitress']
>>> 

Le plus gros du travail est fait, il ne reste plus qu'à rechercher un fichier MANIFEST.in, ou SOURCES.txt, et de supprimer les lignes correspondantes à ce module (on crée alors un patch).

Une dernière chose, la fonction find_packages() se trouve dans le fichier setup.py, on va également le patcher.