PROJET AUTOBLOG


Sam et Max

source: Sam et Max

⇐ retour index

L’encoding en Python, une bonne fois pour toute

dimanche 21 avril 2013 à 09:02

J’avais oublié la zik, je rajoute:

Vous avez tous un jour eu l’erreur suivante :

UnicodeDecodeError: 'machine' codec can't decode character 'trucmuche' in position x: ordinal not in range(z)

Et là, pour vous en sortir, vous en avez chié des ronds de paté.

Le problème vient du fait que la plupart du temps, ignorer l’encoding marche : nous travaillons dans des environnements homogènes et toujours avec des données dans le même format, ou un format plus ou moins compatible.

Mais le texte, c’est compliqué, terriblement compliqué, et le jour où ça se gâte, si vous ne savez pas ce que vous faites, vous ne vous en sortirez pas.

C’est d’autant plus vrai en Python car :

A la fin de cet article, vous saurez vous sortir de toutes les situations merdiques liées aux encodages.

Règle numéro 1 : Le texte brut n’existe pas.

Quand vous avez du texte quelque part (un terminal, un fichier, une base de données…), il est forcément représenté sous forme de 0 et de 1.

La corrélation entre cette suite de 0 et de 1 et la lettre est faite dans un énorme tableau qui contient toutes les lettres d’un côté, et toutes les combinaisons de 0 et de 1 de l’autre. Il n’y a pas de magie. C’est un énorme tableau stocké quelque part dans votre ordinateur. Si vous n’avez pas ce tableau, vous ne pouvez pas lire du texte. Même le texte le plus simple.

Malheureusement, au début de l’informatique, presque chaque pays a créé son propre tableau, et ces tableaux sont incompatibles entre eux : pour la même combinaison de 0 et de 1, ils donnent un caractère différent voir rien du tout.

La mauvaises nouvelle, c’est qu’ils sont encore utilisés aujourd’hui.

Ces tableaux, c’est ce qu’on appelle les encodings, et il y en a beaucoup. Voici la liste de ceux que Python gère :

>>> import encodings
>>> print ''.join('- ' + e + '\n' for e in sorted(set(encodings.aliases.aliases.values())))
- ascii
- base64_codec
- big5
- big5hkscs
- bz2_codec
- cp037
- cp1026
- cp1140
- cp1250
- cp1251
- cp1252
- cp1253
- cp1254
- cp1255
- cp1256
- cp1257
- cp1258
- cp424
- cp437
- cp500
- cp775
- cp850
- cp852
- cp855
- cp857
- cp858
- cp860
- cp861
- cp862
- cp863
- cp864
- cp865
- cp866
- cp869
- cp932
- cp949
- cp950
- euc_jis_2004
- euc_jisx0213
- euc_jp
- euc_kr
- gb18030
- gb2312
- gbk
- hex_codec
- hp_roman8
- hz
- iso2022_jp
- iso2022_jp_1
- iso2022_jp_2
- iso2022_jp_2004
- iso2022_jp_3
- iso2022_jp_ext
- iso2022_kr
- iso8859_10
- iso8859_11
- iso8859_13
- iso8859_14
- iso8859_15
- iso8859_16
- iso8859_2
- iso8859_3
- iso8859_4
- iso8859_5
- iso8859_6
- iso8859_7
- iso8859_8
- iso8859_9
- johab
- koi8_r
- latin_1
- mac_cyrillic
- mac_greek
- mac_iceland
- mac_latin2
- mac_roman
- mac_turkish
- mbcs
- ptcp154
- quopri_codec
- rot_13
- shift_jis
- shift_jis_2004
- shift_jisx0213
- tactis
- tis_620
- utf_16
- utf_16_be
- utf_16_le
- utf_32
- utf_32_be
- utf_32_le
- utf_7
- utf_8
- uu_codec
- zlib_codec

Et certains ont plusieurs noms (des alias), donc on pourrait en compter plus:

>>> len(encodings.aliases.aliases.keys())
307

Quand vous affichez du texte sur un terminal avec un simple print, votre ordinateur va implicitement chercher le tableau qu’il pense être le plus adapté, et fait la traduction. Même pour le texte le plus simple. Même pour un espace tout seul.

Mais surtout, ça veut dire que votre propre code EST dans un encoding. Et vous DEVEZ savoir lequel.

Règle numéro 2 : utf8 est le langage universel, utilisez le

Il existe un encoding qui essaye des regrouper toutes les langues du monde, et il s’appelle unicode. Unicode est un tableau gigantesque qui contient des combinaisons de 1 et de 0 d’un côté, et les caractères de toutes la langues possibles de l’autre : chinois, arabe, français, espagnol, russe…

Bon, il ne contient pas encore absolument tout, mais il couvre suffisamment de terrain pour éliminer 99.999999999 des problèmes de communications de texte entre machines dans le monde.

Le défaut d’unicode est qu’il est plus lent et prend plus de place que d’autres représentations du même texte. Aujourd’hui le téléphone le plus pourri à 10 fois la puissance nécessaire, et ce n’est plus un souci : il peut être utilisé presque partout (sauf peut être dans l’embarqué drastique) sans même réfléchir à la question. Tous les langages les plus importants, tous les services les plus importants, tous les logiciels les plus importants gèrent unicode.

Il y a plusieurs implémentations concrètes d’unicode, la plus célèbre est “UTF 8″.

Moralité, par défaut, utilisez utf-8.

Une fois, à l’entretien d’embauche, un mec m’avait reproché d’utiliser UTF8 parce que “ça posait des problèmes d’encoding”. Comprenez bien qu’utf-8 ne pose aucun problème d’encoding. Ce sont tous les autres codecs du monde qui posent des problèmes d’encoding. UTF-8 est certainement le seul à justement, ne poser aucun problème.

UTF 8 est le seul encoding vers lequel, aujourd’hui, on puisse convertir vers et depuis (pratiquement) n’importe quel autre codec du monde. C’est un espéranto. C’est une pierre de rosette. C’est au texte ce que l’or est à l’économie.

Si UTF8 vous pose “un problème d’encoding”, c’est que vous ne savez pas dans quel encoding votre texte est actuellement ou comment le convertir. C’est tout.

Il n’y a presque aucune raison de ne pas utiliser UTF8 aujourd’hui (à part sur des vieux systèmes ou des systèmes où les ressources sont tellement limitées que vous n’utiliseriez pas Python de toute façon).

Utilisez utf8. Partout. Tout le temps.

Si vous communiquez avec un système qui ne comprend pas UTF8, convertissez.

Mais gardez votre partie en UTF8.

Règle numéro 3 : il faut maîtriser l’encoding de son code

Le fichier dans lequel vous écrivez votre code est dans un encoding et ce n’est pas lié à votre OS. C’est votre éditeur qui s’en occupe. Apprenez à régler votre éditeur pour qu’il utilise l’encoding que vous voulez.

Et l’encoding que vous voulez est UTF8.

Si vous ne savez pas dans quel encoding est votre code, vous ne pouvez pas manipuler du texte et garantir l’absence de bug.

Vous ne POUVEZ PAS.

Donc réflexe : vous configurez votre éditeur de texte pour sauvegarder tous vos nouveaux fichiers par défaut en UTF8. Maintenant. Toute de suite.

Regardez dans la doc de l’éditeur, dans l’aide ou tapez sur Google, mais faites le.

Puis il faut déclarer cet encoding à la première ligne de chaque fichier de code avec l’expression suivante :

# -*- coding: encoding -*-

Par exemple :

# -*- coding: utf8 -*-

C’est une spécificité de Python : si l’encoding du fichier est différent de l’encoding par défaut du langage, il faut le déclarer sinon le programme plantera à la première conversion. En Python 2.7, l’encoding par défaut est ASCII, donc il faut presque toujours le déclarer. En Python 3, l’encoding par défaut est UTF8 et on peut donc l’omettre si on l’utilise. Ce que vous allez faire après la lecture de cet article.

Ensuite, il existe deux types de chaînes de caractères en Python :

Illustration :

$ python2.7
Python 2.7.3 (default, Aug  1 2012, 05:14:39) 
[GCC 4.6.3] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> type('chaine') # bits => encodée
<type 'str'>
>>> type(u'chaine') # unicode => décodée
<type 'unicode'>
$ python3
Python 3.2.3 (default, Oct 19 2012, 20:10:41) 
[GCC 4.6.3] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> type("chaine") # unicode => decodée
<class 'str'>
>>> type(b"chaine") # bits => encodée
 
<class 'bytes'>

Votre but, c’est de n’avoir dans votre code que des chaînes de type ‘unicode’.

En Python 3, c’est automatique. Toutes les chaînes sont de type ‘unicode’ (appelé ‘str’ dans cette version, je sais, je sais, c’est confusionant à mort) par défaut.

En Python 2.7 en revanche, il faut préfixer la chaîne par un u.

Donc, dans votre code, TOUTES vos chaînes doivent être déclarées ainsi :

u"votre chaîne"

Oui, c’est chiant. Mais c’est indispensable. Encore une fois, il n’y a pas d’alternative (dites le avec la voix de Thatcher si ça vous excite).

Si vous voulez, vous pouvez activer le comportement de Python 3 dans Python 2.7 en mettant ceci au début de CHACUN de vos modules :

from __future__ import unicode_literals

Ceci n’affecte que le fichier en cours, jamais les autres modules.

On peut le mettre au démarrage d’iPython également.

Je résume :

Si vous ne faites pas cela, votre code marchera. La plupart de temps. Et un jour, dans une situation particulière, il ne marchera plus. Plus du tout.

Oh, et ce n’est pas grave si vous avez d’anciens modules dans d’autres encodings. Tant que vous utilisez des objets ‘unicode’ partout, ils marcheront sans problème ensemble.

Règle numéro 4 : décodez toute les entrées de votre programme

La partie difficile de ce conseil, c’est de savoir ce qu’est une entrée.

Je vais vous donner une définition simple : tout ce qui ne fait pas partie du code de votre programme et qui est traité dans votre programme est une entrée.

Le texte des fichiers, le nom de ces fichiers, le retour des appels système, le retour d’une ligne de commande parsée, la saisie utilisateur sur un terminal, le retour d’une requête SQL, le téléchargement d’une donnée sur le Web, etc.

Ce sont toutes des entrées.

Comme tous les textes du monde, les entrées sont dans un encoding. Et vous DEVEZ savoir lequel.

Comprenez bien, si vous ne connaissez pas l’encoding de vos entrées, ça marchera la plupart du temps, et un jour, ça va planter.

Il n’y a pas d’alternative (bis).

Or, il n’y a pas de moyen de détecter un encoding de façon fiable.

Donc, soit le fournisseur de la donnée vous donne cette information (settings dans la base de données, doc de votre logiciel, configuration de votre OS, spec du client, coup de fils au fournisseur…), soit vous êtes baisés.

On ne peut pas lire un simple fichier si on ne connait pas son encoding. Point.

Si cela a marché jusqu’ici pour vous, c’est que vous avez eu de la chance : la plupart de vos fichiers étaient dans l’encoding de votre éditeur et de votre système. Tant qu’on travaille sur sa machine, tout va bien.

Si vous lisez une page HTML, l’encoding est souvent déclaré dans la balise META ou dans un header.

Si vous écrivez dans un terminal, l’encoding du terminal est accessible avec sys.(stdin|stdout).encoding.

Si vous manipulez des noms de fichier, on peut récupérer l’encoding du file system en cours avec sys.getfilesystemencoding().

Mais parfois il n’y a pas d’autres moyen d’obtenir cette information que de demander à la personne qui a produit la donnée. Parfois même, l’encoding déclaré est faux.

Dans tous les cas, vous avez besoin de cette information.

Et une fois que vous l’avez, il faut décoder le texte reçu.

La manière la plus simple de faire cela est :

votre_chaine = votre_chaine.decode('nom_du_codec')

Le texte sera de type ‘str’, et decode() retourne (si vous lui fournissez le bon codec ;-)), une version ‘unicode’.

Exemple, obtenir une chaîne ‘unicode’ depuis une chaîne ‘str’ encodée en utf8 :

>>> une_chaine = 'Chaîne' # mon fichier est encodé en UTF8, donc la chaine est en UTF8
>>> type(une_chaine)
<type 'str'>
>>> une_chaine = une_chaine.decode('utf8')
>>> type(une_chaine)
<type 'unicode'>

Donc dès que vous lisez un fichier, récupérez une réponse d’une base de données ou parsez des arguments d’un terminal, appelez decode() sur la chaîne reçue.

Règle numéro 5 : encodez toutes les sorties de votre programme

La partie difficile de ce conseil, c’est de savoir ce qu’est une sortie.

Encore une fois, une définition simple : toute donnée que vous traitez et qui va être lue par autre chose que votre code est une sortie.

Une print dans un terminal est une sortie, une write() dans un fichier est une sortie, un UPDATE en SQL est une sortie, un envoie dans une socket est un sortie, etc.

Le reste du monde ne peut pas lire les objets ‘unicode’ de Python. Si vous écrivez ces objets dans un fichier, un terminal ou dans une base de données, Python va les convertir automatiquement en objet ‘str’, et l’encoding utilisé dépendra du contexte.

Malheureusement, il y a une limite à la capacité de Python à décider du bon encoding.

Donc, tout comme il vous faut connaitre l’encoding d’un texte en entrée, il vous faut connaitre l’encoding attendu par le système avec lequel vous communiquez en sortie : sachez quel est l’encoding du terminal, de votre base de données ou système de fichiers sur lequel vous écrivez.

Si vous ne pouvez pas savoir (page Web, API, etc), utilisez UTF8.

Pour ce faire, il suffit d’appelez encode() sur tout objet de type ‘unicode’ :

une_chaine = une_chaine.encode('nom_du_codec')

Par exemple, pour convertir un objet ‘unicode’ en ‘str’ utf8:

>>> une_chaine = u'Chaîne'
>>> type(une_chaine)
<type 'unicode'>
>>> une_chaine = une_chaine.encode('utf8')
>>> type(une_chaine)
<type 'str'>

Résumé des règles

  1. Le texte brut n’existe pas.
  2. Utilisez UTF8. Maintenant. Partout.
  3. Dans votre code, spécifiez l’encoding du fichier et déclarez vos chaînes comme ‘unicode’.
  4. À l’entrée, connaissez l’encoding de vos données, et décodez avec decode().
  5. A la sortie, encodez dans l’encoding attendu par le système qui va recevoir la données, ou si vous ne pouvez pas savoir, en UTF8, avec encode().

Je sais que ça vous démange de voir un cas concret, alors voici un pseudo programme (téléchargeable ici) :

# -*- coding: utf-8 -*-
 
 
# toutes les chaines sont en unicode (même les docstrings)
from __future__ import unicode_literals
 
"""
    Un script tout pourri qui télécharge plein de page et les sauvegarde
    dans une base de données sqlites.
 
    On écrit dans un fichier de log les opérations effectuées.
"""
 
import re
import urllib2
import sqlite3
 
pages = (
    ('Snippets de Sebsauvage', 'http://www.sebsauvage.net/python/snyppets/'),
    ('Top 50 de bashfr', 'http://danstonchat.com/top50.html'),
)
 
# création de la base de données
conn = sqlite3.connect(r"backup.db")
c = conn.cursor()
 
try:
    c.execute('''
        CREATE TABLE pages (
            id INTEGER PRIMARY KEY,
            nom TEXT,
            html TEXT
        )'''
    )
except sqlite3.OperationalError:
    pass
 
log = open('backup.log', 'wa')
 
for nom, page in pages:
 
    # ceci est une manière très fragile de télécharger et
    # parser du HTML. Utilisez plutôt scrapy et beautifulsoup
    # si vous faites un vrai crawler
    response = urllib2.urlopen(page)
    html = response.read(100000)
 
    # je récupère l'encoding à l'arrache
    encoding = re.findall(r'<meta.*?charset=["\']*(.+?)["\'>]', html, flags=re.I)[0]
 
    # html devient de l'unicode
    html = html.decode(encoding)
 
    # ici je peux faire des traitements divers et varié avec ma chaîne
    # et en fin de programme...
 
    # la lib sqlite convertie par défaut tout objet unicode en UTF8
    # car c'est l'encoding de sqlite par défaut donc passer des chaînes
    # unicode marche, et toutes les chaînes de mon programme sont en unicode
    # grace à mon premier import
    c.execute("""INSERT INTO pages (nom, html) VALUES (?, ?)""", (nom, html))
 
    # j'écris dans mon fichier en UTF8 car c'est ce que je veux pouvoir lire
    # plus tard
    msg = u"Page '{}' sauvée\n".format(nom)
    log.write(msg.encode('utf8'))
 
    # notez que si je ne fais pas encode(), soit:
    # - j'ai un objet 'unicode' et ça plante
    # - j'ai un objet 'str' et ça va marcher mais mon fichier contiendra
    #   l'encoding de la chaîne initiale (qui ici serait aussi UTF8, mais
    #   ce n'est pas toujours le cas)
 
conn.commit()
c.close()
 
log.close()

Quelques astuces

Certaines bibliothèques acceptent indifféremment des objets ‘unicode’ et ‘str’ :

>>> from logging import basicConfig, getLogger
>>> basicConfig()
>>> log = getLogger()
>>> log.warn("Détécé")
WARNING:root:Détécé
>>> log.warn(u"Détécé")
WARNING:root:Détécé

Et ce n’est pas forcément une bonne chose car si il y a derrière écriture dans un fichier de log, cela peut poser problème.

D’autres ont besoin qu’on leur précise:

>>> import re
>>> import re
>>> re.search('é', 'télé')
<_sre.SRE_Match object at 0x7fa4d3f77238>
>>> re.search(u'é', u'télé', re.UNICODE)
<_sre.SRE_Match object at 0x7fa4d3f772a0>

Le module re par exemple aura des résultats biaisés sur une chaîne ‘unicode’ si on ne précise pas le flag re.UNICODE.

D’autres n’acceptent pas d’objet ‘str’:

>>> import io
>>> >>> io.StringIO(u'é')
<_io.StringIO object at 0x14a96d0>
>>> io.StringIO(u'é'.encode('utf8'))
Traceback (most recent call last):
  File "<ipython-input-5-16988a0d4ac4>", line 1, in <module>
    io.StringIO('é'.encode('utf8'))
TypeError: initial_value must be unicode or None, not str

D’autres encore n’acceptent pas d’objet ‘unicode’:

>>> import base64
>>> base64.encodestring('é'.encode('utf8'))
'w6k=\n'
>>> base64.encodestring(u'é')
Traceback (most recent call last):
  File "<ipython-input-3-1714982ca68e>", line 1, in <module>
    base64.encodestring('é')
  File "/usr/lib/python2.7/base64.py", line 315, in encodestring
    pieces.append(binascii.b2a_base64(chunk))
UnicodeEncodeError: 'ascii' codec can't encode character u'\xe9' in position 0: ordinal not in range(128)

Cela peut être pour des raison de performances (certaines opérations sont plus rapides sur un objet ‘str’), ou pour des raisons historiques, d’ignorance ou de paresse.

Vous ne pouvez pas le deviner à l’avance. Souvent c’est marqué dans la doc, sinon il faut tester dans le shell.

Une bibliothèque bien faite demandera de l’unicode et vous retournera de l’unicode, vous libérant l’esprit. Par exemple, requests et l’ORM Django le font, et communiquent avec le reste du monde (en l’occurence le Web et les bases de données) dans le meilleur encoding possible automatiquement de manière transparente. Quand c’est possible bien entendu, parfois il faudra forcer l’encoding car le fournisseur de votre donnée déclare le mauvais. Vous n’y pouvez rien, c’est pareil pour tous les langages du monde.

Enfin il existe des raccourcis pour certaines opérations, utilisez les autant que possible. Par exemple, pour lire un fichier, au lieu de faire un simple open(), vous pouvez faire :

from codecs import open
 
# open() de codec à exactement la même API, y compris avec "with"
f = open('fichier', encoding='encoding')

Les chaînes récupérées seront automatiquement sous forme d’objet ‘unicode’ au lieu d’objet ‘str’ qu’il vous aurait fallut convertir à la main.

Les outils de la dernières chance

Je vous ai menti, si vous ne connaissez pas l’encoding de vos entrées ou de vos sorties, il vous reste encore quelques options.

Sachez cependant que ces options sont des hacks, des trucs à tenter quand tout ce qui a été décrit plus haut a foiré.

Si vous faites bien votre boulot, ça ne doit pas arriver souvent. Une à deux fois max dans votre année, sauf environnement de travail très très merdique.

D’abord, parlons de l’entrée.

Si vous recevez un objet et qu’il vous est impossible de trouver l’encoding, vous pouvez forcer un décodage imparfait avec decode() en spécifiant le paramètre error.

Il peut prendre les valeurs suivantes :

>>> print 'Père Noël'.decode('ascii')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
UnicodeDecodeError: 'ascii' codec can't decode byte 0xc3 in position 1: ordinal not in range(128)
>>> print 're Noël'.decode('ascii', errors='ignore')
Pre Nol
>>> print 're Noël'.decode('ascii', errors='replace')
P��re No��l

Mozilla vient également à la rescousse avec sa lib chardet qu’il faut donc installer :

pip install chardet

Et qui TENTE (du verbe ‘tenter’, “qui essaye”, et qui donc peut échouer et se tromper) de détecter l’encoding utilisé.

>>> chardet.detect(u'Le Père Noël est une ordure'.encode('utf8'))
{'confidence': 0.8063275188616134, 'encoding': 'ISO-8859-2'}
>>> chardet.detect(u"Le Père Noël est une ordure j'ai dis enculé".encode('utf8'))
{'confidence': 0.87625, 'encoding': 'utf-8'}

Cela marche pas trop mal, mais n’attendez pas de miracles. Plus il y a de texte, plus c’est précis, et plus le paramètre confidence est proche de 1.

Parlons maintenant de la sortie, c’est à dire le cas où le système qui va recevoir vos données est une grosse quiche qui plante dès qu’on lui donne autre chose que de l’ASCII.

Je ne veux balancer personne, mais mon regard se tourne vers l’administration américaine. Subtilement. De manière insistante.

D’abord, encode() accepte les mêmes valeurs pour errors que decode(). Mais en prime, il accepte 'xmlcharrefreplace', très pratique pour les fichiers XML :

>>> u"Et là-bas, tu vois, c'est la coulée du grand bronze".encode('ascii', errors='xmlcharrefreplace')
"Et l&#224;-bas, tu vois, c'est la coul&#233;e du grand bronze"

Enfin, on peut essayer d’obtenir un texte potable en remplaçant les caractères spéciaux par leur équivalent ASCII le plus proche.

Avec l’alphabet latin, c’est très facile :

>>> unicodedata.normalize('NFKD', u"éçûö").encode('ascii', 'ignore')
'ecuo'

Pour des trucs plus avancés comme le cyrilique ou le mandarin, il faut installer unidecode :

pip install unidecode
>>> from unidecode import unidecode
>>> print unidecode(u"En russe, Moscou s'écrit Москва")
En russe, Moscou s'ecrit Moskva

flattr this!

Deux conneries à la seconde

samedi 20 avril 2013 à 22:27

Je ne sais plus si je l’ai raconté sur le blog, mais quand j’étais gosse, j’avais lu un dessin de Reiser (ou Plantu, je ne sais plus bien) qui m’avait marqué. Je ne l’ai jamais retrouvé.

Il y avait un dialogue entre deux personnages :

- «les ordinateurs sont formidables», disait le premier. «Il peuvent calculer 20 milliards d’opérations à la seconde, avec seulement une erreur toutes les 10 milliards d’opérations »
- L’autre répond : «2 conneries à la seconde, t’appelles ça un progrès ?»

Internet est un réseau de millions d’ordinateurs aux ordres de millions de consciences humaines.

Si il y a une définition de ce qu’est un catalyseur, Internet est l’exemple qui la suit dans le dictionnaire.

Comme tous les catalyseurs, Internet est neutre, il permet de faire plus vite, plus efficacement et à plus large échelle tout ce dont l’humanité est capable. Les hommes consomment plus vite, apprennent plus vite, lisent plus vite, s’informent plus vite, produisent plus vite, communiquent plus vite, créent plus vite, publient plus vite, débattent plus vite, prennent des décisions plus vite, jouent plus vite, échangent plus vite…

Et avec plus de personnes. A moindre coût.

Effets secondaires

Comme l’humanité n’est pas parfaite, le catalyseur démultiplie aussi ce que l’humanité fait de pire : les terroristes s’organisent plus vite, les pédophiles se satisfont plus vite, les dealers vendent plus vite, les extrêmes idéologiques prennent des forces plus vite…

Tout comme le couteau permet de travailler plus vite et, entre autre, de tuer plus vite.

Tout comme l’imprimerie permet de transmettre le savoir plus vite, y compris celui de faire des bombes.

Tout comme la voiture permet de déplacer plus vite, y compris la cocaïne.

C’est vrai, il y avait moins de Go Fast en 1935.

Aujourd’hui on veut vous faire croire que parce que l’ordinateur permet de faire deux conneries à la seconde, il faut tout protéger. Tout verrouiller. Tout vérifier.

Parce qu’Internet donne une puissance énorme aux malades sexuels, aux fanas des explosifs et aux porteurs d’idées noires, il faut légiférer, espionner, contrôler et brider.

Mais la somme de l’humanité, c’est plus que la souffrance des enfants maltraités par des pervers. C’est le potentiel, pour des des millions d’enfants, d’avoir accès à n’importe quelle information pour pallier à l’imperfection de l’éducation locale.

La somme de l’humanité, c’est plus que la violence de xénophobes agressifs qui instillent la haine. C’est le potentiel de millions de citoyens qui savent, et peuvent, plus et mieux, défendre l’avenir de leurs sociétés.

La somme de l’humanité, c’est plus que la mort sanglante des victimes des bombes du marathon. C’est le potentiel des millions de personnes qui vivent dans des pays éloignés par la distance et la culture, mais qui peuvent entrer en contact, et apprendre à s’aimer.

N’attendez pas l’Unesco

Les grand médias ne parlent plus d’Internet que comme machine à fric et boîte de pandore hébergeant les menaces les plus folles.

C’est bien plus que ça.

Internet, c’est la nouvelle humanité. C’est la route la plus courte pour mettre en relation les communautés, gommer les différences d’age, de sexe, d’éducation et de milieux. Et vite.

C’est la démocratie et la liberté, incarnées dans un service.

Avec l’argent que brasse Internet, on a tendance à croire qu’il a révolutionné le business, et que c’est ce qu’il faut protéger.

On ne pourrait avoir plus tord.

Si demain Internet disparaissait, l’impact économique serait énorme, mais l’impact social serait le plus important.

Nous perdrions le pouvoir de parler tel qu’il existe aujourd’hui.

Comprenez bien, l’histoire est un cycle. On oscille entre la guerre et la paix. Entre l’abondance et la pauvreté. Entre la liberté et l’esclavage. Aucun pays, aucun peuple, aucune période n’est une exception. Notre système s’écroulera et sera remplacé par un autre. Peut être plus libre, sans doute moins. Les français connaîtrons des conflits majeurs, comme le reste du monde. Ils auront faim, puis ça ira mieux, puis ça recommencera.

Tout ceci est naturel.

La possibilité que le prochain cycle soit meilleur que le précédent dépend entièrement de notre capacité à nous éduquer les uns-les autres. Pas du haut vers le bas. Tous. Les uns les autres. Gutenberg a permis aux démocraties modernes d’exister.

A notre niveau de population, de consommation et potentiel d’impact nuisible en tant qu’espèce, Internet est le seul outil qui soit suffisamment efficace pour nous organiser à travailler à un avenir meilleur, plutôt que de nous entre-tuer. Et il n’a cette efficacité actuelle que parce qu’il n’est pas contrôlé par une minorité et permet à tout le monde de parler à tout le monde, de n’importe quoi, en temps réel.

Cette minorité finira toujours par se planter sur le long terme, à cause de ses limites humaines. Par contre, la somme de l’humanité s’équilibre globalement, et d’elle émerge nos meilleurs systèmes, nos innovations, notre futur. Si on laisse un gouvernement ou une entité quelconque choisir ce que permet de faire Internet, nous attendrons ces limites, et ce sera game over. Internet jouera contre nous, plus avec nous.

C’est pourquoi il est primordial de ne pas considérer Internet comme un simple service.

C’est pourquoi il est primordial d’avoir un Internet libre et neutre.

C’est pourquoi il est primordial de ne pas laisser les journalistes lui coller une réputation dans un but sensationnaliste.

C’est pourquoi il est primordial de ne pas laisser un groupe ou un autre avoir une trop grande influence sur le réseau.

Internet n’est plus une expérimentation technologique, il est le support de notre avenir en tant qu’homo sapiens.

Cela a déjà été dit. Mon article n’a rien de nouveau. Mais cette vérité a besoin d’être répétée, martelée, car les gens oublient, se lassent, considèrent les choses les plus importantes comme acquises, et les perdent de n’avoir plus su les apprécier. Cela non plus je ne l’ai pas inventé. Je l’ai constaté à l’échelle mondiale et j’ai pu lire des milliers d’intellectuels faire le même constat en tout point du globe. Ce que je peux faire de mon bureau. Grâce à Internet.

flattr this!

Dans la peau d’un formateur

vendredi 19 avril 2013 à 17:50

J’ai contacté un a un des organismes de formation pour me signaler comme disponible en tant que formateur Python/Django, HTML, CSS, Javascript et Git. Certains ne vérifient rien, d’autres me demandent un plan de cours, voir un support et un CV.

Des semaines voir des mois plus tard, un email arrive. On me demande si je suis disponible pour une session, généralement entre 2 et 5 jours, avec une grande majorité de 3.

Le commercial me communique peu d’informations : leur plan de cours vendu, les dates (potentielles) et le nombre (encore potentiel) de participants, et me demande un devis à la journée.

À ce stade, je check mon agenda pour vérifier ma disponibilité, et je regarde si ça peut être rentable : certaines missions sont loin et chaque jour séparé à bonne distance du suivant, d’autres sont facilement accessibles et groupées d’une traite. Malgré votre expertise et vos aspirations éducatives, le temps et l’espace ont toujours droit de veto sur vos choix de vie.

Il vous faut alors répondre rapidement, mais surtout fournir une estimation de la facturation qui oscille entre 500-800 €/j selon la difficulté du sujet traité, le nombre de participants, les conditions de travail proposée et les frais que je pense rencontrer.

Concernant la difficulté du sujet traité, l’écrasante majorité des formations sont pour débutants, au moins dans la technologie ciblée. Rarement on vous demandera de couvrir des design patterns avancés, de l’analyse de données biologiques ou autre.

Le nombre de participants, lui, excède en peu d’occasions 5, et c’est une bonne chose. Au delà, il est difficile de faire plus que du cours magistral, et c’est en faisant ce constat que j’ai fais la paix avec les plus mauvais enseignants de mon enfance. C’est un boulot qui demande une énergie de fou si on veut le faire bien.

Quand aux conditions de travail, il y a ce qu’on appelle les “intra”, qui se font chez le client, et les “inter”, qui se font dans le centre de formation, mais avec des apprenants venus de différentes boîtes. Entre en jeu des tas de paramètres : si vous allez faire votre show dans une usine de traitement des déchets à Orléan, ce n’est pas la même que dans une salle climatisée avec des machines neuves à Belleville. À l’inverse, former deux collègues qui se connaissent depuis 20 ans, c’est bien plus facile que d’intéresser un codeur Pascal proche de la retraite, un stagiaire de boîte de com et un consultant de SS2I qui se sont d’entrée installés à deux chaise d’écart les uns des autres.

Malheureusement, vous aurez rarement tous ces détails à l’avance, et pas de bon de commande sans devis. Vous lancez WOW, vous faites un random 100, et vous priez.

Plus on connaît l’organisme, plus on a des infos précises, nombreuses et rapidement. Ça devient donc plus facile avec le temps, et c’est une des nombreuses raisons pour lesquelles vous devez entretenir de très bonnes relations avec vos organismes de formation.

Ces derniers sont de toutes sortes : grands, corporates, petits, artisanaux, généralistes, spécialisés, automatisé, manuels, etc. Il n’y a qu’une seule chose importante pour vous : la relation que vous avez avec eux. Personnellement je préfère couper les ponts avec ceux avec lesquels ce n’est pas parfait et ne garder que ceux avec lesquels ça coule tout seul. Je ne pourrai pas vous lister ceux avec lesquels j’ai des contacts car l’anonymat de ce blog est plus que fébrile.

Un bon indicateur est le temps de réponse par email et l’accessibilité de leurs commerciaux. Si vous pouvez avoir leur numéro de portable facilement, c’est souvent positif pour la suite. Car de la réactivité s’extrapole la propreté de l’organisation, l’état des salles de cours et la qualité de votre traitement en général.

Arrive l’attente et l’incertitude : 9 formations sur 10 sont annulées, parfois à la dernière minute, pour des raisons diverses. Il faut dire qu’entre le moment où quelqu’un fait sa demande et votre intervention, le dossier passe plusieurs fois par la hiérarchie, les RH et la compta. Quelques organismes de formations sont mis en concurrence, qui eux-même mettent en concurrence les formateurs. Ajoutez à ceci qu’il faut que tous les planning concordent et, dans le cadre du DIF, que la demande soit validée par l’état. Ça fait beaucoup de si.

Vous, vous avez bloqué cette période. Si vous êtes salariés, vous avez pris des vacances. Si vous êtes freelance, vous avez refusé des clients. Et ceci gonfle le prix de vos prestations.

Coup de chance, celle-ci est acceptée. Il y aura un participant de moins, mais juste assez pour être rentable pour mon intermédiaire. Il me l’annoncera généralement 2 semaines à l’avance, mais c’est déjà arrivé que la nouvelle tombe 5 jours avant l’heure H, avec un déplacement de 800 bornes prévu.

C’est là que je demande à avoir le numéro de téléphone ou l’email des participants, de telle sorte que je puisse connaître des choses essentielles comme :

Il ne faut pas non plus oublier de récupérer les informations suivante auprès de l’organisme de formation, afin d’éviter les mauvaises surprises:

J’ai rarement la chance d’avoir un accès direct aux participants, mais transmettre un questionnaire à ces derniers ou au moins leur responsable de formation ne pose pas de problème. Le retour n’est pas complet, mais c’est mieux que rien.

Étape suivante : envoie du support de cours à l’organisme pour impression. On est informaticiens, mais les gens adorent toujours le papier. Il n’est pas rare que je vois des cahiers petits carreaux scribouillés frénétiquement pendant les sessions. J’envoie systématiquement un support de cours générique pour deux raisons :

Quand la formation commence, les premières heures sont déterminantes. D’abord, je dois asseoir ma crédibilité en temps qu’expert, et comme je suis très jeune et que j’ai pas la gueule de geek des séries télés, les vieux routards ont des doutes. L’astuce consiste à tout de suite parler de leurs technos, sur laquelle j’ai fais une veille avant, afin que la conversation crée une complicité tout en les rassurant sur mes compétences.

Les présentation doivent être chaleureuses, mais pas de blagues pendant la première demi-heure, après on peut se détendre. Le deuxième jour on peut prendre l’apéro.

Comme je l’écrivais plus haut, j’annonce cash aux élèves que je ne vais pas suivre le plan de cours, que j’ai adapté une formation spécialement faite pour eux et que je la modifierai à la volée pour coller à leurs attentes. Je peux faire cela car je suis non seulement un développeur compétent, mais aussi et surtout un bon formateur : je peux improviser des cours complets de tête avec aisance.

Ma spécialité est donc de faire du sur mesure, alors qu’on leur à vendu du thon en boîte.

Pas de slides, dans mes cours, on pratique. Je ne sors pas du code de ma poche, je le tape en direct devant eux, afin qu’ils voient la démarche. Je rajoute toujours des astuces pour aller plus loin, mais sans les détailler, afin qu’ils aient des choses en tête en retournant chez eux. Je fais des blagues, bien entendu. Et parfois je tire sur eux avec un pistolet à fléchettes.

Je ne reste jamais statique très longtemps. La chaise me sert à rentrer le code, ensuite je me lève. Je passe derrière eux alors que je parle, pour les obliger à me suivre. Je fais des emphases, je change de rythme, j’improvise des exercices sur l’actualité, des thèmes controversés (calculer le bénéfice d’une cargaison de hash, télécharger des images de youporn…) ou en rapport avec leurs projets en cours.

J’essaye de les faire sortir de la matière. Je parle de Dukto, de Sublime Text, d’OS alternatifs, de méthodes agiles… Pas longtemps, juste assez pour casser la monotonie, et apporter un plus. Si un débat se lève, je taquine un peu, puis recentre sur le sujet en demandant à reporter la discussion au déjeuner, que l’on passe tous ensemble. Parfois, je paie la tournée.

À la fin, je donne mon numéro de téléphone et mon email personnels en invitant les participant à me contacter en cas de coup dur, bien que la plupart des organismes de formation n’aiment pas vraiment cette idée.

Quand je rentre le soir après chaque cours, je m’écroule sur le lit tout habillé, et je dors.

flattr this!

Lire et écrire dans un fichier XLS avec Python

jeudi 18 avril 2013 à 20:49

< Insérer ici vous-même troll sur Microsoft et LibreOffice et les formats >

Puis, une fois cette corvée administrative remplie, faites un pip install:

pip install xlutils

Ceci va installer deux lib dont on va avoir besoin : xlrd pour la lecture, et xlwt pour l’écriture.

Ensuite c’est très simple…

Pour écrire dans un fichier Excel :

import xlrd
 
from xlwt import Workbook, Formula
 
path = r"C:\chemin\vers\fichier.xls"
 
# On créer un "classeur"
classeur = Workbook()
# On ajoute une feuille au classeur
feuille = classeur.add_sheet("OCB")
 
# Ecrire "1" dans la cellule à la ligne 0 et la colonne 0
feuille.write(0, 0, 1)
# Ecrire "2" dans la cellule à la ligne 0 et la colonne 1
feuille.write(0, 1, 2)
# Ecrire une formule dans la cellule à la ligne 0 et la colonne 2
# qui va additioner les deux autres cellules
feuille.write(0, 2, Formula('A1+B1'))
 
# Ecriture du classeur sur le disque
classeur.save(path)
 
print u"Fichier créé: {}".format(path)
## Fichier créé: C:\chemin\vers\fichier.xls
 
workbook.save(path)

Ce qui nous pond un truc comme ça.

Et pour lire ce même fichier :

import xlrd
 
# Réouverture du classeur
classeur = xlrd.open_workbook(path)
 
# Récupération du nom de toutes les feuilles sous forme de liste
nom_des_feuilles = classeur.sheet_names()
 
# Récupération de la première feuille
feuille = classeur.sheet_by_name(nom_des_feuilles[0])
 
print u"Lecture des cellules:"
print "A1: {}".format(feuille.cell_value(0, 0))
print "B1: {}".format(feuille.cell_value(0, 1))
# On ne peut pas lire les les valeurs des cellules avec formules
print "C1: {}".format(feuille.cell_value(0, 2))
## Lecture des cellules:
## A1: 1.0
## B1: 2.0
## C1:

Comme vous pouvez le voir à la dernière ligne, lire une cellule avec une formule ne revoit rien. L’auteur de la lib explique en effet que les formules sont stockées sous forme de bytecode et qu’il faudrait les décompiler pour obtenir le texte original.

Il existe d’autres manières d’accéder à des fichiers XLS, puis puissantes mais aussi beaucoup plus contraignantes :


Télécharger le code de l’article

flattr this!

Utilisez des variables globales avec JSLint

mercredi 17 avril 2013 à 18:13

Petite astuce si vous utilisez JSlint (par exemple via le super plugin Sublime Text).

Il va vous mettre en avant toutes les variables globales ou non déclarées précédement. C’est génial la plupart du temps, mais c’est un peu chiant pour des variables qui sont volontairement globales et mises à dispo par d’autres scripts comme les frameworks et libs.

Par exemple, il va vous déclarer que jQuery est une variable globale ou non déclarée. Pour éviter ça, mettez ce commentaire tout en haut du fichier :

/*global jQuery:true, $:true */

JSLint va le prendre en compte et ignorer ces variables.

On peut lister autant de variables qu’on le souhaite.

flattr this!