PROJET AUTOBLOG


Sam et Max

source: Sam et Max

⇐ retour index

Vrac Python (encore) 5

lundi 30 mars 2015 à 21:01

Les billets en vrac sont au blogging ce que les pâtes de fond de frigo sont à la cuisine: le dernier recours en cas de manque de ressources ou d’inspiration.

Mais bizarrement, c’est aussi quelque chose qui plait beaucoup. C’est d’ailleurs les posts que je préférais sur le standblog, ou les trucs qui énervaient sebsauvage avant qu’il shaarlise tout ça.

Bref, quelques trucs en Python qui peuvent être passés sous le radar.

Min et Max ont un param “key”

Si vous avez lu l’article sur le tri, vous savez que sorted() et sort() peuvent prendre une fonction de callback via l’argument key, permettant de choisir comment extraire l’information qui va servir à déterminer la place de chaque élément.

min et max marchent de la même manière :

>>> scores = {"allemagne": 1,"montagne": 0}
>>> scores.items()
dict_items([('allemagne', 1), ('montagne', 0)])
>>> max(scores.items())
('montagne', 0)
>>> max(scores.items(), key=lambda x: x[1])
('allemagne', 1)

next possède un second paramètre

Vous savez, sur les dicos, on peut choper une valeur par défaut si une clé n’existe pas :

>>> scores.get("allemagne")
1
>>> scores.get("kamoulox", 1j)
1j

Mais ceci n’existe pas pour les listes. Je pensais que c’était un oubli, mais en fait c’est comblé par le deuxième argument de next :

>>> l = list(range(1))
>>> l
[0]
>>> g = iter(l)
>>> next(g, 1j)
0
>>> next(g, 1j)
1j

__future__ n’est pas bullet proof

Certains comportements backportés en Python 2.7 ne peuvent pas être parfaitement implémentés. Ce sont “ce qu’on peut avoir de plus proche”. C’est déjà pas mal, et je les active tout le temps, mais ça peut vous rattraper à un moment inattendu.

Par exemple, les caractères d’échappements unicodes.

En Python 3:

>>> r"\u"
'\\u'

En Python 2.7, par contre, utiliser u et r entraine une exception :

>>> ur"\u"
  File "<stdin>", line 1
SyntaxError: (unicode error) 'rawunicodeescape' codec can't decode bytes in position 0-1: truncated \uXXXX

Et donc si on active les littéraux unicodes (ce qui est une bonne pratique) :

>>> r"\u"
  File "<stdin>", line 1
SyntaxError: (unicode error) 'rawunicodeescape' codec can't decode bytes in position 0-1: truncated \uXXXX

De quoi se gratter la tête sur des regexs ou des chemins de fichiers Windows.

Reportez, il en restera toujours quelque chose 4

samedi 28 mars 2015 à 10:54

L’année dernière j’ai reporté un bug sur nuitka, un compilateur Python.

Mon rapport était incomplet, et on m’a demandé plus d’informations. J’ai pris du temps, mais j’ai répondu.

Malheureusement à la deuxième demande d’informations, j’ai trainé la patte. Je l’ai mis dans ma todo list, et ai regardé en chien de fusil l’entrée pendant des mois.

Il fallait réinstaller nuitka, me replonger dans ce que je voulais faire à l’époque, lancer la compilation, répondre sur le bug tracker. Une heure dépensée au moins.

C’est ça dont les gens ne se rendent pas compte : participer à l’open source, ou à un quelconque effort collectif, ça prend énormément de temps. Entre les interruptions que ça génère, l’historique qu’il faut remonter à chaque fois, le changement de contexte, et bien entendu l’activité elle-même.

Donc, quand on vous rapporte un bug, même de manière énervée, bénissez le Dieu de la procrastination que la personne ait pris le temps de le faire. Vous ne vous devez rien l’un à l’autre, et c’est un beau moment de synergie humaine.

En l’occurrence, donc, mon interlocuteur a fini par marquer le bug comme “lack of feedback”.

D’un certain côté, ça m’a soulagé, je n’avais plus du tout envie de m’y remettre.

D’un autre côté, j’ai culpabilisé, me disant que quand même, j’utilise du FOSS, faudrait contribuer plus, et que j’avais fais perdre du temps à l’auteur.

Au final, cette semaine, je reçois une notification comme quoi une autre personne a continué le fil : il a le même problème.

J’avais oublié ce détail : en prenant le temps d’ouvrir un fil de discussion, je lui avais donné vie. Je lui avait donné une légitimité. Et il se trouve qu’avec les infos fournies par le nouvel arrivant, le ticket a été marqué en bug, et sera corrigé.

C’est un effort collectif, et même si je n’ai pas fais autant que ce que j’aurais voulu, j’ai joué mon rôle. Un rôle imparfait, mais utile.

Donc souvenez-vous : reportez vos bugs, il en restera toujours quelque chose.

Views VS generators 6

mercredi 25 mars 2015 à 19:57

Avec Python 2.7, un outil appelé les “views” (les “vues”) est apparu. Une vue est juste un enrobage qui permet de voir un objet d’une certaine façon, et de le manipuler d’une certaine façon (avec une autre API), sans changer cet objet.

Les vues ont surtout été notables pour leur apparition dans les dictionnaires avec Python 2.7:

    >>> scores = {"sam": 1, "max": 0}
    >>> scores.items() # retourne une lsite
    [('max', 0), ('sam', 1)]
    >>> scores.iteritems() # retourne un générateur
    <dictionary-itemiterator object at 0x7f8782a26628>
    >>> list(scores.iteritems()) # sans views
    [('max', 0), ('sam', 1)]
    >>> scores.viewsitems() # avec views
    Traceback (most recent call last):
      File "<ipython-input-12-dc0b08011047>", line 1, in <module>
        scores.viewsitems() # avec views
    AttributeError: 'dict' object has no attribute 'viewsitems'
 
    >>> scores.viewitems() # retourne une vue
    dict_items([('max', 0), ('sam', 1)])

Néanmoins personne ne les a vraiment utilisé, et c’est un tort. Elles sont en effet très performantes, et pour cette raison sont retournées par défaut avec items() en Python 3.

En effet, les vues ne sont qu’un enrobage : elles ne contiennent rien, et donc ne prennent pas beaucoup mémoire, tout comme les générateurs.

Mais contrairement aux générateurs, les vues ne se vident pas et peuvent exposer une API plus complète que les générateurs, comme par exemple déclarer une taille :

>>> items = scores.iteritems()
>>> list(items)
[('max', 0), ('sam', 1)]
>>> list(items) # woops
[]
>>> items = scores.viewitems()
>>> list(items)
[('max', 0), ('sam', 1)]
>>> list(items)
[('max', 0), ('sam', 1)]
>>> len(scores.iteritems()) # nope
Traceback (most recent call last):
  File "<ipython-input-21-9c7f250da51d>", line 1, in <module>
    len(scores.iteritems())
TypeError: object of type 'dictionary-itemiterator' has no len()
 
>>> len(scores.viewitems())
2

Alors certes, on ne peut pas mettre des vues partout, et les générateurs restent utiles. Mais quand il est possible de les utiliser, et à moins d’avoir besoin d’une liste afin de modifier les valeurs in place, il n’y pas de raison de ne pas le faire : c’est le meilleur des deux mondes.

Tableau de référence des exceptions en Python 6

lundi 23 mars 2015 à 00:23

J’ai fais un topo sur la gestion des erreurs en python, mais je pense que les débutants peuvent bénéficier d’un petit tableau pour s’y retrouver quand ils tombent sur les erreurs les plus courantes.

Les exceptions suivantes sont levées en cas d’erreur. Elles héritent toutes de StandardError :

Exception Cause Résolution
NotImplementedError Un développeur a volontairement levé cette exception dans une des méthodes de sa classe afin de signaler que c’est aux enfants de la classe de l’implémenter. N’utilisez pas la classe directement, mais créez une classe enfant. Overridez la méthodes afin de lui donner un comportement. Si vous ne comprenez pas ce que je viens de dire, lisez le guide sur la POO.
IndentationError ou TabError Le fichier mélange des tabs et des espaces ou n’utilisent pas le même nombre de tabs ou d’espaces partout. Activez l’affichage des tabs et espaces dans votre éditeur de texte, et assurez-vous d’utiliser 4 espaces partout comme valeur d’indentation.
ImportError Python ne peut pas importer un module. Vérifier que le nom du module ne comporte pas de fautes (Python est sensible à la casse). Assurez-vous que le module est importable (situé dans un dossier du PYTHON PATH) et qu’il ne contient pas d’erreurs empêchant son importation, telle qu’une référence cyclique. Si vous ne savez pas ce qu’est le PYTHON PATH, lisez l’article sur les imports.
AssertionError Une expression assert est fausse. Si c’est dans votre code, retirez le assert, ce mot clé n’est que pour les tests unittaires. Si vous avez la malchance de tomber sur une lib qui l’utilise comme garde fou pour le passage d’arguments valeur, lisez le code source, et passez une valeur qui rendra l’expression vraie. Si vous en avez dans les tests et que vous ne savez pas quoi en faire, lisez le guide sur le tests.
AttributeError Vous demandez à un objet de vous fournir un attribut qu’il ne possède pas. Vérifiez que le nom de l’attribut et le nom de l’objet ne contiennent pas de faute (Python est sensible à la casse). Vérifier que l’attribut a bien été créé avant son accès et pas supprimé entre temps (Python est dynamique, les attributs peuvent être créés et supprimés à la volée. Si le cas où l’attribut n’existe pas est un cas valide, vous pouvez tester cette existence avec hasattr() ou obtenir une valeur par défaut avec getattr().
NameError Vous tentez d’utiliser un nom (de variable, fonction, classe, etc) qui n’existe pas. Vérifiez que ce nom ne contient pas de faute (Python est sensible à la casse). Assurez-vous que ce que vous nommez a bien été créé avant cette ligne.
IndexError Vous tentez d’accéder à une partie d’une indexable (souvent une liste ou un tuple) qui n’existe pas. Assurez vous que l’indexable contient assez d’éléments. Si le cas d’un indexable trop court est normal, vous pouvez vérifier la longueur de l’indexable avec len(), ou utilisez un try/except.
KeyError Vous tentez d’accéder à une clé d’un mapping (souvent un dictionnaire) qui n’existe pas. Assurez vous que la clé existe. Si le cas d’une clé inexistante est normal, vous pouvez vérifier qu’une clé est dans le mapping avec ‘in’, utiliser try/except ou obtenir une valeur par défaut avec get(). Dans le cas où vous souhaitez aussi que la valeur par défaut soit ajoutée à la collection, utilisez setdefault() ou collection.defaultdict().
TypeError Vous tentez une opération incompatible avec ce type. Si l’erreur a lieu au niveau d’une fonction que vous appelez, assurez-vous de passer des paramètres du type attendu par la fonction. Vous pouvez vérifier le type d’un objet avec type(). Vérifiez également que vous n’utilisez pas un opérateur incompatible avec un type (par exemple, & ne fonctionne pas sur les strings) ou entre deux types incompatibles (par exemple, il n’est pas possible d’additionner une string avec un entier).
ValueError Vous passez une valeur à une fonction qui n’a aucun sens dans ce contexte ou dont le sens est ambiguë. Assurez-vous de ne pas passer une valeur aberrante et que le résultat attendu soit évident. Par exemple, si vous essayez de faire int(‘é’), la conversion de la lettre “é” en entier n’a pas de résultat évident.
UnicodeDecodeError Vous gérez votre texte comme un porc. Lisez le guide sur l’encoding.
UnicodeEncodeError
OverflowError Vous faites des calculs trop gros pour les types numériques des base Utilisez le module decimal
ZeroDivisionError Vous faites une division par zéro Assurez-vous que sous aucune condition aucun dénominateur n’est égal à 0
IOError Erreur d’entrée / sortie Vérifiez que vous pouvez lire / écrire depuis et vers la ressource que vous utilisez. Parmi les problèmes récurrents : disque dur plein, système corrompu, absence de permissions, fichier inexistant, etc.
OSError L’OS retourne une erreur Les causes peuvent être très variées, mais concernent souvent le système de fichier ou l’utilisation d’un sous-process. Vérifier les lignes où vous utiliser les modules os, shutils, popen, subprocess, multiprocessing, etc.
MemoryError Vous utilisez trop de mémoire Vérifiez vos remplissages de listes et dictionnaires, particulièrement si vous en déclarez un comme valeur par défaut d’un paramètre.

Je n’ai pas mentionné quelques exceptions beaucoup plus rares, mais vous pouvez trouver la liste complète ici.

Toutes les exceptions héritent de BaseException, y compris Exception.

Il existe 3 exceptions qui n’héritent pas de Exception, et ne représentent PAS des erreurs :

Bien qu’héritant d’Exception, les warnings sont des mécanismes un peu particulier. Ils sont tous notés XxxWarning (ex: DeprecationWarning pour signaler l’usage d’une fonctionnalité en cours de dépréciation) et sont des enfants de Warning.

Généralement les warnings ne sont pas fait pour être levés avec raise, mais appelé avec warnings.warn. Par défaut ces warnings sont affichés, mais peuvent être réduits au silence, filtrés ou levés comme exception selon le désir du développeur.

Enfin, StopIteration est levée quand on appelle next() sur un itérateur vide. C’est le seul enfant de Exception (avec Warning) qui n’hérite pas de StandardError car ce n’est pas une erreur en soit, mais simplement un mécanisme de contrôle de flux qui dit à la boucle for quand s’arrêter.

Le don du mois : VLC 6

samedi 14 mars 2015 à 12:12

Le concept du don du mois fait des emules, et c’est bien. Que nous réserve donc mars ?

Un petit flasback, comme souvent.

Souvenez-vous, ce temps des packs de codecs buggés et vérolés. Des multiples lecteurs à installer. Des fichiers incomplets ou corrompus illisibles pour un bit de travers. De votre ordi qui rame car le film est trop compressées. L’ère pré-historique de la lecture de vidéo.

Et puis, cocorico !

A l’école Centrale de Paris, des élèves décident de streamer des films entre deux points de leur internat, et ainsi naquis VLC.

Qui lit tout. Même les .mov, les trucs fait pour realplayer, les .ogg, les videos streamées, les films cassés, les DVD chiffrés ou zonés… Tout, en prenant le minimum de ressource. Avec tellement d’options en plus : les sous-titre, le décalage du son, le transcoding, le changement d’orientation, de format, de pitch, le saut des pubs, etc. Et il existe des interfaces ncurse, HTTP et telnet !

Cerise sur le gâteau, ça marche sur Mac. Sur Linux. Et sur Windows, en un exe, qu’on peut rendre portable sur clé USB. Et depuis la dernière version, sur mobile.

La fondation VLC accepte les bitcoins et reçoit donc 50€ d’amour inconditionnel.