PROJET AUTOBLOG


Sam & Max: Python, Django, Git et du cul

Site original : Sam & Max: Python, Django, Git et du cul

⇐ retour index

La maison des horreurs de l’encoding 13

dimanche 29 janvier 2017 à 15:58

Ah, l’encoding, le truc que tout le monde veut mettre sous le tapis. Il faut dire que c’est dur à gérer. En fait tellement dur que:

Tout ce bordel amène les devs à essayer d’ignorer le problème le plus longtemps possible. Ca marche assez bien pour les anglophones car leur environnement est assez homogène, orienté ASCII, et certains peuvent faire une très belle carrière en restant joyeusement ignorant.

Ca marche beaucoup moins bien pour les européens, et pas du tout pour le monde arabe et asiatique. Néanmoins, pas besoin de chercher bien loin pour trouver des échecs critiques.

Naviguez tranquillement sur un site espagnol a priori joli, moderne, utilisant des tildes et tout ce qu’il faut. Maintenant regardez la requête HTTP, vous noterez que le serveur n’indique pas le charset du contenu. Fort heureusement dans le HTML vous trouvez:

<meta http-equiv="Content-Type" content="ISO-8859-1">

Nickel, récupérons le texte du bouton “Ver más ideas”:

>>> import requests
>>> res = requests.get('http://www.airedefiesta.com/76-pinatas-y-chuches.html') 
>>> data = res.content.split(b'http://www.airedefiesta.com/ideas.html?c=76">')[1].split(b'</a>')[0]
>>> data
b'Ver m\xc3\xa1s ideas'

Une suite de bits comme maman les aime. On décode:

>>> data.decode('ISO-8859-1')
'Ver más ideas'
Chenille dans labyrinthe

Et puis on ne voudrait pas que vous arriviez au château trop vite

Enfer et sodomie ! Le charset déclaré n’est pas celui utilisé. Tentons un truc au hasard:

>>> data.decode('utf8')
'Ver más ideas'

Bref, en 2017, on se touche la nouille pour savoir qui a son architecture multi-services load balancée web scale à base de NoSQL, de containers orchestrés et de serveurs asynchrones. Mais pour afficher du texte y a plus personne hein…

Vous croyez que ce ne sont que les amateurs qui font ces erreurs. Naaaaaaaaa. Par exemple le standard pour les fichiers zip a une vision très… hum… personnelle du traitement de l’encoding des noms de fichier.

L’encoding, c’est la raison majeur de l’incompatibilité de Python 2 et 3, mais aussi un signe de la bonne santé de la techno puisque c’est un des rares vieux langages (je rappelle que Python est plus vieux que Java) à gérer la chose correctement. A savoir:

Python n’est pas parfait pour autant. Par exemple il garantit un accès 0(1) indexing sur les strings, ce qui à mon sens est inutile. Swift a un meilleur design pour ce genre de choses. Mais globalement c’est quand même super bon.

Si ne savez toujours pas comment ça marche, on a évidement un tuto pour ça.

Alors pourquoi l’encoding c’est un truc compliqué ?

Et bien parce que comme pour le temps ou l’i18n, ça touche à la culture, au langage, à la politique, et on a accumulé les problèmes au fil des années.

A solid dick from an iron man

Mais je vous jure ça avait du sens y a 40 ans !

Par exemple, parlons un peu d’UTF.

Vous savez, on vous dit toujours d’utiliser utf8 partout pour être tranquille…

Mais déjà se pose la question : avec ou sans BOM ?

Le BOM, c’est une suite d’octets qui indique en début de fichier qu’il contient de l’UTF. Si ça à l’air pratique, c’est parce que ça l’est. Malheureusement, celui-ci n’est pas obligatoire, certaines applications le requièrent, d’autres l’ignorent, et d’autres plantent face au BOM. D’ailleurs, le standard unicode lui-même ne le recommande pas:

Use of a BOM is neither required nor recommended for UTF-8

Ca aide vachement à faire son choix.

Perso je ne le mets jamais, sauf si je dois mélanger des fichiers de différents encodings et les différencier plus tard.

Mais Powershell et Excel par exemple, fonctionnent parfois mieux si vous leur passez des données avec le BOM :)

Si vous avez un peu creusé la question, vous savez qu’il existe aussi UTF16 (par défaut dans l’API de Windows 7 et 8 et les chaînes de .NET), UTF32 et UTF64. Ils ont des variantes Big et Little Endians, qui ne supportent pas le BOM, et une version neutre qui le supporte, pour faciliter la chose.

Bien, bien, bien.

Mais saviez-vous qu’il existe aussi UTF-1, 5 et 6 ? Si, si. Et UTF9 et UTF18 aussi, mais sauf que eux ce sont des poissons d’avril, parce que les gens qui écrivent les RFC sont des mecs trop funs en soirées.

Que sont devenus ces derniers ? Et bien ils ont été proposés comme encoding pour l’internationalisation des noms de domaine. UTF5 est un encoding en base 32, comme son nom l’indique. Si, 2 puissance 5 ça fait 32. Funs en soirée, tout ça.

Néanmoins quelqu’un est arrivé avec une plus grosse bit(e), punycode, en base 36, et a gagné la partie. J’imagine que les gens se sont dit qu’utiliser base64 était déjà trop fait par tout le monde et qu’on allait pas se priver de cette occasion fabuleuse de rajouter un standard.

Standard qui ne vous dispense pas, dans les URLs, d’encoder DIFFÉREMMENT ce qui n’est pas le nom de domaine avec les bons escaping. Et son lot de trucs fantastiques. Encoding qui est différent pour les valeurs de formulaire.

Python supporte par ailleurs très bien tout ça:

>>> 'Père noël'.encode('punycode')
b'Pre nol-2xa6a'
>>> import urllib
>>> urllib.parse.quote('Père Noël')
'P%C3%A8re%20No%C3%ABl'
>>> urllib.parse.quote_plus('Père Noël')
'P%C3%A8re+No%C3%ABl'

En plus, si Punycode est l’encoding par défaut utilisé dans les noms de domaine, c’est donc aussi celui des adresses email. Ce qui vous permettra de profiter des interprétations diverses de la spec, comme par exemple le retour de la valeur d’un HTML input marqué “email”, qui diffère selon les navigateurs.

Président faisant un fuck dans idiocracy

If you don’t encode in Tarrlytons…fuck you!

Pourquoi je vous parle des adresses emails tout à coup ? Ah ah ah ah ah ah ah !

Mes pauvres amis.

Je ne vous avais jamais parlé d’utf7 ?

Non, je ne me fous pas de votre gueule. Je suis très sérieux.

Figurez-vous que le format email MIME accepte l’utilisation d’utf7 en lieu et place de base64.

Mais ce n’est pas ça le plus drôle.

Y a mieux, je vous jure.

UTF7 est en effet l’encoding par défaut pour IMAP, particulièrement les noms des boîtes aux lettres. Vous savez, “INBOX”, “Spams” et “Messages envoy&AOk-s” ;)

Or comme l’enculerie ne serait pas aussi délicieuse sans un peu de sable…

La version utilisée maintenant (et pour toujourssssssss) par IMAP est une version d’UTF7 non standard et modifiée.

Pourquoi ? Ben parce qu’allez-vous faire foutre.

The choosen one, crying

The choosen one would soon realize that some things survived outside of the vault. Like bad UI and terrible IT standards. And his ‘science’ skills is at 42% and life sucks.

Au final je n’ai fait que parloter d’UTF, mais souvenez-vous que:

>>> import encodings
>>> len(encodings.aliases.aliases)
322

Donc on n’a fait qu’effleurer la surface de l’anus boursouflé de la mouche.

J’espère que la nuit, à 3h du mat, lorsque votre prochaine mise en prod agonisera sur un UnicodeDecodeError, vous penserez à moi et pendant un instant, un sourire se dessinera sous vos larmes.

Ecriture extra-terrestre de the arrival

Militaire : Votre avis ? – Unicode Consortium : Tuez les tous.