Archive
Faker : une bibliothèque python pour générer des noms aléatoirement
Faker est une bibliothèque python pour générer aléatoirement des noms, adresses, etc. Elle facilite l’écriture de script pour remplir une base de données avec de fausses informations diversifiées.
Les données produites peuvent être en français. Comme ça, vous pourrez avoir Bertrand Picard plutôt que Lisa Smith. L’anglais est la langue par défaut mais il est aussi possible d’avoir des résultats en italien, espagnol, russe, ukrainien, etc.
from faker import Factory
fake = Factory.create('fr_FR')
fake.name() # 'Laetitia Roger'
fake.name() #'Victoire de la Cohen'
fake.first_name() # 'Jeannine'
fake.first_name() # 'Christophe'
fake.first_name_female() # 'Alexandrie'
fake.first_name_male() # 'Rémy'
fake.address() # 'chemin Catherine Legendre\n33959 LerouxBourg'
fake.department_name() # 'Tarn'
La bibliothèque est disponible en python 2.7 et 3, sous licence MIT, et est activement maintenue.
La liste de champs possibles (nom, prénom, adresse, siren, etc.) est presqu’aussi longue qu’une boucle sans condition de fin. Certains champs ne sont pas encore traduits mais ils sont assez secondaires.
Et pour quelques elif de plus
Face à une succession de if-elif-elif-elif-… longue comme un générique de film, il peut être tentant de les remplacer par un switch-case comme on peut en voir en C, PHP, Java, etc. C’est simplement impossible en Python car cette instruction n’existe pas.
Plusieurs propositions pour l’inclure dans le langage ont été faites mais elles ont été rejetées car Guido Van Rossum ne les a pas trouvées convaincantes. Son argumentaire détaillé est disponible dans la PEP 3103 publiée en 2006.
L’enchaînement de conditions que l’on souhaite éviter :
if personnage == "Le manchot":
acteur = "Clint Eastwood"
elif personnage == "Colonel Douglas Mortimer":
acteur = "Lee Van Cleef"
elif personnage == "El Indio":
acteur = "Gian Maria Volontè"
#je vous fais grâce de toute la distribution
else:
acteur = None
La FAQ de la documentation python explique comment se passer du switch (pour python 2, pour python 3). Inutile le lire les deux, c’est la même réponse à chaque fois. Elle recommande l’utilisation d’un dictionnaire. C’est aussi la solution la plus courante que l’on trouve dans du code Python.
L’équivalent du code précédent avec un dictionnaire est :
d = {"Le manchot": "Clint Eastwood",
"Colonel Douglas Mortimer": "Lee Van Cleef",
"El Indio": "Gian Maria Volontè"}
acteur = d.get("El Indio", None)
Comme la structure switch n’est pas disponible dans le langage, plusieurs tentatives ont été faites pour l’ajouter à partir des structures existantes. Comme on peut s’y attendre, le code est plus verbeux et, à mon avis, la solution à base de dictionnaire reste préférable. Parmi les essais disponibles dans pypi.python.org, on peut trouver plusieurs choix d’interface :
Switchcase qui utilise l’instruction break de manière similaire au switch des autres langages : sans l’instruction break, les cas suivants sont automatiquement considérés comme vrai. Fonctionnalité permettant des comportments astucieuses mais qui est aussi un nid à bogues difficile à comprendre. C’est donc probablement une excellente idée de l’avoir ajoutée.
Switch qui a une interface me semblant plus simple à comprendre. La simulation du break est fait par un paramètre facultatif, ce qui s’écarte du switch canonique.
Pyswitch qui utilise les annotations pour définir les différents cas. Je ne suis pas sûr que ce soit le plus lisible mais c’est un choix original.
Une autre solution est de transformer le code en remplaçant les tests par du polymorphisme. Avec l’exemple précédent, on aurait :
class Personnage:
acteur = None
class LeManchot(Personnage):
acteur = "Clint Eastwood"
class ColonelDouglasMortimer(Personnage):
acteur = "Lee Van Cleef"
class ElIndio(Personnage):
acteur = "Gian Maria Volontè"
p.acteur # p étant une instance d'une des classes précédente
Il n’est pas nécessaire que p soit une instance héritant de Personnage. Il suffit que l’interface soit identique. Cette transformation n’est pas spécifique à Python, elle est utilisable dans tous les langages objet.
Redimensionner des images avec python-resize-image
python-resize-image est une bibliothèque python disponible sur pypi.python.org. C’est une surcouche à PIL qui simplifie son usage pour des redimensionnements courants d’image. Bien pratique, elle ne remplacera pas PIL pour toutes les autres possibilités comme les empilements d’image par exemple.
La bibliothèque est utilisable avec python2 et python3 mais le fichier wheel n’a été fait que pour la version 2.
Pour l’installer sur python2, pip install python-resize-image suffit.
Pour l’installer sur python3, il faut d’abord cloner le projet, créer l’archive avant de l’installer dans son virtualenv.
Cet article montre un rendu d’exemple pour chaque fonction.
L’image de départ fait 1000 × 413 pixels :
Crop : resize_crop()
Dimension d’arrivée : 800 x 700 pixels
L’image d’arrivée possède une zone transparente au-dessus et au-dessous de la photo.
Cover : resize_cover()
Dimension d’arrivée : 800 x 700 pixels
Contrairement au crop, l’image d’arrivée est complètement remplie par la photo d’origine
Contain : resize_contain()
Dimension d’arrivée : 800 x 700 pixels
Comme le crop, l’image d’arrivée possède une zone transparente au-dessus et au-dessous de la photo.
Largeur fixée : resize_width()
Dimension d’arrivée : 400 x 165 pixels
Pas de transparence. L’image d’arrivée est un redimensionnement de l’image de départ, sous la contrainte de la largeur donnée en paramètre de resize_with().
Hauteur fixée : resize_height()
Dimension d’arrivée : 969 x 400 pixels
Le comportement est équivalent à resize_width(), mais pour la hauteur.
Vignette : resize_thumbnail()
Dimension d’arrivée : 800 x 330 pixels
La transformation respecte le ratio de départ et redimensionne au mieux pour respecter les dimensions passées en paramètre. Dans l’exemple, les 800 pixels pour la largeur sont bien là mais les 700 pixels demandés pour la hauteur ont été réduits à 330.
Sources
J’ai pris la photo au Cap (en Afrique du Sud, pas en France) la semaine précédent la DebConf 16.
L’ensemble des images sont disponibles à http://stephane.yaal.fr/python-resize-image/ au cas où l’envoi sur WordPress ait modifié les données.
Le code source qui a permis de produire les images :
from PIL import Image
from resizeimage import resizeimage
with open('origine_1000.png', 'r') as f:
TAILLE = [800, 700]
FIXE = 400
img = Image.open(f)
img = resizeimage.resize_crop(img, TAILLE)
img.save('crop.png', img.format)
img = Image.open(f)
img = resizeimage.resize_cover(img, TAILLE)
img.save('cover.png', img.format)
img = Image.open(f)
img = resizeimage.resize_contain(img, TAILLE)
img.save('contain.png', img.format)
img = Image.open(f)
img = resizeimage.resize_width(img, FIXE)
img.save('width.png', img.format)
img = Image.open(f)
img = resizeimage.resize_height(img, FIXE)
img.save('height.png', img.format)
img = Image.open(f)
img = resizeimage.resize_thumbnail(img, TAILLE)
img.save('thumbnail.png', img.format)
Des graphiques à la Xkcd
Ou comment faire des graphiques dans le légendaire style de XKCD (une finesse du trait plus tranchante que Michel-Ange, des couleurs plus contrastées que Léonard de Vinci).
Les développeurs de Matplotlib l’ont fait et intégré à la bibliothèque. Globalement, il suffit d’initialiser le script python avec la fonction xkcd(). Cette fonction initialise des paramètres pour le rendu des graphiques.
with plt.xkcd():
#le code pour produire le graphique
Installation
Dans Debian, le paquet de base de Matplotlib est python-matplotlib pour python2 et python3-matplotlib pour python3.
Pour que le graphique ait une police similaire à ceux de xkcd, la police Humor Sans doit être installée. Dans Debian, elle se trouve dans le paquet fonts-humor-sans.
Il est possible d’avoir encore une erreur signalant que la police n’est pas trouvée :
/usr/lib/python2.7/dist-packages/matplotlib/font_manager.py:1288: UserWarning: findfont: Font family [u'Humor-Sans'] not found. Falling back to Bitstream Vera Sans
En réalité, elle est bien accessible par matplotlib mais la bibliothèque a construit un cache des polices disponibles lors de la création d’un autre graphique par le passé. Ce cache n’est pas vidé après l’installation de la police. L’erreur survient car matplotlib regarde dans son cache et non les polices actuellement installées sur le système. Il suffit donc de supprimer ce cache (fontList.cache pour python2 ou fontList.py3k.cache pour python3) et d’exécuter à nouveau le script.
stephane@foehn:~$ ls .cache/matplotlib/
fontList.cache fontList.py3k.cache tex.cache
stephane@foehn:~$ rm .cache/matplotlib/fontList.cache #si script lancé avec python2
Et là, ça marche ! 🙂
Évitez tout de même de mettre des accents, la police ne dispose pas de ces caractères et un « ? » est affiché à la place.
Versions des logiciels utilisés, code source
paquet python-matplotlib : 1.5.1-1
paquet fonts-humor-sans : 1.0-1
Le code source qui a permis de produire le magnifique graphique inséré au début de l’article :
from matplotlib import pyplot as plt
import numpy as np
with plt.xkcd():
fig = plt.figure()
ax = fig.add_subplot(1, 1, 1)
ax.spines['right'].set_color('none')
ax.spines['top'].set_color('none')
plt.xticks([])
plt.yticks([])
ax.set_ylim([-1, 2])
x = np.linspace(0, 10)
plt.plot(x, np.sin(x) + 0.3, '--')
plt.xlabel('abscisses')
plt.ylabel('ordonnees')
plt.title("c'est le plus beau graphique du monde !")
plt.savefig("/tmp/graph_xkcd.png")
hglib, la bibliothèque python pour s’interfacer avec Mercurial
Mercurial, un système de gestion de version décentralisé, propose hglib pour pouvoir agir sur un dépôt programmatiquement depuis Python.
Hglib est compatible avec python 2 et 3 donc, à moins de faire de l’archéologie logicielle (python 2.3 et encore plus vieux), il n’y a pas de raison de s’en priver.
L’installation est triviale puisque la bibliothèque est disponible à partir de pypi (donc utilisable dans un virtualenv) ou dans votre distribution préférée (paquet python-hglib ou python3-hglib avec Debian).
Créer un dépôt
>>> import hglib >>> depot = hglib.init(dest="/tmp/geographie") >>> print(depot) <hglib.client.hgclient object at 0x7f9e75dc2250>
Le résultat est équivalent à la commande :
$ hg init /tmp/geographie
Le résultat est banalement prévisible :
$ ls -la /tmp/geographie/ total 268 drwxr-xr-x 3 alice alice 4096 oct. 13 21:07 . drwxrwxrwt 17 root root 262144 oct. 13 21:08 .. drwxr-xr-x 3 alice alice 4096 oct. 13 21:07 .hg
Accéder au dépôt
Pour ouvrir le dépôt :
>>> import hglib
>>> depot = hglib.open("/tmp/geographie")
>>> print(depot)
<hglib.client.hgclient object at 0x7f2b230a8050>
Il est possible de voir et ajouter des branches :
>>> depot.branches()
[] #pas de branche particulière
>>> depot.branch()
'default' #'default' est la branche que Mercurial crée par défaut
>>> depot.branch("dev") #création et passage sur la branche 'dev'
'dev'
>>> depot.branch()
'dev'
>>> depot.branches()
[]
>>> depot.commit("ajout de la branche dev")
(0, 'f1c9646830e4efc57830ddd588001917c3046e5d')
>>> depot.branches()
[('dev', 0, 'f1c9646830e4')] #la branche 'dev' est maintenant visible
depot.commit() retourne un couple (numéro de révision, identifiant du nœud).
depot.branches() retourne la liste des branches, chacune avec le triplet (nom de la branche, numéro de révision, identifiant du nœud).
Ajouter des fichiers
Supposons que nous ayons un fichier listant les villes de France nommé villes.tsv et que nous décidions de l’ajouter à notre dépôt :
>>> depot.add("/tmp/geographie/villes.tsv") #équivaut à hg add /tmp/geographie/villes.tsv
True
>>> depot.status() #équivaut à hg status
[('A', 'villes.tsv')]
>>> depot.commit("ajout des villes") #équivaut à hg commit -m"ajout des villes"
(1, '815765cc96bde95ff8ca02305f798e1effc31688')
>>> depot.status()
[]
depot.status() retourne une liste des changements. Chaque changement est représenté par un couple (lettre_code, fichier). lettre_code vaut les valeurs classiques :
- M : fichier modifié
- A : fichier ajouté
- R : fichier supprimé
- etc.
Modifier des fichiers
Malheureusement, une erreur s’était glissée dans le fichier villes.tsv… Après la correction mais avant le commit :
>>> depot.diff() #le diff brut de décoffrage
'diff -r 815765cc96bd villes.tsv\n--- a/villes.tsv\tTue Oct 13 21:49:41 2015 +0200\n+++ b/villes.tsv\tTue Oct 13 22:13:12 2015 +0200\n@@ -13492,7 +13492,7 @@\n giey-sur-aujon\tGiey-sur-Aujon\n giez\tGiez\n giffaumont-champaubert\tGiffaumont-Champaubert\n-png-sur-yvette\tPng-sur-Yvette\n+gif-sur-yvette\tGif-sur-Yvette\n gigean\tGigean\n gignac-34\tGignac\n gignac-46\tGignac\n'
>>> for ligne in depot.diff().split('\n'):) #équivaut à hg diff
... print(ligne)
...
diff -r 815765cc96bd villes.tsv
--- a/villes.tsv Tue Oct 13 21:49:41 2015 +0200
+++ b/villes.tsv Tue Oct 13 22:13:37 2015 +0200
@@ -13492,7 +13492,7 @@
giey-sur-aujon Giey-sur-Aujon
giez Giez
giffaumont-champaubert Giffaumont-Champaubert
-png-sur-yvette Png-sur-Yvette
+gif-sur-yvette Gif-sur-Yvette
gigean Gigean
gignac-34 Gignac
gignac-46 Gignac
Après le commit, le diff est aussi vide qu’on peut s’y attendre :
>>> depot.commit("correction gif-sur-yvette")
(2, 'a972c3bee591b2ec549420ef6fbe36e2cc433189')
>>> depot.diff()
''
depot.diff() accepte un nom de fichier ou une plage de révisions en paramètre pour limiter les résultats.
Voir l’historique
L’équivalent de hg log est aussi disponible :
>>> import pprint # c'est juste pour la lisibilité
>>> pprint.pprint(depot.log())
[('2',
'a972c3bee591b2ec549420ef6fbe36e2cc433189',
'tip',
'dev',
'Alice <[email protected]>',
'correction gif-sur-yvette',
datetime.datetime(2015, 10, 13, 22, 21, 53)),
('1',
'815765cc96bde95ff8ca02305f798e1effc31688',
'',
'dev',
'Alice <[email protected]>',
'ajout des villes',
datetime.datetime(2015, 10, 13, 21, 49, 41)),
('0',
'f1c9646830e4efc57830ddd588001917c3046e5d',
'',
'dev',
'Alice <[email protected]>',
'ajout de la branche dev',
datetime.datetime(2015, 10, 13, 21, 18, 34))]
C’est une liste de commits. Chaque commit est un n-uplet (numéro de la révision, identifiant du nœud, tip ou pas, branche, auteur, message, date).
Créer une archive
Vu l’avancée pour l’humanité réalisée, il est temps de faire une archive du projet :
>>> depot.archive("/tmp/archive") # équivaut à hg archive /tmp/archive
Le répertoire obtenu :
$ ls -la /tmp/archive/ total 1200 drwxr-xr-x 2 alice alice 4096 oct. 13 22:41 . drwxrwxrwt 18 root root 262144 oct. 13 22:41 .. -rw-r--r-- 1 alice alice 168 oct. 13 22:41 .hg_archival.txt -rw-r--r-- 1 alice alice 948448 oct. 13 22:41 villes.tsv
Générique de fin
Ce n’était qu’un survol des possiblités de hglib. De nombreuses possibilités n’ont pas été abordées (ouvrir un dépôt distant, depot.pull(), depot.push(), la création de .tar.gz pour les archives, etc.). La plupart des méthodes prennent des paramètres facultatifs pour en modifier le comportement.
Les cascades de cet article ont été réalisées avec Python 2.7.10, Mercurial 3.4.2 et hglib 1.7-1. Vous pouvez les reproduire chez vous, même si vous n’êtes pas un professionnel.
Aucun terminal n’a été maltraité lors de la réalisation de cet article.






