Python gestion et déploiement de modules externes

Il existe un projet qui permet de gérer plus facilement les modules externes

Comme beaucoup de projet l’arrivée de Python3 a déclaré la fin de certains projets et la création de nouveau.

Installation d’un module externe

Python < 2.8

La solution la plus simple est d’utiliser easy_install

Pour l’installer il suffit d’aller sur https://pypi.python.org/pypi/setuptools puis de télécharger et de lancer l’executable

cela génère dans le dossier scripts de python un excecutable nommé easy_install

Pour l’utiliser rien de plus simple

easy_install jinja2

Python > 3.0

La il faut changer d’outil pour utiliser pip

Aller sur http://www.pip-installer.org/en/latest/installing.html et suivre les instructions en lançant get-pip.py

Sinon on peut le faire manuellement, il faut tout d’abord télécharger et installer distribute sur https://pypi.python.org/pypi/distribute (version 0.6.49)

Puis pour lancer l’installation

python setup.py install

faire la même chose avec pip sur https://pypi.python.org/pypi/pip/1.2.1

python setup.py install

cela génère dans le dossier scripts de python un excecutable nommé pip

pour l’utilisation rien de plus simple

pip install simplejson
pip install --upgrade simplejson
pip uninstall simplejson

Il est possible d’utiliser pip à travers un proxy

pip install --proxy=myboite\mylogin:mypassword@proxy.myboite.fr:8080 ablog

Création d’un module externe

Il existe plusieurs moyens pour fournir à d’autres des outils développés en python. Suivant ce qui a été produit (application ou module) on peut imaginer leurs déploiement par:

  • un installateur (un msi ou exe pour windows, un deb ou rpm pour linux, ...)
  • un egg: un fichier zip directement utilisable par python
  • l’utilisation de pip ou setuptools

Nous allons voir comment comment utiliser setuptools (ed donc pip) pour réaliser tout cela.

Nous allons imaginer de créer le module mytools ayant la structure suivante

mytools
|_ mytools
|  |_ __init__.py
|  |_ parser.py
|  |_ sample
|     |_ sample1.py
|_ setup.py

Le fichier __init__.py

#!/usr/bin/env python
# -*- coding: utf-8 -*-

"""
    Module mytools.
    Tout les outils idispensables pour python
"""

__version__ = "0.0.1"

from parser import OptionParser

Note

l’import d’OptionParser dans le init permet de faire “from mytools import OptionParser” au lieu de “from mytools.parser import OptionParser”. cela est donc plus simple pour importer des classes importantes du module

Le fichier parser.py

#!/usr/bin/env python
# -*- coding: utf-8 -*-

"""
    Implémentation de parser avec de la couleur
"""

import colorconsole, colorconsole.terminal
import os, sys
import optparse

__all__ = ['OptionParser']

import colorconsole, colorconsole.terminal
import os, sys
import optparse

class OptionParser(optparse.OptionParser):

    def __init__(self, screen=None, **kw):
        optparse.OptionParser.__init__(self, **kw)
        if screen == None:
            screen = colorconsole.terminal.get_terminal()
        self.screen = screen

    def exit(self, status=0, msg=None):
        if msg:
            sys.stderr.write(msg)
        self.screen.set_color(colorconsole.terminal.colors["WHITE"], colorconsole.terminal.colors["BLACK"])
        sys.exit(status)

    def error(self, msg):
        self.screen.set_color(colorconsole.terminal.colors["LRED"], None)
        optparse.OptionParser.error(self, msg)

    def print_version(self, file=None):
        self.screen.set_color(colorconsole.terminal.colors["LGREEN"],None)
        optparse.OptionParser.print_version(self, file)
        self.screen.set_color(colorconsole.terminal.colors["WHITE"], colorconsole.terminal.colors["BLACK"])

    def print_help(self, file=None):
        self.screen.set_color(colorconsole.terminal.colors["LGREEN"],None)
        optparse.OptionParser.print_help(self, file)
        self.screen.set_color(colorconsole.terminal.colors["WHITE"], colorconsole.terminal.colors["BLACK"])

    def print_info(self, msg=""):
        self.screen.set_color(colorconsole.terminal.colors["LGREEN"],None)
        print(msg)
        self.screen.set_color(colorconsole.terminal.colors["WHITE"], colorconsole.terminal.colors["BLACK"])

    def print_stdout(self, msg, fg="WHITE", bg="BLACK"):
        self.screen.set_color(colorconsole.terminal.colors[fg],colorconsole.terminal.colors[bg])
        sys.stdout.write(msg)
        self.screen.set_color(colorconsole.terminal.colors["WHITE"], colorconsole.terminal.colors["BLACK"])

    def get_formatwidth(self, default=80):
        try:
            width = int(os.environ['COLUMNS'])
        except (KeyError, ValueError):
            width = default
        return "%-"+str(width)+"s"

Note

Il est important de documenter son module. l’utilisation de __all__ permet de n’autoriser que certains éléments a être importable ... afin de faciliter la gestion du module pour les utilisateurs

Le fichier sample1.py

import sys, traceback
from mytools.parser import OptionParser
import os

VERSION="0.1"
PROG="sample1"
DESCRIPTION="""description of sample1"""
AUTHOR="Frederic Aoustin"

if __name__ == '__main__':
    usage = "usage: %prog [options]"
    parser = OptionParser(version="%s %s" % (PROG,VERSION), usage=usage)
    parser.description= DESCRIPTION
    parser.epilog = AUTHOR
    parser.add_option("-a","--arg",
        dest = "myarg",
        action = "store_true",
        help = "arg by default False",
        default = False)
    try:
        (options, args) = parser.parse_args()
        if options.myarg:
            parser.error("myarg is True, error ;-)")
        sys.exit()
    except Exception as e:
        parser.error(e)
        sys.exit(1)

On peut maintenant tester notre module mytools

C:\Users\fraoustin\Google Drive\project\mytools>set PYTHONPATH=%PYTHONPATH%;.

C:\Users\fraoustin\Google Drive\project\mytools>python mytools\sample\sample1.py -h
Usage: sample1.py [options]

description of sample1

Options:
  --version   show program's version number and exit
  -h, --help  show this help message and exit
  -a, --arg   arg by default False

Frederic Aoustin

Note

on remarque que nous modifions notre PYTHONPATH afin de pouvoir charger notre module Notre installateur nous permettra d’éviter cette action

Le fichier setup.py

#!/usr/bin/env python
# -*- coding: utf-8 -*-

from setuptools import setup, find_packages

import mytools

setup(

    name='mytools',
    version=mytools.__version__,
    packages=find_packages(),
    author="Frederic Aoustin",
    author_email="fraoustin@gmail.com",
    description="mes outils pour python",
    long_description="mes super outils pour python",
    install_requires= ['colorconsole'], #on peut preciser une version "colorconsole >= 0.1"
    include_package_data=False, #mettre True si vous avez un fichier manifest.ini

    url='http://mypage.com',

    # liste des marqueurs disponible sur:https://pypi.python.org/pypi?%3Aaction=list_classifiers
    classifiers=[
        "Programming Language :: Python",
        "Development Status :: 3 - Alpha",
        "License :: OSI Approved",
        "Natural Language :: French",
        "Operating System :: OS Independent",
        "Programming Language :: Python :: 2.7",
        "Programming Language :: Python :: 3",
        "Programming Language :: Python :: 3.0",
        "Programming Language :: Python :: 3.1",
        "Programming Language :: Python :: 3.2",
        "Programming Language :: Python :: 3.3",
        "Programming Language :: Python :: 3.4",
        "Topic :: Communications",
        "Topic :: Terminals",
    ],


    # La syntaxe est "nom-de-commande-a-creer = package.module:fonction".
    #entry_points = {
    #    'console_scripts': [
    #        'proclame-sm = sm_lib.core:proclamer',
    #    ],
    #},

)

Nous n’utilisons pas de fichier manifest.ini mais il peut être utile si on ne souhaite pas par exemple exclure le répertoire sample mais inclure des fichiers jpg par exemple

exclude sample
include *.jpg

La notion de entry_point permet d’obtenir des scripts qui lance automatiquement la fonction identifiée.

Maintenant on peut:

  • installer notre module
python setup.py install
  • créer une source de distribution
python setup.py sdist --formats=tar,zip
  • créer un module eegs
python setup.py bdist_egg
  • créer un installateur windows
python setup.py bdist_wininst

Pour ajouter notre module sur le site pypi

créer un nouveau compte si besoin (option 2)

python setup.py register

ce connecter à son compte (option 1)

python setup.py register

cela permet de créer un fichier .pypirc au niveau de votre home contenant

[distutils]
index-servers =
    pypi

[pypi]
username:fraoustin
password:V=4/3pir3

puis par la suite créer une source de distribution et la mettre en ligne

python setup.py sdist upload

Note

si vous avez une erreur 401 d’authentification il faut mettre à jours le module distribute via un “pip install –upgrade distribute”

vous pouvez maintenant l’installer depuis pypi

pip install mytools

La comparaison de version pour l’outil est réalisée ainsi

>>> from distutils.version import StrictVersion
>>> StrictVersion('10.4.10') > StrictVersion('10.4.9')
True