L’indentation de Python m’a tuer 17


J’adore le fait que Python se base sur des espaces pour délimiter les blocs de code. La seule contrainte que cela a jusqu’ici posé, c’est que les blocs ne peuvent pas être passés en arguments.

Aujourd’hui pourtant, ce système si merveilleux nous a bien fait chier pendant une demi-heure.

Cas simple: Max me file un snippet bien racheux dans 0bin. Je clic sur “copy to clipboard”, j’élague la fonction des loggers et des try/catch qui attrapent tout, même un rhûme, et je lance des tests.

  File "truc.py", line 35
     
    ^
SyntaxError: invalid syntax

Quid ?

Je cherche, je recherche, je creuse, je retourne, je m’enfonce.

Passage en mode fourmis.

Je retire des blocs. Des lignes une a une. Des combinaisons des deux. Des combinaisons arbitraires, aléatoires de blocs transposés dans un autre fichier après conversion en utf-8 et des tabs en espaces.

  File "truc.py", line racine de 12
     
    ^
SyntaxError: invalid syntax

Je prends Max a témoin.

Nous cherchons. Nous recherchons. Nous creusons. Je m’énerve.

Ce putain de code DOIT marcher. Il n’y a AUCUNE putain d’erreur de syntax là dedans.

Puis eureka, je copie et je colle le texte comme string dans le shell Python.

Lumière:

>>>"""def download(url, dest_path, progress_callback=lambda x, y: 0, proxy=None, block_sz=8192):
...         
...          
...             if proxy is not None:
...                         # build a new opener that uses a proxy requiring authorization
...                         proxy_support = urllib2.ProxyHandler({"http" : proxy})
...                         opener = urllib2.build_opener(proxy_support, urllib2.HTTPHandler)
...                  
...                         # install it
...                         urllib2.install_opener(opener)
...                  
...                     u = urllib2.urlopen(url, timeout=30)
...                     f = open(dest_path, 'w')
...                  
...                     meta = u.info()
...                 
...                     file_size = int(meta.getheaders("Content-Length")[0])
...                  
...                     block_sz = file_size_dl = 8192
...                     buffer  = u.read(block_sz)
...                     previous_status = ()
...                  
...                     while buffer:
...                          
...                                 file_size_dl += block_sz
...                                 f.write(buffer)
...                                 status = (file_size_dl, file_size_dl * 100. / file_size)
...                                 if status != previous_status:
...                                 """    
...            
 
'\ndef download(url, dest_path, progress_callback=lambda x, y: 0, proxy=None, block_sz=8192):\n    \n    \xc2\xa0\n        if proxy is not None:\n                # build a new opener that uses a proxy requiring authorization\n                proxy_support = urllib2.ProxyHandler({"http" : proxy})\n                opener = urllib2.build_opener(proxy_support, urllib2.HTTPHandler)\n        \xc2\xa0\n                # install it\n                urllib2.install_opener(opener)\n        \xc2\xa0\n            u = urllib2.urlopen(url, timeout=30)\n            f = open(dest_path, \'w\')\n        \xc2\xa0\n            meta = u.info()\n        \n            file_size = int(meta.getheaders("Content-Length")[0])\n        \xc2\xa0\n            block_sz = file_size_dl = 8192\n            buffer  = u.read(block_sz)\n            previous_status = ()\n        \xc2\xa0\n            while buffer:\n            \xc2\xa0\n                    file_size_dl += block_sz\n                    f.write(buffer)\n                    status = (file_size_dl, file_size_dl * 100. / file_size)\n                    if status != previous_status:\n                            previous_status = status\n                            progress_callback(*status)\n                \n                        buffer = u.read(block_sz)\n                \xc2\xa0\n                \xc2\xa0\n                    f.close()\n                \xc2\xa0\n                '

Mais quel est ce petit salopard de “\xc2\xa0″ ?

In [2]: print "a\xc2\xa0b"
a b


Caractère utf8 pour “espace insécable”
.

Pour Sublime Text et 0bin, se sont des espaces comme les autres. Pour Python, c’est une syntax error.

Fuck.

La coloration syntaxique de 0bin doit sans doute insérer ce truc à chaque saut de ligne. Du coup on a patché tout ça, les sources sont à jour sur github et j’en ai profité pour updater le paquet sur pypi qui est maintenant la dernière en date avec tous les goodies: détection de code source, send by mail, compteur, etc.

17 thoughts on “L’indentation de Python m’a tuer

  • Freak0

    C’est ce qui m’embête dans python et me fait toujours préféré php. :)

    Ça et le fait que je maitrise php depuis de longues années, du coup je ne vois pas l’intérêt de passer à python.

  • Sam Post author

    Oula, ne me faites pas dire ce que j’ai pas dit. L’indentation Python est une très bonne chose. Cela force le code à être lisible. C’est au contraire une raison de passer à Python, IMO.

    Mais comme je le disais hier, passer à Python ne veut pas dire abandonner PHP.

  • Freak0

    Ce que je disais, ce n’est pas que l’indentation de python soit une bonne ou mauvaise chose, c’est que à mes yeux c’est “désagréable”. :)

  • Freak0

    C’est marrant car j’ai poster une réponse sur ce blog :p

    J’y tenais les propos suivants sans développer.

    En fait, c’est peu être juste une impression, c’est surtout face à Python que la guerre fait rage en ce moment avec Php. Est que les utilisateurs de python se sentent mal aimé ?

    Ce que je voulais dire par là c’est que dans le cadre du web uniquement, les alternatives à php sont python et ruby.

    Perl a un intérêt pour le scripting coté serveur et l’automatisation de taches, les cronjobs. Java est une usine à gaz coté web et devrait être réservé aux applications desktop.

    Reste Ruby, qui est surement très bien mais comme le dit mon pote Babe (dev php et fan de python), c’est truc pour les gai sous mac (propos ostensiblement trollesques). En plus c’est un langage qui n’existe pratiquement qu’à travers son FW Rails.

    Python est au milieu, c’est un excellent langage pour le web, le scripting et les applications desktop.

    Php, c’est le web (le scripting est anecdotique) et c’est sur le web que la comparaison avec python se fait. D’où mes propos sur le fait que les reproches viennent majoritairement de sympathisants de python.

    Les deux langages ont leurs qualités et leurs défauts. C’est indéniable.

    De mon point de vue, j’ai une bonne maitrise et une bonne expérience de Php pour le web. Pour l’automatisation de taches, je touche un peu de perl et de java et je fais pas mal de chose en bash, j’ai même fait deux ou trois trucs avec Php.

    Je ne fais pas de dev pour le desktop mais si je devais le faire j’utiliserais java que je connais déjà.

    Du coup python ne représente que peu d’intérêt pour moi coté web, coté client ou coté serveur.

    Mon temps disponible est une ressource finie, je me dois donc de choisir où porter mes efforts d’apprentissages et de formation. Apprendre python juste pour le plaisir a donc aucun intérêt.

    Si je veux m’améliorer coté web, je peux toujours me former sur l’un ou l’autre des grands framework php, me mettre vraiment à jquery, …

    Voila j’espère que je raconte pas trop ma vie et que j’ai pu du coup développer un peu mon point de vue.

    Pour finir si c’était à refaire, peu être que je choisirais Python. Je n’aurais en tout cas pas les mêmes freins à tenter son apprentissage.

  • ludovic Gasc

    Dans le code d’exemple, je recommande vivement l’utilisation de requests à la place d’urllib, il n’y a pas photo.

  • tshirtman

    Bah avec un bon éditeur de texte, tu l’aurais vu assez vite ^^.
    J’ai ça dans ma config vim :)

    " show insecable spaces that i sometime insert by accident
    hi insecable ctermbg=red cterm=NONE guibg=red
    call matchadd("insecable", ' ')
    

    Par ce que depuis que je suis passé à bépo, c’est vrai que je tape souvent des espaces insécables par mégarde :(.

    (et pylint/pep8 gueulaient tout le temps un m’indiquaient tout le temps à peu près le bon endroit :))

  • thican

    Justement, pourquoi utiliser des esapces au lieu des tabulations ?

    Je trouve que taper 2 espaces, au lieu d’une seule tabulation est quelque fois source d’erreurs (osef de l’espace disque).

    J’avoue que sinon, j’utilise très souvent l’espace insécable ^^
    Il faut dire que c’est de ta faute Sam ; il ne faut pas copier/coller un code, mais le réécrire.

    Et puis c’est “m’a tué” dans le titre

  • thican

    rhââ non ! toutes mes blagues douteuses avec </troll> par exemple on était supprimé … x'(

    Z’êtes pas gentils. Obligé de s’auto-échapper … :'(

    C’est de ma faute, j’aurais dû activer le javascript pour voir la prévisualisation … :s

  • tshirtman

    «ont été»

    pour le «m’a tuer» un peu de culture :p http://fr.wikipedia.org/wiki/Affaire_Omar_Raddad

    Pour le reste, je ne sais pas si tu es sérieux, mais il y a plusieurs bonnes raisons d’utiliser les espaces au lieu des tabulations.

    Présentation consistante.
    Pas besoin de deviner ce qu’est le caractère, tout espace vide est un espace.
    tout bon éditeur est capable d’insérer un nombre déterminé d’espaces quand on appuis sur tab, et de les effacer n par n si possible quand on fait backspace, la facilité n’est donc pas un soucis :)
    Enfin, le caractère tabulation n’a jamais été pensé pour être inséré dans un fichier, mais pour permettre un affichage tabulaire dans les sorties écrans, en étant instantanément remplacé par des espaces.
    Enfin, le mélange des deux est en effet source d’erreurs, il est bon de n’en garder que l’un des deux si possible, hors, il est difficile de se passer complètement des espaces, donc autant se passer des tabulations.

  • thican

    Au risque de passer pour un boulet, je connais bien la blaque de “m’a tuer” (comme le doctissimo m’a tuer :D) et son origine, c’est juste que tous mes “traits d’humours” n’ont pas été affichés à cause des < et > effacés. (peut-être sont-ils encore dans les données brutes de mon commentaire ?) Ça m’apprendra tiens ^^

    @tshirtman: Merci pour les infos.
    Mais justement, j’utilise les tabulations, car entre dev, nous ne sommes jamais d’accord sur le nombre d’espaces à mettre pour faire une indentation. 2 espaces ou 4 (ou 1, comme j’ai pu voir …) ?
    C’est comme la chicorée, une cuillère et ça suffit :)
    Mais au sujet du mélange, c’est simple, on ne peut pas car Python hurle avant.

  • Sam Post author

    Y a une preview du commentaire en live sous le formulaire pour éviter ce genre de petits désagréments :-)

  • tshirtman

    @thican: Une autre pratique apprécié est la limitation des lignes à 80 caractères, si tu développe avec une indentation de 2, et que tu respecte cette règle, et qu’un autre dévelopeur ouvre ton code avec l’affichage par défaut des tabs (8 espaces, ce que je trouve aussi bien trop grand pour s’en servir pour l’indentation de code, mais il faut se rappeler que ça servait à faire des tableaux, pas à indenter du code!), donc, le dev ouvre ton code, et ça tiens plus dans 80 caractères, il est tout triste… et l’indentation du code n’a plus vraiment de sens, les alignements 2 par 2 qui marchaient bien pour s’aligner sur la ligne précédente son complètement cassés, c’est hideux.

    Essaye de changer ponctuellement la taille de la tabulation pour 4 ou 8 pour regarder l’effet que ça a sur la présentation de ton code, je pense que tu verras des soucis, qui te feront penser à changer cette pratique.

Leave a comment

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <pre> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

Des questions Python sans rapport avec l'article ? Posez-les sur IndexError.