L’indentation obligatoire et l’existence du PEP8 sont pour moi deux features fondamentales de Python, limitant énormément la quantité de code illisible qu’on trouve dans la communauté.
Malgré cela, le reformatage de code reste une tache courante, et nécessaire, mais un gâchis énorme de temps. D’abord il faut décider comment on va formater, ce qui en équipe veut dire débat sur le pire sujet qui soit: le goût. Ensuite il faut mettre en place des configurations de linter (flake8, pylint, etc), et potentiellement l’infra qui va avec (tox, hooks git, CI…).
Pour cette raison, de nombreux outils de formatage automatique ont vu le jour. Le premier a été autopep8, et plus tard yapf de Google.
Mais ces deux outils ont quelques soucis:
- Pas facile à faire marcher ou à configurer.
- Ne résout pas l’éternel débat du formatage préféré qui revient dans un meeting chaque année.
- Parfois ils ne marchent pas.
- Parfois ils changent le sens du code (arg!).
- Ils vous font des git diff bien velus.
- Ne marche pas avec toutes les versions de Python.
Le monde du langage Go a choisi une stratégie différente: la technique du “ta gueule”.
Cette technique subtile et raffinée s’est incarnée dans l’outil Gofmt, qui est fourni par défaut avec go, et n’a AUCUN réglage.
Le résultat, tout le monde a fermé sa gueule et a adopté l’outil.
Est-ce que le formatage est parfait ? Non.
Est-ce qu’il plaît à tout le monde ? Absolument pas.
Est-ce qu’il fait fermer sa putain de gueule à tout le monde afin qu’on puisse enfin retourner à des choses plus importantes comme coder ?
Yes !
Gofmt produit un formatage suffisamment clair et pragmatique, et comme il est fortement ancré dans la communauté, tout le monde est à la même enseigne. Passer d’un code à un autre est facile. Pas de temps perdu à discuter du style ou à tweaker ses linters. Tout le monde lance go fmt
(aka go ferme ta …) et on passe à autre chose.
Dernièrement facebook a décidé de faire pareil, et à pondu en open source black (en référence à Henry Ford), un outil de formatage en Python, qui n’a que 2 réglages. Il suit le PEP8, mais évidemment sa propre interprétation, et ne propose rien d’autre.
Black a aussi l’avantage de fournir des diffs assez petits, et surtout, vérifie si l’AST change après un reformatage, et annule le cas échéant, garantissant que le sens de votre code n’est pas altéré.
Est-ce que j’aime toutes les règles de formatages de black ? Non.
Est-ce que regarder sa sortie me donne parfois envie de me bouffer les couilles parce que franchement, qui pense que c’est une bonne idée d’aligner les choses comme ça ? Parfois.
Mais c’est good enough.
Et du coup, l’adoption de black a été très rapide dans la communauté, et il a été appliqué à heroku, requests, tablib, envoy, clint, fabric 2 et pytest. 4000 stars sur github.
Installation
Évidemment, ça se pip install, mais uniquement sur Python 3.6. Black peut checker du code 2.7, mais il lui faut du 3.6 minimum pour exister, donc on l’installe en parallèle. Évidemment, on peut l’intégrer à ST, Vim ou VSCode. Si votre projet utilise un Python different, il faut donc dans les options faire pointer l’exécutable vers l’installation séparée.
Résultat
Dans l’esprit du lien partagé par Seb, créons un générateur de titre de film porno:
import random subject_qualifiers = ( "shy", "mature", "busty", "hot", "horny", "ebony", "quiet", "excited", "naughty", "bad", "cheating", "beautifull", "gorgeous", "drunk", "emo", "fat", "chubby", "goth", "lingery wearing", "latex enthousiast", "placid", "energic", 'slutty', 'sweaty', 'curvy', ) subjects =( 'teen', 'doll', 'brunette', 'blonde', 'midget', 'milf', 'bitch', 'babe', 'sister', 'step-mom', 'vixen', 'secretary', 'real estate agent', 'teacher', 'student', 'schoolgirl', 'cheer leader', 'asian tourist', 'babysitter', 'ex girlfriend', 'nurse', 'squirtter', 'model', 'granny', 'furry', ) actions = ( "recieves anal", "get busted", "driven to bukakke", "taught double penetration", "fucked hard", 'gently chocked', 'punished', 'forced into blow job', 'pounded', 'creampied', 'ass raped', "eaten", "get her pussy wet", "shamed", "get an orgasm for the first time", 'lead to loud climax', 'offered best sex of her life', 'worn out', 'cured from boredom', 'warmed up', 'loved in and out', 'generously oiled', 'shocked and impressed', 'decieved into giving it', 'woke up roughly', 'get sexy massage', 'ridden to exhaustion', 'turned into a lavish slave', 'never submit to torture', 'rebels against abuses', 'taken in every possible way', 'enjoy the 10 inches provided', ) actors = ( "pawn shop owner", "corrupted cop", "dirty plumber", "big ass nigga", "sport coach" "her boss", "twisted psychiatrist", "ripped doctor", "crispy fire fighter", "smug playboy", "skinny geek", "eccentric millionaire", "airplane pilot", "movie star", 'football team', 'her big brother', 'security guard', 'hairy beast', 'wasted guitard player', 'hung indian immigrant', 'a guy twice her size', '17 guys in a row', 'her ideal man', 'her secret prince charming', 'weirdo albinos', 'muscle giant', 'the worst cook ever', 'cable man', 'more men that she can count', 'two friendly brothers', 'enrike strongsteel' ) contexts = ( "on the beach","in a cheap motel","in the back of a van", "in airplane toilets", "for hours", "to pay back her depts", "for a stupid mistake", "and it gets better", "and ask for more", "because she could", "in exchange for a favor", "right next to her boyfriend", "as a reward", "hopping to get him back", "caught on security cam", "every monday", "in a barn", "but that's not all", 'but she has a secret', "and she has a dick too", 'before inviting her friend over', 'while her father is watching', 'with her ', "while auditing for a role", "to get her job back", "for an interview", "in exclusive sex tape", "again and again", ", begging to stop", "for a change", "for chrismas", "in public", 'in a back alley', "during a concert", 'on her death bed' ) punctuation = ('','!','!!','...') def get_title(subject_qualifiers, subjects, actions, actors, contexts) : qualifier = random.choice(subject_qualifiers) subject = random.choice(subjects) action = random.choice(actions) actor = random.choice(actors) context = random.choice(contexts) return f"{qualifier} {subject} {action} by {actor} {context}" .capitalize() if __name__ == "__main__": print(get_title(subject_qualifiers = subject_qualifiers, subjects=subjects, actions=actions, actors=actors, contexts=contexts)) |
Usage:
$ python3.6 porn_title_generator.py Chubby model loves bukakke by skinny geek during a concert $ python3.6 porn_title_generator.py Busty bitch rebel against abuses by security guard but she has a secret $ python3.6 porn_title_generator.py Lingery wearing student creampied by weirdo albinos on the beach $ python3.6 porn_title_generator.py Horny bitch offered best sex of her life by hairy beast in airplane toilets $ python3.6 porn_title_generator.py Emo blonde punished by airplane pilot on the beach $ python3.6 porn_title_generator.py Quiet squirtter lead to loud climax by wasted guitard player while auditing for a role $ python3.6 porn_title_generator.py Emo babysitter get her pussy wet by football team caught on security cam $ python3.6 porn_title_generator.py Busty asian tourist taken in every possible way by muscle giant and she has a dick too $ python3.6 porn_title_generator.py Placid milf ass raped by muscle giant in a back alley |
On applique black, zero réglage, usage simplissime:
$ black . # appel recursif, modification in place par défaut |
Le résultat.
import random subject_qualifiers = ( "shy", "mature", "busty", "hot", "horny", "ebony", "quiet", "excited", "naughty", "bad", "cheating", "beautifull", "gorgeous", "drunk", "emo", "fat", "chubby", "goth", "lingery wearing", "latex enthousiast", "placid", "energic", "slutty", "sweaty", "curvy", ) subjects = ( "teen", "doll", "brunette", "blonde", "midget", "milf", "bitch", "babe", "sister", "step-mom", "vixen", "secretary", "real estate agent", "teacher", "student", "schoolgirl", "cheer leader", "asian tourist", "babysitter", "ex girlfriend", "nurse", "squirtter", "model", "granny", "furry", ) actions = ( "recieves anal", "get busted", "driven to bukakke", "taught double penetration", "fucked hard", "gently chocked", "punished", "forced into blow job", "pounded", "creampied", "ass raped", "eaten", "get her pussy wet", "shamed", "get an orgasm for the first time", "lead to loud climax", "offered best sex of her life", "worn out", "cured from boredom", "warmed up", "loved in and out", "generously oiled", "shocked and impressed", "decieved into giving it", "woke up roughly", "get sexy massage", "ridden to exhaustion", "turned into a lavish slave", "never submit to torture", "rebels against abuses", "taken in every possible way", "enjoy the 10 inches provided", ) actors = ( "pawn shop owner", "corrupted cop", "dirty plumber", "big ass nigga", "sport coach" "her boss", "twisted psychiatrist", "ripped doctor", "crispy fire fighter", "smug playboy", "skinny geek", "eccentric millionaire", "airplane pilot", "movie star", "football team", "her big brother", "security guard", "hairy beast", "wasted guitard player", "hung indian immigrant", "a guy twice her size", "17 guys in a row", "her ideal man", "her secret prince charming", "weirdo albinos", "muscle giant", "the worst cook ever", "cable man", "more men that she can count", "two friendly brothers", "enrike strongsteel", ) contexts = ( "on the beach", "in a cheap motel", "in the back of a van", "in airplane toilets", "for hours", "to pay back her depts", "for a stupid mistake", "and it gets better", "and ask for more", "because she could", "in exchange for a favor", "right next to her boyfriend", "as a reward", "hopping to get him back", "caught on security cam", "every monday", "in a barn", "but that's not all", "but she has a secret", "and she has a dick too", "before inviting her friend over", "while her father is watching", "with her ", "while auditing for a role", "to get her job back", "for an interview", "in exclusive sex tape", "again and again", ", begging to stop", "for a change", "for chrismas", "in public", "in a back alley", "during a concert", "on her death bed", ) punctuation = ("", "!", "!!", "...") def get_title(subject_qualifiers, subjects, actions, actors, contexts): qualifier = random.choice(subject_qualifiers) subject = random.choice(subjects) action = random.choice(actions) actor = random.choice(actors) context = random.choice(contexts) return f"{qualifier} {subject} {action} by {actor} {context}".capitalize() if __name__ == "__main__": print( get_title( subject_qualifiers=subject_qualifiers, subjects=subjects, actions=actions, actors=actors, contexts=contexts, ) ) |
L’indentation est revue et normalisée vers 4 espaces, les espacements et sauts de ligne sont rééquilibrés (limite de caractères à 88 ), les quotes deviennent toutes ‘”‘. C’est lisible. Le code marche toujours.
Problem solved.
Oui mais …
J’ai lu Craft Your Python Like Poetry et depuis je pense qu’un outil automatique ne pourra jamais égaler un humain qui réfléchit au sens de son indentation.
Cela dit:
Y a plein de cas où on s’en fiche
L’article dit aussi qu’il faut être cohérent dans toute la code base et block est super pour ça
On peut désactiver black sur certaines lignes si besoin avec un commentaire
Du coup, je me tâte à y passer mes projets.
À suivre …
Merci pour l’article !
Je suis assez d’acc…En plus des problématiques naturelles (et nécessaires) sur la mise en place des règles de formatage et d’écriture … la diversité des linters ne facilite pas “forcement” leurs mises en place ( trop de choix tue le choix ) :)
Je crois qu’il y a aussi ‘prettier’ qui partage (en partie?) les mêmes objectifs (un formater le code avec des règles universelles et simple d’usage ).
Ok c’est du JS … mais des plugins pour tout les langages sont en train de se mettre en place et le plugin python est en cours de dev https://github.com/prettier/plugin-python
Je n’ai pas eu l’occaz de tester .. ces deux projets semblent vouloir régler le même pb ?
La possibilité d’avoir un seul outil pour gérer ses différentes bases de code (front et back typiquement) n’est peut-être pas si mauvaise ?
Prettier est plus chiant à installer, outre le fait qu’il n’est pas pip installable et qu’il demande npm, il faut aussi gérer son intégration dans la path et l’ajout du plugin Python. Ca rajoute une sacré barrière.
Mais le plus gros problème est que prettier a une tonne d’options: https://prettier.io/docs/en/options.html
Donc débat, meeting, setup de linters, inconsistance à travers les bases de code, etc.
80 caractères par ligne, c’est standard ? Je trouve ça tellement court (mais c’est peut être parce que je dois coder en Java) !
Le PEP8 est à 79. Black a 88. Plus d’infos: http://sametmax.com/comment-garder-des-lignes-courtes/
Merci !
“4000 stars sur github”, regardez bien, vous verrez plus ça très souvent. XD
Pourquoi un espace entre la déclaration de la fonction et la déclaration des variables ? Déjà que la PEP8 impose 2 sauts de ligne avant de déclarer une fonction …
@Morgotth : je sais pas et le but c’est justement de plus se demander et de passer à la suite. Tout n’est pas parfait, mais c’est vraiment, mais alors vraiment pas important par rapport au gain d’un format universel, homogène, utilisable et sans overhead.
Et comment ca gère les docstrings ?
Ca les touche pas.
@Luigi pour les docstring, il y a blacken docs qui applique black sur les zones marquées
.. code-block:: python
ou bien```python
Hello,
Je trouve PEP8 pas mal mais pour moi, il manque 3 choses essentielles à du code vraiment lisible :
– la notation hongroise afin de typer ses variables et d’arrêter de changer le type de celles-ci en plein milieu du code code comme un gros porc. Cela évite aussi les trucs crades à la OpenSSL ou i est une string, str est un int…
– le CamelCase, afin de rendre le texte plus lisible, puis qu’en typographie, on constate que les phrase les plus lisibles sont celles dont la première lettre des mots est en majuscule (et encore mieux avec une lettre au mileu en rouge)
– les accolades, qui selon moi, rendent le code plus lisible et mieux structuré
bashing 3…2..1.. go ;-)
Tu oublis l’instruction goto, qui permet de sortir de plusieurs if d’affilé et manque cruellement à Python
Tout ceci est intéressant mais pourquoi avoir illustré l’article avec une image pornographique !? Visiblement l’image n’est plus presente sur le site mais apparaît dans le flux rss…
Toi, tu sais visiblement pas sur quel blog tu es tombé.
@Jonathan Sam & Max fait partie des rares sites qu’on pourrait classer dans la catégorie FW-NSFW, un site qui est pourtant tellement pratique pour le travail pour débuter en programmation et qui pourtant se place sur la sellette jusqu’au jour où un admin réseau un peu zélé décide de le placer dans la catégorie pour adulte et le bloque.
On voudrait bien partager des articles avec ses collègues qui débutent mais sans avertissement au préalable on risque vite de passer pour un pervers. «Qu’est-ce qui t’as amené à tomber sur ce site ?» juste la prog pardi…
Mais c’est peut-être un moyen à eux d’éviter la surcharge réseau, seuls les initiés s’y aventure !
Ou alors c’est au contraire pour attirer du monde.
En effet, une fois qu’on a goûté aux formatteurs automatiques (qui marchent), c’est difficile de s’en passer. J’ai du mal à croire que j’ai gaspillé des heures de ma vie à formater du code à la main.
Le support des docstrings serait top, mais c’est toujours en cours (https://github.com/ambv/black/issues/144). Idem pour la concaténation des string literals.
Ah et pour info, si vous écrivez des scripts shell, il y a shfmt: https://github.com/mvdan/sh
La philosophie reste la même (ta gueule) et c’est évidemment dispo en plugin pour tous les éditeurs (vim, emacs, ST, VScode etc.).