Contenu

Faire un paquetage en python

Autant le dire tout de suite, ça parait simple, ça l’est mais quand vous cherchez ou demandez personne ne fait pareil. Du coup, la première fois c’est une galère totale. Voici l’arborescence d’un de mes programmes :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
    .
    ├── AUTHORS
    ├── cfg
    │   ├── imagesat_example.conf
    │   └── imagesat.service
    ├── CHANGELOG
    ├── imagesat
    ├── Imagesat
    │   ├── download.py
    │   ├── img_info.py
    │   ├── __init__.py
    │   ├── mylog.py
    │   └── number.py
    ├── INSTALL
    ├── LICENSE
    ├── MANIFEST.in
    ├── README.rst
    └── setup.py

Explications

Les répertoires

  • cfg Dans ce répertoire, je mets mes fichiers de config qui seront placés à plusieurs endroits du système.
  • Imagesat Dans ce répertoire qui porte le même nom que mon programme, je mets toutes les dépendances de mon programme qui iront se placer dans /usr/lib/pythonX.Y/site-packages/. Ce répertoire est inclu dans le path de python ce qui vous garanti que votre application retrouvera ses petits au lancement. Pourquoi dans ce répertoire ? C’est tout simple. Par exemple, lors d’une mise à jour de python, de la v3.3 à la v3.4, vous n’aurez pas besoin de réinstaller votre programme. Tout ce qui se trouve dans /usr/lib/pythonX.Y/site-packages/ sera «transféré» dans /usr/lib/pythonX.(Y+1)/site-packages/

Les fichiers

init.py Je vous le donne en brut et je fais les explications ensuite :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
    #!/usr/bin/env python3
    # -*- coding: utf-8 -*-
    
    __author__           = "mon nom et prénom"
    __author_email__     = "mon adresse email"
    __copyright__        = "Copyright (c) 2013, mon nom et prénom"
    __description__      = "Daemon can download satellite images"
    __long_description__ = """It's a python's script who download some pictures  \
            into /tmp directory (see imagesat_example.conf) and you can display  \
            them with conky or conky like (dzen2, ...). This script can search,  \
            with regexp, a picture to download on html page, even name of  \
            picture change all the time"""
    __license__          = "GPLv3"
    __name__             = "imagesat"
    __platforms__        = "GNU/Linux"
    __url__              = "https://github.com/Chipsterjulien/imagesat"
    __version__          = '0.3.1'
    
    from .download import download
    from .img_info import Img_info
    from .mylog import *
    from .number import is_number

Lignes 1 à 13

Elles se passent de commentaire.

Ligne 14

Attention, il faut mettre le nom du futur paquetage !

Lignes 15 à 16

Elles se passent de commentaire là aussi

Lignes 18 à 22

Afin de faire des vérifications (voir la dernière partie), j’inclus tout ce qui sert dans mon programme principal :

  • Fonctions
  • Classes

Pensez bien à mettre le . pour indiqué que ce que vous voulez inclure se trouve à partir du répertoire courant.

AUTHORS

Je pense que vous l’aurez deviné, j’y mets le nom des personnes ayant participé au projet.

CHANGELOG

J’y mets les changements que j’apporte au programme

imagesat

C’est «l’exécutable», celui qui sera placé dans /usr/bin

INSTALL

J’y décris la/les procédure(s) d’installation.

LICENSE

J’y mets sous quelle licence est distribué mon programme.

MANIFEST.in

Ce fichier est important. Au moment de la construction du paquetage, il permet de lister les fichiers que vous souhaitez y mettre. J’utilise 2 commandes :

  • include nom_du_fichier
  • recursive-include nom_du_rep joker

La première permet d’inclure des fichiers un par un alors que la seconde permet de le faire de manière récursive. Voici un exemple :

1
2
3
4
5
6
7
include AUTHORS
include INSTALL
include LICENSE
include MANIFEST.in
include README.rst
recursive-include Imagesat *
recursive-include cfg *

Si je ne veux prendre que les fichiers .py du répertoire Imagesat, je mets :

1
recursive-include Imagesat *.py

Pour plus d’informations, je vous renvoie sur la doc python

README.rst

Ce fichier me permet d’expliquer à quoi sert mon programme. C’est une reprise du fichier INSTALL en un peu plus détaillé. Il peut être au format md (MarkDown) ou rst (ReStructuredText). Je ne l’ai pas changé mais ma préférence se porte sur le .md

setup.py

C’est le fichier le plus important puisque c’est lui qui permet la création du paquetage. Je vous le donne en brut et je fais les explications ensuite :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
#!/usr/bin/env python3
# -*- coding: utf-8 -*-

"""
Imagesat: Daemon can download satellite images
This file is part of Imagesat.

See the file LICENSE for copying permission.
"""

try:
  from setuptools import setup, find_packages
except ImportError:
  from distutils.core import setup, find_packages

import Imagesat

CLASSIFIERS = [
  'Development Status :: 5 - Production/Stable',
  'Environment :: Console',
  'Intended Audience :: System Administrators',
  'License :: OSI Approved :: GNU General Public License v3 (GPLv3)',
  'Natural Language :: English',
  'Operating System :: POSIX :: Linux',
  'Programming Language :: Python :: 3',
  'Topic :: Internet',
]

DATA_FILES = [('/etc/imagesat', ['cfg/imagesat_example.conf']),
('/usr/lib/systemd/system', ['cfg/imagesat.service'])]

SCRIPTS = ['imagesat', ]

setup(
  name             = Imagesat.__name__,
  version          = Imagesat.__version__,
  description      = Imagesat.__description__,
  long_description = Imagesat.__long_description__,
  author           = Imagesat.__author__,
  author_email     = Imagesat.__author_email__,
  url              = Imagesat.__url__,
  license          = Imagesat.__license__,
  platforms        = Imagesat.__platforms__,
  data_files       = DATA_FILES,
  packages         = find_packages(),
  include_package_data = True,
  scripts          = SCRIPTS,
  requires         = ['requests', 'pyyaml', 'imaging', 'python (>=3.3)'],
  classifiers      = CLASSIFIERS,
)

Lignes 1 à 10

Je les passe sous silence 😉

Lignes 11 à 14

Ici, j’importe des lib qui me seront utiles quelques lignes plus bas. La ligne 14 importe Imagesat, ce qui aura pour effet de charger le fichier init.py vu plus haut. Cette inclusion nous assure qu’il n’y aura pas d’effet de bord.

Lignes 18 à 27

Les CLASSIFIERS vous permettent de classer votre programme selon certains critères. Vous trouverez votre bonheur à cette adresse.

Lignes 29 à 30

Dans DATA_FILES, je vais ranger tout ce qui aura besoin d’être dans un répertoire spécifique différent de /usr/lib/pythonX.Y/site-packages/. La déclaration se fait comme ceci :

1
2
DATA_FILES = [('/futur/emplacement/', ['emplacement/avant/creation_du_paquetage.conf']),
			  ('/autre/futur/emplacement/', 'conf/paquetage.conf']),]

Ligne 32

Vous y mettez vos futurs exécutable. Après installation, vous le trouverez dans /usr/bin/ sous GNU/Linux.

Lignes 34 à 50

C’est le gros du morceau ! Le but est de transmettre les informations nécessaires à la création du paquetage via la fonction setup(). Vous trouverez une liste exhaustive de ce qu’il est possible de mettre à cette adresse qui est la pep 0314.

Finalisation

Bon, nous y sommes ! Sauf qu’il serait quand même sympathique de pouvoir vérifier tout ça. Tout a été bien pensé, ne vous inquiétez pas. En console, lancez la commande :

1
python setup.py check

Si vous n’avez pas running check, pas la peine d’aller plus loin. Corrigez vos erreurs !