# articles

feed_id: http://sametmax.com/?p=16944
title: Le debug par la force du Q
uri: Le-debug-par-la-force-du-Q
url: http://sametmax.com/le-debug-par-la-force-du-q/
content:
<p>D’abord ça commence avec des <code>print()</code> puis du <a href="http://sametmax.com/debugger-en-python-les-bases-de-pdb/">pdb</a>&#8230; Puis le code se retrouve dans en background, ou on a des threads, des sous-processes, des proxies, son serveur WSGI qui tourne, etc. Et là, il faut sortir les outils de logging, la massue, le truc qui demande 3 ans à config.</p>
<p>Il n’y a pas de juste milieu.</p>
<p>Alors un mec nous a donné son <a href="https://github.com/zestyping/q">Q</a> pour changer tout ça.</p>
<pre>pip install q</pre>
<p>Q est typiquement un lib de feignasse :</p>

<div class="wp_syntax"><table><tr><td class="code"><pre class="python" style="font-family:monospace;"><span style="color: #ff7700;font-weight:bold;">def</span> bip<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>:
    a <span style="color: #66cc66;">=</span> <span style="color: #ff4500;">1</span>
&nbsp;
    <span style="color: #ff7700;font-weight:bold;">import</span> q
    q<span style="color: black;">&#40;</span>a<span style="color: black;">&#41;</span>
    q<span style="color: black;">&#40;</span>a + <span style="color: #ff4500;">1</span><span style="color: black;">&#41;</span>
&nbsp;
bip<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span></pre></td></tr></table></div>

<p>Et pouf, tout est loggé dans <code>/tmp/q </code>:</p>
<pre>
     0.0s bip: a=1
     0.0s bip: a + 1=2
</pre>
<p>On peut aussi l’utiliser en décorateur pour tracer l’exécution d’une fonction :</p>

<div class="wp_syntax"><table><tr><td class="code"><pre class="python" style="font-family:monospace;"><span style="color: #ff7700;font-weight:bold;">import</span> q
&nbsp;
<span style="color: #66cc66;">@</span>q
<span style="color: #ff7700;font-weight:bold;">def</span> bip<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>:
    a <span style="color: #66cc66;">=</span> <span style="color: #ff4500;">1</span>
bip<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span></pre></td></tr></table></div>

<p>Ce qui donne :</p>
<pre>
 0.0s bip()
 0.0s -> None
</pre>
<p>Ce n’est bien entendu pas fait pour être laissé dans le code, mais uniquement pour le debug. Néamoins c’est fort pratique :</p>
<ul>
<li>Le fichier de log est setup automatiquement. On peut quand même choisir le dossier avec la variable d’environnement <code> $TMPDIR</code></li>
<li>Le contenu de l’expression est logguée magiquement avec le résultat.</li>
<li>En prime on a le temps d’exécution.</li>
<li>Le fichier de sortie contient de la coloration syntaxique, s’il vous plait !</li>
</ul>
<p>Avec <a href="https://github.com/karan/joe">Joe</a>, qui génère des .gitignores, c’est ma petite découverte sympa de la rentrée.</p>


feed_id: http://sametmax.com/?p=16982
title: Le don du mois qui saute 1
uri: Le-don-du-mois-qui-saute-1
url: http://sametmax.com/le-don-du-mois-qui-saute/
content:
<p>Comme je le précisais au début de la série, il faut faire les dons à hauteur de ses moyens. Étant à -4000 euros sur ma compta, je ne fais donc pas de don du mois pour octobre. J’ai attendu le 31 pour être sûr que je ne pourrais pas le caler, mais ça ne sera pas possible.</p>
<p>Je publie quand même un article, car :</p>
<ul>
<li>Ca fait un mois que je n’ai pas donné de nouvelles </li>
<li>Il faut garder le truc en vie.</li>
<li>Je veux bien mettre en avant la philosophie du projet : faites à votre rythme, selon vos capacités. Donnez sans contrainte, sans culpabilité.</li>
</ul>
<p>Tous les projets autour du blog trainent un peu. J’ai what milles tickets ouverts sur github (je les regarde tous les jours pour me donner bonne conscience), mais bon, même S&#038;M ont parfois des mois difficiles ^^ Enfin &#8220;ont&#8221;&#8230; Max lui a trouvé un moyen de doubler son chiffre d’affaires donc il va payer l’apéro.</p>
<p>J&#8217;ai été assez actif sur twitter et un peu sur reddit, donc je me dis qu&#8217;un truc type &#8220;shaarly&#8221; ça serait quand même pas mal pour ramener chez nous tout ce contenu qu&#8217;on fournit à des services opaques. </p>
<p>Vous avez du noter aussi que le blog tombe souvent en marche. C&#8217;est parce que le serveur arrive au bout de sa capacité à héberger nos sites qui prennent trop de mémoire. Va falloir migrer vers une plus grosse instance, aussi plus chère. J&#8217;attends un peu.</p>
<p>À tout !</p>


feed_id: http://sametmax.com/?p=16986
title: itertools.product fait sauter des boucles imbriquées 1
uri: itertools-product-fait-sauter-des-boucles-imbriquées-1
url: http://sametmax.com/itertools-product-fait-sauter-des-boucles-imbriquees/
content:
<p>Je connais <code>product()</code> depuis bel lurette, et je n&#8217;avais jamais réalisé son utilité. Des fois on a le truc sous les yeux, comme ça, et on voit rien.</p>
<p>Vous savez, on veut parfois parcourir tous les trucs, et leur appliquer tous les machins. Ca donne une boucle imbriquée :</p>

<div class="wp_syntax"><table><tr><td class="code"><pre class="python" style="font-family:monospace;">res <span style="color: #66cc66;">=</span> <span style="color: black;">&#91;</span><span style="color: black;">&#93;</span>
<span style="color: #ff7700;font-weight:bold;">for</span> truc <span style="color: #ff7700;font-weight:bold;">in</span> trucs:
    <span style="color: #ff7700;font-weight:bold;">for</span> machin <span style="color: #ff7700;font-weight:bold;">in</span> machins:
        res.<span style="color: black;">append</span><span style="color: black;">&#40;</span>bidule<span style="color: black;">&#40;</span>machin<span style="color: #66cc66;">,</span> truc<span style="color: black;">&#41;</span><span style="color: black;">&#41;</span></pre></td></tr></table></div>

<p>Un code parfaitement légitime, clair, lisible. Mais l&#8217;envie de faire une liste en intension est si forte !</p>
<p>C&#8217;est là que <code>product()</code> intervient, avec ses petits bras musclés :</p>

<div class="wp_syntax"><table><tr><td class="code"><pre class="python" style="font-family:monospace;"><span style="color: #ff7700;font-weight:bold;">from</span> <span style="color: #dc143c;">itertools</span> <span style="color: #ff7700;font-weight:bold;">import</span> product
res  <span style="color: #66cc66;">=</span> <span style="color: black;">&#91;</span> bidule<span style="color: black;">&#40;</span>machin<span style="color: #66cc66;">,</span> truc<span style="color: black;">&#41;</span> <span style="color: #ff7700;font-weight:bold;">for</span> truc<span style="color: #66cc66;">,</span> machin <span style="color: #ff7700;font-weight:bold;">in</span> product<span style="color: black;">&#40;</span>truc<span style="color: #66cc66;">,</span> machin<span style="color: black;">&#41;</span><span style="color: black;">&#93;</span></pre></td></tr></table></div>

<p>Python va magiquement créer autant de tuples <code>(machin, truc)</code> qu&#8217;il y en a besoin.</p>
<p>Et parce que ça fait un mois que j&#8217;ai pas mis d&#8217;article, faut prendre tout de suite les bonnes habitudes : </p>
<div id="attachment_16988" style="width: 510px" class="wp-caption aligncenter"><a href="media/8fb095b1.tumblr_nx598dMRu71r539hzo1_500.jpg" data-original-source="http://sametmax.com/wp-content/uploads/2015/11/tumblr_nx598dMRu71r539hzo1_500.jpg" class="grouped_elements" rel="tc-fancybox-group16986"><img src="media/8fb095b1.tumblr_nx598dMRu71r539hzo1_500.jpg" data-original-source="http://sametmax.com/wp-content/uploads/2015/11/tumblr_nx598dMRu71r539hzo1_500.jpg" alt="Hommes nus assis sur un homme nus" width="500" height="397" class="size-full wp-image-16988" /></a><p class="wp-caption-text">for but in buts</p></div>


feed_id: http://sametmax.com/?p=16993
title: Breakpoint conditionnels
uri: Breakpoint-conditionnels
url: http://sametmax.com/breakpoint-conditionnels/
content:
<p>Quand vous lancez pdb, il est parfois intéressant stopper le programme à un endroit donné sous une certaine condition. En cela, on peut utiliser un if:</p>

<div class="wp_syntax"><table><tr><td class="code"><pre class="python" style="font-family:monospace;"><span style="color: #ff7700;font-weight:bold;">if</span> truc:
    <span style="color: #ff7700;font-weight:bold;">import</span> <span style="color: #dc143c;">pdb</span><span style="color: #66cc66;">;</span> <span style="color: #dc143c;">pdb</span>.<span style="color: black;">set_trace</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span></pre></td></tr></table></div>

<p>Mais il est aussi possible de le faire pendant qu&#8217;on est en train d&#8217;utiliser le debugger grace à la commande &#8220;b&#8221; :</p>
<p><code>b 50, age == 18</code></p>
<p>Ceci va mettre un breakpoint à la ligne 50 qui s&#8217;activera uniquement si age vaut 18.</p>
<p>Des fois que ça serve&#8230;</p>


feed_id: http://sametmax.com/?p=17022
title: Upgrade du server du blog 1 &#160; Recently updated !
uri: Upgrade-du-server-du-blog-1-Recently-updated
url: http://sametmax.com/upgrade-du-server-du-blog/
content:
<p>Bon, on va arrêter de faire les radins avec notre instance à $8 et on passe à $20 par mois. Ca va laisser le site un peu respirer parce qu&#8217;il tombait en rade tellement souvent que Google nous mettait premier pour les keywords &#8220;grand-mère hémiplégique&#8221;.</p>
<p>Go mamie, go ! </p>


feed_id: http://sametmax.com/?p=17032
title: Page de présentation du blog 1 &#160; Recently updated !
uri: Page-de-présentation-du-blog-1-Recently-updated
url: http://sametmax.com/page-de-presentation-du-blog/
content:
<p>On a reçu un mail d&#8217;un lecteur avec un texte de présentation de Sam et Max. Le travail est gigantesque, bien documenté, et franchement de bonne qualité.</p>
<p>Du coup cela sera la <a href="http://sametmax.com/mais-ou-je-suis-tombee-la/">page de présentation du blog</a> / FAQ / post épinglé du subreddit. Ca tombe bien, ça faisait super longtemps qu&#8217;on avait besoin d&#8217;un truc comme ça.</p>
<p>On a aussi un article invité qui arrive sur la pénétration anale.</p>
<p>Merci à tous ceux qui participent (correcteurs, modos, users de IE, animateurs sur IRC, etc), on se sent moins seul derrière son clavier.</p>
<p>Ca aide. Ca aide <strong>vraiment</strong>.</p>


feed_id: http://sametmax.com/?p=16998
title: DTC, si ça rentre, ça ne ressort pas ! 1
uri: DTC-si-ça-rentre-ça-ne-ressort-pas-1
url: http://sametmax.com/dtc-si-ca-rentre-ca-ne-ressort-pas/
content:
<p style="text-align: center;"><em><small>Ceci est un post invité de <a href="http://sametmax.com/author/Oligator/">Oligator</a> posté sous licence <a href="http://creativecommons.org/licenses/by/3.0/">creative common 3.0 unported</a>.</small></em></p>
<p>Voici un petit point technique sur notre anatomie (hommes et femmes) et une mise en garde sur la pénétration anale à l&#8217;aide d&#8217;un sex-toy&#8230; ou encore d&#8217;un objet du quotidien pour les plus économes !</p>
<p>Il faut savoir que cette zone sensible présente une particularité souvent méconnue des amateurs en herbe de sensations fortes ! Tout objet inséré dans l&#8217;anus est fort susceptible d&#8217;y rester coincer à partir du moment où sa base passe la barrière du sphincter interne (zone musculaire circulaire localisée quelques cm après l&#8217;entrée de l&#8217;anus) d&#8217;où certaines complications majeures faciles à imaginer.</p>
<p>Certains fournisseurs de sex-toys l&#8217;indiquent d&#8217;ailleurs très clairement <a href="http://www.sextoy.fr/precautions-d-usage-pour-les-sextoys.html">dans leurs précautions d&#8217;emploi dédiées</a> : pour une stimulation de ce type, il faut impérativement utiliser une référence prévue pour, c&#8217;est-à-dire pourvue d&#8217;une large base empêchant toute absorption.</p>
<p><img class=" size-full wp-image-17013 alignnone" src="media/63547de6.sex-toy.jpg" data-original-source="http://sametmax.com/wp-content/uploads/2015/11/sex-toy.jpg" alt="Sex-toy anal" width="300" height="300" /></p>
<p>&nbsp;</p>
<p><strong>Accident domestique ou sexuel, même combat</strong></p>
<p>Si une telle mésaventure devait vous arriver, mieux vaut vous rendre directement aux urgences où le personnel est largement habitué à gérer ce genre d&#8217;accidents domestiques&#8230;</p>
<p>Oui, et en particulier lorsque le corps étranger incriminé est une bouteille en verre, un bout de manche à balai ou encore un concombre bio, le patient affirme la plupart du temps se l&#8217;être malencontreusement introduit en glissant ou en s&#8217;asseyant dessus !</p>
<p><img class=" size-full wp-image-17012 alignnone" src="media/8425e130.radio.jpg" data-original-source="http://sametmax.com/wp-content/uploads/2015/11/radio.jpg" alt="oops" width="300" height="300" /></p>
<p>&nbsp;</p>
<p><strong>Mourir de honte ou mourir, il faut choisir</strong></p>
<p>Car n&#8217;imaginez pas que votre joujou va ressortir tout seul. Non non, ce serait bien trop simple. Vous pourriez attendre longtemps avant de vous soulager, tellement longtemps que <a href="http://www.mirror.co.uk/news/uk-news/man-died-after-vibrator-became-4039637">vous pourriez même en mourir</a> ! Vous l&#8217;avez compris, le passage par la case hôpital s&#8217;avère indispensable.</p>
<p>Si vous êtes chanceux, vous aurez droit à quelques sédatifs ou une anesthésie générale, le tout accompagné d&#8217;une exploration de votre orifice à l&#8217;aide d&#8217;un spéculum et d&#8217;une pince pour retirer l&#8217;ustensile. Pour les moins chanceux, direction le bloc opératoire pour une intervention chirurgicale.</p>
<p>Pour en revenir aux sex-toys, les incidents liés semblent en nette hausse d&#8217;après les différentes sources : +50% par rapport à 2007 aux Etats-Unis, avec un pic en 2012 associé par certains à la sortie du roman érotique <em>Fifty Shades of Grey</em> !</p>
<p>Sur le vieux continent, le dernier en date recensé évoque un sex-toy de 23 cm coincé dans le rectum d&#8217;un irlandais, mais ce dernier semble être un sacré coquin puisqu&#8217;<a href="http://www.medisite.fr/a-la-une-un-sextoy-de-23-cm-coince-dans-le-rectum.922992.2035.html">il s&#8217;agirait d&#8217;un récidiviste</a> !</p>
<p>Vous voilà prévenu maintenant.</p>
<p>Et pour terminer en fanfare, une dépêche AFP {risible|tragique}, non loin du sujet !</p>
<p><iframe src="https://www.youtube.com/embed/so2KB-O9wNw" width="420" height="315" frameborder="0" allowfullscreen="allowfullscreen"></iframe></p>


feed_id: http://sametmax.com/?p=17038
title: Update du mirroir du blog 3
uri: Update-du-mirroir-du-blog-3
url: http://sametmax.com/update-du-mirroir-du-blog/
content:
<p>Depuis que je me suis remis à bosser sur le blog, les coups de main afflux, et c&#8217;est très agréable.</p>
<p>Par exemple, on a un <a href="https://github.com/sametmax/miroir-du-blog">mirroir statique du blog</a> sur github pour pouvoir le lire hors ligne. On peut le cloner, mais le plus simple est de <a href="https://github.com/sametmax/miroir-du-blog/archive/master.zip">télécharger le zip</a>.</p>
<p>Le problème, c&#8217;est qu&#8217;il faut crawler le blog pour le générer, et je ne l&#8217;ai pas fait depuis des mois et des mois, du coup il manquait plein de trucs.</p>
<p>Mais c&#8217;est sans compter <a href="https://github.com/sametmax/miroir-du-blog/graphs/contributors">une petite PR</a> d&#8217;un lecteur, qui a tout mis à jour.</p>
<p>Merci pour le poisson !</p>


feed_id: http://sametmax.com/?p=17045
title: Le don du mois : framasoft 2 &#160; Recently updated !
uri: Le-don-du-mois-framasoft-2-Recently-updated
url: http://sametmax.com/le-don-du-mois-framasoft/
content:
<p>La période de vaches maigres se termine, et les dons reprennent.</p>
<p>Ce mois-ci, c’est <a href="https://framasoft.org">Framasoft</a>, la référence site français en matière de soutien au monde du libre, qui en bénéficie.</p>
<p>J’ai commencé ma carrière avec deux sites : le (feu) site du zéro, aujourd’hui transmuté en l’horrible openclassroom, et framasoft.</p>
<p>Pour ceux qui ne connaissent pas framasoft, ils ont commencé comme un annuaire de logiciels libres, au début surtout pour Windows. Ils permettaient à des petits joueurs de se faire connaitre comme Firebird, l’ancêtre de Firefox, et animaient une communauté super sympa via leur forum. Quand je cherchais une info sur un logiciel, je passais toujours par eux : c’était un gain de temps, et j’avais beaucoup plus confiance en leurs sources que les autres.</p>
<p>Aujourd’hui Framasoft a bien évolué : <a href="http://framabook.org/">éditeur de livres</a>, mise en place de <a href="http://framablog.org/2015/10/05/degooglisons-saison-2-ils-ne-savaient-pas-que-cetait-impossible-alors-ils-lont-fait/">nombreux web services</a> et blog d’alerte&#8230;</p>
<p>Je ne rends presque plus visite à l’annuaire original, mais j’utilise quelques services comme <a href="https://framapad.org/">framapad</a>.</p>
<p>Ce don, c’est surtout pour les nouveaux venus dans le monde du libre, qui auront besoin comme j’en ai eu besoin d’une source d’information fiable. C’est aussi un remerciement, et la reconnaissance de leur ténacité : ils sont là depuis des années malgré les problèmes de trésorerie et la masse de taff incroyable que ça représente.</p>
<p><a href="https://soutenir.framasoft.org/">50 euros pour eux</a>. Et merci.</p>


feed_id: http://sametmax.com/?p=17120
title: Nan mais vous êtes sérieux ? 3
uri: Nan-mais-vous-êtes-sérieux-3
url: http://sametmax.com/nan-mais-vous-etes-serieux/
content:
<p>Je donne sa chance au produit.</p>
<p>Vraiment.</p>
<p>Régulièrement je retourne vers des technos que j’avais écartées pour voir comment elles ont évolué. Peut-être que c’est mieux. Peut-être que j’ai changé et que maintenant ça me parle.</p>
<p>Mais là, non ! Non, non, non !</p>
<p>Je m’en vais rejeter un coup d’œil vers buildout, un des premiers systèmes de build Python qui résout les problématiques de pip, virtualenv, cookiecutter et bien plus. Un couteau suisse robuste et polyvalent.</p>
<p>Je l’avais mis à la poubelle pour sa difficulté de mise en œuvre et sa doc pourrie. Un produit typiquement dans la philosophie de la communauté Zope dont il est issu : super puissant, super flexible, et aussi plaisant qu’un cactus dans le cul.</p>
<p>Réflexe logique, aller sur la page pypi, et dès le premier paragraphe je lis la description:</p>
<blockquote><p>Assembly runs the gamut from stitching together libraries to create a running program, to production deployment configuration of applications, and associated systems and tools</p></blockquote>
<p><a href="https://pypi.python.org/pypi/zc.buildout/2.5.0">Je n’invente rien.</a> Il y a quelqu’un qui a écrit ce texte en ce disant que c’était pertinent. Il a dû être super content de lui le con. Je l’imagine en train de taper sur son clavier, mettant un point final à cette phrase un sourire aux lèvres. Fier. Le sentiment du devoir accompli.</p>
<p>Il avait raison sur un point : avec ce texte on sait tout ce donc on a besoin de savoir sur buildout, c’est-à-dire qu’il ne faut pas l’utiliser.</p>
<p>Au revoir. </p>
<p>P.S: je ne suis pas un demeuré, bien entendu que je suis allé plus loin et que j’ai passé du temps sur le site officiel. Je vous fais ici un résumé de ma pensée.</p>


feed_id: http://sametmax.com/?p=17131
title: Mon trouble obsessionnel compulsif veut un poney &#160; Recently updated !
uri: Mon-trouble-obsessionnel-compulsif-veut-un-poney-Recently-updated
url: http://sametmax.com/mon-trouble-obsessionnel-compulsif-veut-un-poney/
content:
<p>Il y a une tendance ces derniers temps à vouloir demander des changements dans Python pour le faire correspondre à une quelconque forme de fantasme qu’a chaque membre de chaque secte qui demande à corps et à cri telles ou telles modifications.</p>
<p>Sans ordre précis :</p>
<ul>
<li><a href="http://sametmax.com/le-debat-sur-la-programmation-fonctionnelle-en-python/">la FP c’est la vie</a>. Tout doit être immutable.</li>
<li>je veux que tout soit asynchrone. Ouai même les boucles for. Au et puis non, filez-moi juste des appels récursifs. </li>
<li> aucun programme n’est possible sans multi-core. Il me faut du multi-core. Mon problème d’érection vient du fait que mon script de 3 lignes utilise qu’un CPU.</li>
<li>il faut <a href="http://www.snarky.ca/if-i-were-designing-imort-from-scratch">changer le système d’import</a> pour faire juste &#8220;import truc&#8221; et rien d’autre. </li>
<li>les go-routine c’est trop de la balle, réécrivez tout Python pour retirer le GIL, virez asyncio et changez la syntaxe du langage.</li>
<li>et je veux des lamdbdas multilignes, Dieu m’a dit que c’était bon.</li>
<li>le typage dynamique est une honte, haskel est un vrai langage, il faut faire pareil.</li>
<li>mais où est le pattern matching ?</li>
</ul>
<p>En clair, vous ne voulez pas utiliser Python. </p>
<p>Alors utilisez un autre langage : Lisp, Go, Javascript, whatever, mais ne demandez pas à un chien de miauler.</p>
<p>Le système d’import est là aussi pour faciliter la programmation exploratoire et les scripts courts, pas juste de coder Youtube. La mutabilité permet de simplifier la formation des nouveaux venus. Tout programme n’a pas de problèmes de performances, et ceux qui en ont ne sont pas forcément résolues pas l’asyncio ou le multicore, qui rendent le code plus complexe. Le GIL rend Python rapide sur un core et facile la maintenance pour la toute petite équipe des core dev. Les restrictions syntaxiques ont un but stylistique qui tient de la philosophie du langage basée sur la facilité à lire le code. Le typage dynamique permet la rapidité d’écriture d’un code souple.</p>
<p>Ce sont des fonctionnalités. Pas des accidents. Des choix volontaires, conscients.</p>
<p>Elles se font au détriment d’autres fonctionnalités, et <a href="http://sametmax.com/pourquoi-il-est-difficile-de-juste-ajouter-un-nouveau-mot-cle/">tient compte de l&#8217;héritage du passé</a>, car on ne peut pas tout avoir. Peut-être préférez-vous les autres fonctionnalités. Vous avez le droit. Mais dans ce cas utilisez une techno qui a fait le choix de mettre ces dernières en avant, ne demandez pas à un projet qui n’a pas fait les choix que vous voulez de changer.</p>
<p>Est-ce que ça veut dire que Python ne va pas changer ? Qu’il va rester dans son état et ne pas se moderniser ?</p>
<p>Certainement pas !</p>
<p>En fait durant les 3 dernières années Python a énormément changé.</p>
<p>Toutes ces demandes sont entendues, et explorées et quand on y trouve une réponse bénéfique, celle-ci est intégrée au langage. Python 3 a été une réponse. Les types hints ont été une réponse. Asyncio a été une réponse. La 3.6 aura une réponse pour le multi-core.</p>
<p>Mais ces réponses se feront toujours dans le cadre de la philosophie et des contraintes du langage. Inutile d’espérer que Python se transforme en Erlang, Julia ou R, ça n’arrivera pas. </p>
<p>Par ailleurs, vous n’en avez probablement pas besoin. La plupart de ces requêtes sont des demandes de poney car il existe déjà des outils pour traiter ces problématiques. C’est vrai que ça peut être cool un poney, mais ce n’est pas un besoin. La PSF est généreuse en dons équestres, et on en reçoit régulièrement, mais il ne faut pas abuser : utilisez les méthodes qui existent déjà et qui fonctionnent. Ou comprenez que votre façon de faire n&#8217;est pas celles de milliers d&#8217;utilisateurs de Python qui l&#8217;utilisent ainsi parce que cette manière est optimale pour eux.</p>
<p>Apprenez à connaitre le langage plutôt que vouloir le muter pour qu’il comble votre TOC ou votre ignorance. </p>
<p>Parfois, les outils en place ne sont pas suffisants ou sont contraignants. C’est le cas pour le packaging, par exemple. Et dans ce cas, soyez patient, ou participez à la solution, ou encore prenez une autre techno. Toutes ces réponses sont valables. Tapez du pied et pleurer pour un appaloosa ne l’est pas.</p>


feed_id: http://sametmax.com/?p=17155
title: Le don du mois : la quadrature du net &#160; Recently updated !
uri: Le-don-du-mois-la-quadrature-du-net-Recently-updated
url: http://sametmax.com/le-don-du-mois-la-quadrature/
content:
<p>Je suis très content d’avoir inspiré d’autres blogs à suivre le principe &#8220;du don du mois&#8221;.</p>
<p>Mais pour une fois, c’est moi qui vais suivre le mouvement et me laisser inspirer par sciunto et son <a href="https://blog.sciunto.org/posts/don_lqdn/">don pour la quadrature</a>.</p>
<p>Pour ceux qui ne les connaissent pas, <a href="https://www.laquadrature.net/">cette asso</a> s’engage jusqu’aux couilles dans le processus démocratique, particulièrement dès que ça touche Internet.</p>
<p>Ces lanceurs d’alerte nous tiennent informés, lancent les débats qui comptent, et interviennent auprès des élus avec une persévérance qui tient de l’écureuil de l’âge de glace.</p>
<p>Le tout sans agression, avec intelligence, analyse et respect.</p>
<p>Merci. 50 euros pour cette <a href="https://support.laquadrature.net/">donation.</a></p>


feed_id: http://sametmax.com/?p=17160
title: On a tenté de nous hacker 0bin
uri: On-a-tenté-de-nous-hacker-0bin
url: http://sametmax.com/on-a-tente-de-nous-hacker-0bin/
content:
<p>En me baladant dans l’arbo de <a href="http://0bin.net">0bin.net</a> pour retirer un dox qu’on m’avait signalé je suis tombé sur un dossier au nom bizarre :</p>

<div class="wp_syntax"><table><tr><td class="code"><pre class="bash" style="font-family:monospace;"><span style="color: #666666; font-style: italic;"># ls -l</span>
total <span style="color: #000000;">4</span>
<span style="color: #000000;">4</span> drwxr-xr-x <span style="color: #000000;">7</span> root root <span style="color: #000000;">4096</span> sept. <span style="color: #000000;">24</span>  <span style="color: #000000;">2014</span> <span style="color: #660033;">--</span></pre></td></tr></table></div>

<p>Hum, voilà qui n’est pas pratique à analyser depuis le bash. Et pourquoi j’ai ce dossier-là d’ailleurs ?</p>
<p>Regardons ce qu’il y a dedans :</p>

<div class="wp_syntax"><table><tr><td class="code"><pre class="bash" style="font-family:monospace;"><span style="color: #666666; font-style: italic;"># python -c &quot;import os; os.rename('--', &quot;strange_dir&quot;)&quot;</span>
<span style="color: #666666; font-style: italic;"># ls strange_dir/</span>
3Q  Dk  k5  Oh  <span style="color: #660033;">-u</span></pre></td></tr></table></div>

<p>Oh, y a un dossier nommé &#8220;-u&#8221;&#8230;</p>
<p>A ce stade-là, j’ai juste supprimé le truc.</p>
<p>Et j’ai réalisé : on crée les dossiers de l’arbo en fonction du nom du paste, qui est une clé générée côté client, et donc fournie par un code JavaScript non trusté.</p>
<p>Du coup l’attaqueur nous a sans doute passé une commande bash comme nom de fichier. J’imagine, espérant qu’on fabriquait le fichier avec une exécution shell. Comme on utilise le module os pour le faire, je pense que ça n’a eu aucun impact.</p>
<p>J’espère :)</p>


feed_id: http://sametmax.com/?p=17172
title: Vous n&#8217;avez pas besoin de nous demander
uri: Vous-n-avez-pas-besoin-de-nous-demander
url: http://sametmax.com/vous-navez-pas-besoin-de-nous-demander/
content:
<p>Aujourd’hui, on nous a demandé la permission, par mail, de réutiliser un de nos schémas pour <a href="http://www.vulgairedev.fr/blog/article/deploiement-django">un article.</a></p>
<p>C’est très gentil de demander, mais vraiment ne vous embêtez pas avec ça.</p>
<p>Tout le blog, sauf mention spécifique explicite, est sous licence <a href="http://creativecommons.org/licenses/by/3.0/">Creative Common Unported</a>. Vous pouvez donc prendre notre contenu et le publier ailleurs, le modifier, et même le vendre. Et vous ne nous devez rien.</p>
<p>La seule obligation, c’est de citer la source : mettez un lien vers le blog à côté de ce que vous réutilisez.</p>
<p>C’est tout.</p>
<p>Donc inutile de nous écrire pour nous piquer un truc. Pillez mes amis, pillez.</p>


feed_id: http://sametmax.com/?p=17187
title: Retour sur la pénurie de devs 3 &#160; Recently updated !
uri: Retour-sur-la-pénurie-de-devs-3-Recently-updated
url: http://sametmax.com/retour-sur-la-penurie-de-devs/
content:
<blockquote><p>Rien qu’autour de moi, on cherche pas loin de 50 devs joomla</p></blockquote>
<p>Un de mes amis m’a rapporté cette discussion qu’il a eue avec un de ses contacts sur Nantes. Nantes n’est pas une grande ville. Et joomla est un CMS parmi d’autres. Par ailleurs, les CMS ce sont qu’une techno Web parmi d’autre. Et la prog Web n’est qu’une sorte de prog parmi d’autres.</p>
<p>Or je vous rappelle ce qu’on m’avait déjà sorti:</p>
<blockquote><p>Quand on trouve un profil qui maitrise toutes les technos de notre stack, on lui sort le tapis rouge. C’est juste improbable.</p></blockquote>
<p>Un collègue à moi, qui me disait il y a 2 ans autour d’un verre que son process de recrutement le minait.</p>
<blockquote><p>Si tu viens ici je te trouve un poste à 220k.</p></blockquote>
<p>Un client américain de longue date qui me recrute en remote parce que « c’est moins cher » et qui me confiait les tarifs pour mon profil étaient aberrant chez lui.</p>
<p><strong>Le fait est qu’on a déjà du mal à trouver de la main-d’œuvre bien qualifiée maintenant.</strong></p>
<p>Il y a plusieurs raisons à cela.</p>
<p>D’abord, il y a un nombre limité de devs, et une explosion du nombre de technos. Ces technos se sont, dans la dernière décennie, organisées en stacks complexes. Elles sont accumulées des process et des couches d’abstraction, qui évoluent rapidement pour répondre à la mutation des usages : mobile, streaming, temps réel, offline-mode, synchronisation&#8230;.</p>
<p>La virtualisation, le cloud, les pré-processeurs, la variété des plateformes, le big data, etc. C’est à n’en plus finir.</p>
<p>Une personne ne peut pas tout savoir, et il peut être productif uniquement dans un nombre limité de domaines, nombre dont la taille en — proportion au reste du monde — se réduit année après année.</p>
<p>Les attentes des gens ayant évoluées, faire un service web compétitif aujourd’hui demande des compétences côté client et serveur, du design et la compréhension du mobile, ne serait-ce que pour un proto tout pourri. Et ça c’est que pour le Web. N’oubliez pas l’embarqué, le sysadmin, l’analyse de données, l’exploration scientifique, le jeu vidéo, les systèmes d’information internes, les systèmes métier et tout le bordel.</p>
<p><strong>Bref, il faut du monde. Du monde compétent. Ce monde est limité, et la demande augmente de par la complexification de nos outils et contraintes, comme on vient de le voir. Mais aussi par la demande technologique qui grimpe.</strong></p>
<p>Car aujourd’hui nous avons des ordinateurs partout. Un dans la poche, un dans la voiture, un dans la télé et certains essayent d’en foutre dans les chaussures et le frigo. La fiche de paie, l’ordonnance du médecin, la facture de carrefour et l’avis d’imposition sont tous traités par informatique.</p>
<p>Ceci ne va pas ralentir. Même si on ne se rajoutait pas de nouveaux usages, et on va le faire, on a 3 milliards d’asiastiques qui sont en plein boom technologique.</p>
<p>Et là vous allez me dire que c’est justement l’occasion de faire du offshore pour répondre à la demande.</p>
<p>Mais ça ne marche pas.</p>
<p>J’ai suivi l’expérimentation de la sous-traitance à l’étranger des années post-bulle. A l’époque, j’ai entendu un décideur dire :</p>
<p><blocquote>on peut très prendre un Indien pour pisser du code</blocquote>. </p>
<p>J’ai aussi vu tous ces projets se planter. Une fois, un simple proto Web couter 140 000 euros pour au final n’avoir AUCUNE fonctionnalité. Véridique. 140 boulasses.</p>
<p>Ce n’est pas qu’on ne trouve pas les compétences adéquates hors de nos contrées. Au contraire. Mais il y a des tas de facteurs propres à notre industrie qui rendent l’exercice de la délégation délicate, et ça ferait un excellent article, mais je ne vais pas les exposer ici.</p>
<p>L’important c’est de noter qu’on en revient. Les devs indiens vont participer à l’essor de l’informatique de l’Inde. Les devs chinois à celui de la Chine. Et ils auront les mêmes problématiques que nous à différentes échelles et sous d’autres visages. Mais le croisement des effluves sera limité.</p>
<p>En revanche la globalisation va faire que leur expansion va, par effet de bord, augmenter notre activité informatique en plus de nos propres causes de croissance. On va avoir besoin de plus de devs.</p>
<p>Toujours plus, encore plus.</p>
<p><strong>Des devs de plus en plus spécialisés pour des archis de plus en plus complexes.</strong></p>
<p>Je ris quand les gens disent que les nouveaux venus ne sont pas « des vrais programmeurs ». <q>Moi je devais faire attention au nombre de cycle CPU</q>, <q>aujourd’hui les gens rachètent des serveurs plutôt que d’économiser de la mémoire</q>, etc.</p>
<p>Ouai, ouai, pépé, tu faisais de l’assembleur, c’est viril. </p>
<p>Mais le gamin de 20 ans qui arrive sur le marché, on lui colle entre les pates une app react native en JSX qui une fois transpilée par babel + gulp va se faire valider dans un market pour s’installer dans une sandbox sur des terminaux hétérogènes où elle va taper à travers un réseau wifi/4G intermittent dans une API REST sur des serveurs distants organisés en microservices Django + dockers déployés sur du cloud AWS. </p>
<p>Le seul point commun avec les deux époques, c’est qu’il n’y a toujours pas de doc.</p>
<p>Donc non, l’informatique n’est pas plus facile aujourd’hui. Il y a plus à savoir. Ça change plus vite. Chacun est plus cloisonné dans sa bulle de connaissance spécialisée. Et ça s’accélère.</p>
<p>Les challenges sont là, juste différents, mais la demande est immensément plus forte.</p>
<p>Et puis vous imaginez la gueule de l’offre d’emploi pour recruter ce profil ? Doit parler anglais, 7 ans d’expérience en mécanique quantique, taper en Bépo est un plus. On paie le SMIC mais on offre le café illimité comme chez Google.</p>
<p><strong>A cela se rajoute le problème que les formations ne suivent pas.</strong></p>
<p>Elles ne produisent pas assez d’informaticiens.</p>
<p>Elles produisent des profils sur des technos qui ne sont pas à jour, qui ne correspondent pas à la demande du marché.</p>
<p>Et surtout, elles produisent des nazes.</p>
<p>Pardonnez-moi, jeunes gens, ce n’est pas personnel.</p>
<p>Mais 90% des gens qui sortent de l’école, je ne souhaite jamais bosser avec. Jamais.</p>
<p>Ils sont mauvais, au point de ne pas savoir coder un script simple. Certains ne savent pas exprimer une idée clairement, et encore moins définir un problème. Même pas poser les bonnes questions.</p>
<p>Alors oui, ça peut s’améliorer une fois en entreprise, mais qui a le temps ? Qui a l’envie ? Qui a l’argent ?</p>
<p><strong>Vous vous imaginez en train d’embaucher un cuisinier qui ne sait pas faire cuir un oeuf ?</p>
<p>C’est exactement ce qui se passe en informatique.</strong></p>
<p>D’un côté on a des demandes du marché pour des profils qui font plus.</p>
<p>De l’autre on a des filières qui mettent sur le marché des gens qui ne font rien.</p>
<p>En même temps, on n&#8217;est pas près à payer ceux qui ont les compétences au juste prix, et les grosses boîtes comme Google, Facebook, Microsoft, Yahoo, Instragram, Twitter, etc. raflent tout.</p>
<p>Il ne reste plus rien.</p>
<p>Je résume :</p>
<ul>
<li>Une demande qui augmente.</li>
<li>Un marché fragmenté qui se complexifie.</li>
<li>Une formation au plus bas.</li>
<li>Des industries de pointe qui ne laissent que des miettes.</li>
</ul>
<p>Voilà la situation <em>aujourd’hui</em>.</p>
<p>Et j’entends encore des devs qui me disent qu’ils ne trouvent pas du boulot. </p>
<p>Et ça m’énerve !</p>
<p>Il y a d’abord le mec, qui ne veut pas bouger, pas changer de technos, pas se mettre à jour, et cherche une fois par mois de la même façon que le faisait sa grand-mère.</p>
<p>Il y a la meuf qui est dans le fond de la creuse sur un modem 56k et qui ne comprend pas que les starts up ne frappent pas à sa porte.</p>
<p>Ceux qui ont 3 enfants et qui ne peuvent pas. Peuvent pas quoi, je ne sais pas, mais ils peuvent pas. Ceux qui envoient des CV et n’appellent jamais. Ceux qui arrivent à l’entretien d’embauche avec des miettes dans la barbe. Ceux qui attendent chez eux qu’un miracle arrive alors qu’ils ont 15 évènements de réseautage dans leur inbox&#8230;</p>
<p>Bref, non seulement le marché est déséquilibré, mais une partie du vivier s’assure activement de rester inaccessible à l’embauche.</p>
<p>En plus — merde ! — le dev c&#8217;est quand même un des métiers les plus permissifs du monde.</p>
<p>Tu es en chaise roulante ? Cool des aides.</p>
<p>Tu as des piercings ? Ranafout.</p>
<p>Tu as la capacité sociale d’une moule ? On s’arrange.</p>
<p>Tu veux bosser chez toi ? Ça se négocie.</p>
<p>Alors je ne dis pas que tout le monde embauche. Je ne prétends pas qu’il suffit de se balader avec une pancarte dans la rue avec marqué « échange COBOL contre soussous » pour faire fortune.</p>
<p>Mais tout de même, on est sur le marché le plus florissant du monde. Faites péter le champagne !</p>
<p>Et si vous êtes recruteurs, préparez-vous à devenir attractif. Car vous risquez fort de vous retrouver uniquement avec le fond du panier, et à remercier le ciel qu’on vous en ait laissé.</p>
<p>Or la compétitivité d’une boite dépend aujourd’hui tout autant de sa vitrine IRL que de celle en ligne. Et le prix du ticket d’entrée du service minimum a pris une sacrée inflation. J’ai vu cette semaine une meuf s’enerver qu’un site ne marchait pas parce que la barre de recherche n’avait pas de résultat live : il fallait appuyer sur le bouton pour avoir les résultats.</p>
<p>Tirez-en la conclusion que vous voulez, mais tirez-la vite.</p>


feed_id: http://sametmax.com/?p=17365
title: Pourquoi utiliser un mécanisme d&#8217;exceptions ?
uri: Pourquoi-utiliser-un-mécanisme-d-exceptions
url: http://sametmax.com/pourquoi-utiliser-un-mecanisme-dexceptions/
content:
<p>L&#8217;article ne vient pas de nulle part. En fait j&#8217;ai du dans la même journée répondre sur le subreddit Python a cette question et à un lecteur par email, alors je me suis dis qu&#8217;il serait bon de faire le point ici.</p>
<p>Avant de lire l&#8217;article, assurez-vous de bien avoir compris <a href="http://sametmax.com/gestion-des-erreurs-en-python/">la gestion des erreurs en Python</a>.</p>
<h2>La réponse simple, courte, et définitive</h2>
<p>&#8220;Pourquoi utiliser un mécanisme d&#8217;exceptions ?&#8221; </p>
<p>Parce que c&#8217;est la manière de faire du langage que vous utilisez.</p>
<p>Si vous êtes dans un langage qui utilise des codes de retour comme C, alors gérez les erreurs avec des codes de retour. Si vous avez du pattern matching comme en Rust, utilisez le pattern matching. Si vous avez des exceptions comme en Java, utilisez les exceptions. Si vous avez des guards comme en swift, vous utilisez des guards.</p>
<p>Je crois que vous avez pigé.</p>
<p>Ceci est la règle, qui est complètement indépendante de la qualité du système de gestion des erreurs : codez dans le style approprié pour le langage et n&#8217;essayez pas de bricoler votre solution perso dans votre coin. Si ça vous fait chier, changez de langage. Mais ne faites pas du pseudo-Go en Erlang, ça n&#8217;a pas de sens, et ça va souler tous vos collègues, en plus de diminuer l&#8217;intégration avec l&#8217;écosystème de la techno.</p>
<p>En Python, les erreurs (et même plus) sont gérées via un mécanisme à base d&#8217;exceptions, et c&#8217;est donc ce qu&#8217;il faut utiliser.</p>
<p>Maintenant, un mécanisme de gestion d&#8217;erreurs est vraiment une question de gout. Il n&#8217;y en a pas de parfait, mais il peut être intéressant de connaitre les points forts de celui qu&#8217;utilise Python.</p>
<p>Quels sont donc les points forts d&#8217;un mécanisme à base d&#8217;exceptions ?</p>
<h2>Cela évite les races conditions</h2>
<p>En informatique, dès qu&#8217;un système peut faire plusieurs choses à la fois, plusieurs choses peuvent arriver simultanément et créer ce qu&#8217;on appelle une race condition.</p>
<p>Par exemple, si je fais ceci:</p>

<div class="wp_syntax"><table><tr><td class="code"><pre class="python" style="font-family:monospace;"><span style="color: #808080; font-style: italic;"># on vérifie que le fichier existe avant de l'ouvrir</span>
<span style="color: #ff7700;font-weight:bold;">if</span> <span style="color: #dc143c;">os</span>.<span style="color: black;">path</span>.<span style="color: black;">isfile</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'monfichier'</span><span style="color: black;">&#41;</span>: 
    <span style="color: #ff7700;font-weight:bold;">with</span> <span style="color: #008000;">open</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'monfichier'</span><span style="color: black;">&#41;</span> <span style="color: #ff7700;font-weight:bold;">as</span> f:
        <span style="color: #ff7700;font-weight:bold;">print</span><span style="color: black;">&#40;</span>f.<span style="color: black;">read</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span><span style="color: black;">&#41;</span>
<span style="color: #ff7700;font-weight:bold;">else</span>:
    <span style="color: #ff7700;font-weight:bold;">print</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'pouet'</span><span style="color: black;">&#41;</span></pre></td></tr></table></div>

<p>Entre la première ligne et la seconde ligne s&#8217;écoule un temps très court pendant lequel un autre processus peut supprimer le fichier et faire planter mon programme.</p>
<p>En utilisant la philosophie &#8220;il est plus facile de demander pardon que la permission&#8221;, on évite ce problème:</p>

<div class="wp_syntax"><table><tr><td class="code"><pre class="python" style="font-family:monospace;"><span style="color: #ff7700;font-weight:bold;">try</span>:
    <span style="color: #808080; font-style: italic;"># on s'en bat les couilles, on essaye</span>
    <span style="color: #808080; font-style: italic;"># de l'ouvrir à sec</span>
    <span style="color: #ff7700;font-weight:bold;">with</span> <span style="color: #008000;">open</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'monfichier'</span><span style="color: black;">&#41;</span> <span style="color: #ff7700;font-weight:bold;">as</span> f:
        <span style="color: #ff7700;font-weight:bold;">print</span><span style="color: black;">&#40;</span>f.<span style="color: black;">read</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span><span style="color: black;">&#41;</span>
<span style="color: #ff7700;font-weight:bold;">except</span> <span style="color: #008000;">OSError</span>:
    <span style="color: #ff7700;font-weight:bold;">print</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'pouet'</span><span style="color: black;">&#41;</span></pre></td></tr></table></div>

<p>Dans ce cas on tente d&#8217;ouvrir le fichier de toute façon, et si l&#8217;ouverture déclenche une erreur, alors on la gère. Pas de race condition.</p>
<h2>Finally et with pour les opérations de nettoyage</h2>
<p>En ouvrant mon fichier, je dois m&#8217;assurer de le fermer après. Mais je peux avoir une erreur à l&#8217;ouverture du fichier, ou pendant sa lecture, qui fasse planter mon programme et que je n&#8217;avais pas prévu.</p>
<p>Comme les exceptions remontent file d&#8217;appel, on peut les attraper à plusieurs niveaux. Grave à <code>finally</code> (et <code>with</code> qui enrobe finally), on peut donc très élégamment s&#8217;assurer que les opérations de nettoyage sont lancées automatiquement, même si tout pête:</p>

<div class="wp_syntax"><table><tr><td class="code"><pre class="python" style="font-family:monospace;"><span style="color: #808080; font-style: italic;"># ouvrir un fichier avec with garantit sa fermeture</span>
<span style="color: #ff7700;font-weight:bold;">with</span> <span style="color: #008000;">open</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'monfichier'</span><span style="color: black;">&#41;</span> <span style="color: #ff7700;font-weight:bold;">as</span> f:
    <span style="color: #ff7700;font-weight:bold;">print</span><span style="color: black;">&#40;</span>f.<span style="color: black;">read</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span><span style="color: black;">&#41;</span></pre></td></tr></table></div>

<h2>Les exceptions sont très explicites</h2>
<p>Des mécanismes comme le pattern matching ou le retour de codes sont génériques, et peuvent être utilisés pour à peu près tout.</p>
<p>Les exceptions, à l&#8217;image des guards, ont un champ d&#8217;usage plus restreint, et quand on en voit, on sait donc généralement à quoi s&#8217;en tenir. Cela facilite la lecture en diagonale du code.</p>
<h2>Les exceptions décrivent une hiérarchie d&#8217;erreurs</h2>
<p>Ceci permet non seulement de choisir de gérer plusieurs erreurs d&#8217;un coup, ou alors séparément, mais également de documenter par le type quel est le problème. Une fois qu&#8217;on connait les exceptions les plus courantes en Python (ValueError, OSError, KeyError, TypeError, etc.), on identifie vite l&#8217;idée générale d&#8217;un message d&#8217;erreur ou d&#8217;un code attrapant une erreur.</p>
<p>Comme peut créer ses propres types d&#8217;exceptions, on peut permettre le ciblage des erreurs d&#8217;une lib en particulier, ou d&#8217;un sous ensemble d&#8217;une lib ou d&#8217;une opération. Et c&#8217;est aussi une forme de documentation par le code.</p>
<p>Tout cela autorise une fine granularité sur ce qu&#8217;on veut gérer ou pas : tout d&#8217;un coup, au cas par cas, seulement sur une partie du code, etc.</p>
<h2>Les exceptions bubblent</h2>
<p>Sous ce terme barbare se cache le fait que les exceptions se déclenchent localement dans un bloc de code, et l&#8217;interrompe, mais ne font pas planter le programme tout de suite. À la place, l&#8217;exception monte d&#8217;un niveau dans la file d&#8217;appels, et casse tout, puis remonte d&#8217;un cran, et casse tout, et ainsi de suite, jusqu&#8217;en haut.</p>
<p>Ce mécanisme permet de choisir exactement où on veut arrêter l&#8217;exception, et ce que l&#8217;on souhaite qu&#8217;elle puisse interrompre. Cela laisse le choix de gérer des erreurs de manière macroscopique ou microscopique.</p>
<p>Par exemple, si j&#8217;utilise un <code>try</code> en dehors d&#8217;une boucle :</p>

<div class="wp_syntax"><table><tr><td class="code"><pre class="python" style="font-family:monospace;"><span style="color: #ff7700;font-weight:bold;">print</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'start'</span><span style="color: black;">&#41;</span>
<span style="color: #ff7700;font-weight:bold;">try</span>:
    <span style="color: #ff7700;font-weight:bold;">for</span> x <span style="color: #ff7700;font-weight:bold;">in</span> <span style="color: #008000;">range</span><span style="color: black;">&#40;</span><span style="color: #ff4500;">0</span><span style="color: #66cc66;">,</span> <span style="color: #ff4500;">10</span><span style="color: black;">&#41;</span>:
        <span style="color: #ff7700;font-weight:bold;">print</span><span style="color: black;">&#40;</span><span style="color: #ff4500;">1</span> / <span style="color: black;">&#40;</span>x - <span style="color: #ff4500;">2</span><span style="color: black;">&#41;</span><span style="color: black;">&#41;</span>
<span style="color: #ff7700;font-weight:bold;">except</span> <span style="color: #008000;">ZeroDivisionError</span>:
    <span style="color: #ff7700;font-weight:bold;">pass</span>
&nbsp;
<span style="color: #ff7700;font-weight:bold;">print</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'fin'</span><span style="color: black;">&#41;</span>
<span style="color: #808080; font-style: italic;">## Affiche :</span>
<span style="color: #808080; font-style: italic;">## start</span>
<span style="color: #808080; font-style: italic;">## -0.5</span>
<span style="color: #808080; font-style: italic;">## -1.0</span>
<span style="color: #808080; font-style: italic;">## fin</span></pre></td></tr></table></div>

<p>Et un dans une boucle :</p>

<div class="wp_syntax"><table><tr><td class="code"><pre class="python" style="font-family:monospace;"><span style="color: #ff7700;font-weight:bold;">print</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'start'</span><span style="color: black;">&#41;</span>
<span style="color: #ff7700;font-weight:bold;">for</span> x <span style="color: #ff7700;font-weight:bold;">in</span> <span style="color: #008000;">range</span><span style="color: black;">&#40;</span><span style="color: #ff4500;">0</span><span style="color: #66cc66;">,</span> <span style="color: #ff4500;">10</span><span style="color: black;">&#41;</span>:
    <span style="color: #ff7700;font-weight:bold;">try</span>:
        <span style="color: #ff7700;font-weight:bold;">print</span><span style="color: black;">&#40;</span><span style="color: #ff4500;">1</span> / x<span style="color: black;">&#41;</span>
    <span style="color: #ff7700;font-weight:bold;">except</span> <span style="color: #008000;">ZeroDivisionError</span>:
        <span style="color: #ff7700;font-weight:bold;">print</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'ERROR !'</span><span style="color: black;">&#41;</span>
    <span style="color: #ff7700;font-weight:bold;">else</span>:
        <span style="color: #ff7700;font-weight:bold;">print</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">&quot;Pas d'erreur :)&quot;</span><span style="color: black;">&#41;</span>
    <span style="color: #ff7700;font-weight:bold;">finally</span>:
        <span style="color: #ff7700;font-weight:bold;">print</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'TOUJOURS'</span><span style="color: black;">&#41;</span>
<span style="color: #ff7700;font-weight:bold;">print</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'fin'</span><span style="color: black;">&#41;</span>
<span style="color: #808080; font-style: italic;">## Affiche:</span>
<span style="color: #808080; font-style: italic;">## start</span>
<span style="color: #808080; font-style: italic;">## ERROR !</span>
<span style="color: #808080; font-style: italic;">## TOUJOURS</span>
<span style="color: #808080; font-style: italic;">## 1.0</span>
<span style="color: #808080; font-style: italic;">## Pas d'erreur :)</span>
<span style="color: #808080; font-style: italic;">## TOUJOURS</span>
<span style="color: #808080; font-style: italic;">## 0.5</span>
<span style="color: #808080; font-style: italic;">## Pas d'erreur :)</span>
<span style="color: #808080; font-style: italic;">## TOUJOURS</span>
<span style="color: #808080; font-style: italic;">## 0.3333333333333333</span>
<span style="color: #808080; font-style: italic;">## Pas d'erreur :)</span>
<span style="color: #808080; font-style: italic;">## TOUJOURS</span>
<span style="color: #808080; font-style: italic;">## 0.25</span>
<span style="color: #808080; font-style: italic;">## Pas d'erreur :)</span>
<span style="color: #808080; font-style: italic;">## TOUJOURS</span>
<span style="color: #808080; font-style: italic;">## 0.2</span>
<span style="color: #808080; font-style: italic;">## Pas d'erreur :)</span>
<span style="color: #808080; font-style: italic;">## TOUJOURS</span>
<span style="color: #808080; font-style: italic;">## 0.16666666666666666</span>
<span style="color: #808080; font-style: italic;">## Pas d'erreur :)</span>
<span style="color: #808080; font-style: italic;">## TOUJOURS</span>
<span style="color: #808080; font-style: italic;">## 0.14285714285714285</span>
<span style="color: #808080; font-style: italic;">## Pas d'erreur :)</span>
<span style="color: #808080; font-style: italic;">## TOUJOURS</span>
<span style="color: #808080; font-style: italic;">## 0.125</span>
<span style="color: #808080; font-style: italic;">## Pas d'erreur :)</span>
<span style="color: #808080; font-style: italic;">## TOUJOURS</span>
<span style="color: #808080; font-style: italic;">## 0.1111111111111111</span>
<span style="color: #808080; font-style: italic;">## Pas d'erreur :)</span>
<span style="color: #808080; font-style: italic;">## TOUJOURS</span>
<span style="color: #808080; font-style: italic;">## fin</span></pre></td></tr></table></div>

<p>J&#8217;obtiens un résultat radicalement différent. On peut choisir facilement l&#8217;étendue de la progragation de l&#8217;erreur.</p>
<h2>Les exceptions ont des données attachées</h2>
<p>Les exceptions sont des objets riches, qui viennent avec un message d&#8217;erreur, un contexte qui permet de générer une stack trace, et parfois des attributs en plus comme le code d&#8217;erreur fourni par l&#8217;OS.</p>
<p>C&#8217;est un point d&#8217;entrée capable de concentrer en un seul endroit tout ce dont on a besoin pour le debug.</p>
<h2>Pas cher mon fils</h2>
<p>En Python, les exceptions sont particulièrement peu couteuses à utiliser. En fait, Python les utilise pour le contrôle de flux (<code>StopIteration</code>, <code>GeneratorExit</code>, etc) donc elles sont au coeur du fonctionnement du langage, et pas juste pour pour les erreurs.</p>
<p>Faire un <code>try</code>/<code>except</code> n&#8217;a pas le coup qu&#8217;on a en Java ni en terme de performance du code, ni en terme de verbosité car il n&#8217;y a pas de <code>throw</code> à déclarer.</p>
<p>Le truc le plus ennuyeux, c&#8217;est bien entendu de trouver le nom de l&#8217;exception qu&#8217;on veut gérer et comment l&#8217;importer. Afin d&#8217;éviter cette chose affreuse:</p>

<div class="wp_syntax"><table><tr><td class="code"><pre class="python" style="font-family:monospace;">    <span style="color: #ff7700;font-weight:bold;">try</span>:
        meh<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
    <span style="color: #ff7700;font-weight:bold;">except</span>: <span style="color: #808080; font-style: italic;"># YOLO</span>
        <span style="color: #ff7700;font-weight:bold;">print</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">&quot;Il s'est passé un truc, mais je sais pas quoi&quot;</span><span style="color: black;">&#41;</span></pre></td></tr></table></div>

<p>Il y a à peine quelques heures j&#8217;étais avec un client qui avait des utilisateurs se plaignant que le système ne marchait pas sans vraiment pouvoir diagnostiquer pourquoi. </p>
<p>Vous voyez, pas besoin d&#8217;inventer, j&#8217;ai les exemples qui me tombent tout cru dans le bec.</p>
<p>Voici ce qu&#8217;il avait, <code>en prod</code> :</p>

<div class="wp_syntax"><table><tr><td class="code"><pre class="python" style="font-family:monospace;"><span style="color: #808080; font-style: italic;"># je vous pseudo code, mais c'est l'idée</span>
<span style="color: #ff7700;font-weight:bold;">def</span> get_dbs<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>:
    <span style="color: #ff7700;font-weight:bold;">try</span>:
        <span style="color: #808080; font-style: italic;"># connection, listing, filtrage, casting</span>
        <span style="color: #808080; font-style: italic;"># nahhh, que pourrait-il arriver de grave ?</span>
        con <span style="color: #66cc66;">=</span> Connect<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
        dbs <span style="color: #66cc66;">=</span> con.<span style="color: black;">get_databases</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
        dbs <span style="color: #66cc66;">=</span> fitler_system_db<span style="color: black;">&#40;</span>dbs<span style="color: black;">&#41;</span>
        <span style="color: #ff7700;font-weight:bold;">return</span> <span style="color: #008000;">tuple</span><span style="color: black;">&#40;</span>dbs<span style="color: black;">&#41;</span>
    <span style="color: #ff7700;font-weight:bold;">except</span>: <span style="color: #808080; font-style: italic;"># double combo: catch all ET silence</span>
        <span style="color: #ff7700;font-weight:bold;">return</span> <span style="color: black;">&#40;</span><span style="color: black;">&#41;</span></pre></td></tr></table></div>

<p>Oui, j&#8217;ai bien dis en prod. La base de données gère des listings de médecins. Ouch.</p>
<p>Le code utilisant cette fonction faisait (avant que j&#8217;arrive avec mon fouet de zorro et corrige tout ce bordel):</p>

<div class="wp_syntax"><table><tr><td class="code"><pre class="python" style="font-family:monospace;">dbs <span style="color: #66cc66;">=</span> get_dbs<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
<span style="color: #ff7700;font-weight:bold;">if</span> <span style="color: #ff7700;font-weight:bold;">not</span> dbs:
    display_error<span style="color: black;">&#40;</span><span style="color: #483d8b;">&quot;Mongo ne tourne pas, ou la connection a échoué, ou aucune table n'est créé&quot;</span><span style="color: black;">&#41;</span></pre></td></tr></table></div>

<p>Vous imaginez comme l&#8217;utilisateur final pouvait facilement décrire son problème&#8230; Ouai alors soit t&#8217;as pas un truc, soit le truc marche pas, soit tu te sers par du truc. Ouai ça couvre à peut prêt l&#8217;ensemble des erreurs possibles sur tout système, comme ça on peut pas avoir fondamentalement tort. </p>


feed_id: http://sametmax.com/?p=17502
title: Le don du mois : Tor 5
uri: Le-don-du-mois-Tor-5
url: http://sametmax.com/le-don-du-mois-tor/
content:
<p>Comme chaque mois, je fais un petit don à une entité et je fais sa pub. Premier don de 2016 : Tor, le logiciel le logiciel d&#8217;anonymisation de connexion Internet.</p>
<p>Je l&#8217;utilise pourtant peu. De temps en temps, je reinstalle tout le bordel, et je regarde où ça en est. Parfois, je fais un bot qui a besoin de plus d&#8217;IP et je le plug temporairement sur Tor. Mais ce n&#8217;est pas un usage régulier.</p>
<p>Pourtant, philosophiquement, je trouve que sa mission est de la plus haute importance : nous accumulons de l&#8217;expérience sur la mise en place d&#8217;un Internet plus démocratique, plus ouvert, moins centralisé, plus humain.</p>
<p>Or Internet est dévenu le cerveau connectant l&#8217;humanité entière, et il faudra bien cela, et beaucoup d&#8217;autres choses, pour s&#8217;assurer que les pensées de tous s&#8217;y reflètent, et pas juste celles d&#8217;une minorité.</p>
<p>Pour cela, je soutiens le développement de Tor. Je pense que ses jours sont pourtant comptés, mais sa simple existence a été primordiale à la santé d&#8217;Internet.</p>
<p>50 euros, <a href="https://www.torproject.org/donate/donate-options.html.en">en bitcoin</a>, pour ce don.</p>


feed_id: http://sametmax.com/?p=17508
title: Desactiver la validation des mots de passe en mode DEBUG sous Django
uri: Desactiver-la-validation-des-mots-de-passe-en-mode-DEBUG-sous-Django
url: http://sametmax.com/desactiver-la-validation-des-mots-de-passe-en-mode-debug-sous-django/
content:
<p>Django 1.9 rajoute la validation de &#8220;sécurité&#8221; des mots de passe comme corde à son arc. </p>
<p>Personnellement je trouve ça naze. </p>
<p>Je suis pour permettre aux utilisateurs de mettre le password qu&#8217;ils veulent, même tout pourri. De toute façon les gens qui ne font pas attention à leur sécurité ne vont jamais se souvenir du mot de passe compliqué et faire un « j&#8217;ai oublié mon mot de passe » à chaque fois. </p>
<p>Pire, la complexité ajoutée va faire fuir les nouveaux utilisateurs qui ne vont pas vouloir se faire chier à remplir le formulaire. Ou alors ils vont faire « login with facebook ».</p>
<p>Mais le plus naze dans tout ça c&#8217;est que la validation est tout ou rien : on peut pas mettre de la validation juste pour les comptes admin par exemple. Ce qui aurait du sens.</p>
<p>Alors, oui à la mise en oeuvre d&#8217;un indicateur de force de mot de passe, mais pas un refus catégorique de s&#8217;inscrire comme le système actuel le fait.</p>
<p>Dans tous les cas, vous voudrez au moins le désactiver en mode <code>DEBUG</code> car devoir taper un mot de passe compliqué en dev sur votre machine en local pour accéder à une base de données sqlite de test, c&#8217;est relou.</p>
<p>Car oui, <code>./manage.py createsuperuser</code> refuse votre password si il n&#8217;est pas &#8220;sécurisé&#8221; par défaut. En prod, ça a du sens, mais quand je veux tester une merde sur mon laptop, &#8220;admin/admin&#8221; est généralement ce je veux.</p>
<p>Donc:</p>

<div class="wp_syntax"><table><tr><td class="code"><pre class="python" style="font-family:monospace;">AUTH_PASSWORD_VALIDATORS <span style="color: #66cc66;">=</span> <span style="color: black;">&#91;</span>
    <span style="color: black;">&#123;</span>
        <span style="color: #483d8b;">'NAME'</span>: <span style="color: #483d8b;">'django.contrib.auth.password_validation.UserAttributeSimilarityValidator'</span><span style="color: #66cc66;">,</span>
    <span style="color: black;">&#125;</span><span style="color: #66cc66;">,</span>
    <span style="color: black;">&#123;</span>
        <span style="color: #483d8b;">'NAME'</span>: <span style="color: #483d8b;">'django.contrib.auth.password_validation.MinimumLengthValidator'</span><span style="color: #66cc66;">,</span>
        <span style="color: #483d8b;">'OPTIONS'</span>: <span style="color: black;">&#123;</span>
            <span style="color: #483d8b;">'min_length'</span>: <span style="color: #ff4500;">9</span><span style="color: #66cc66;">,</span>
        <span style="color: black;">&#125;</span>
    <span style="color: black;">&#125;</span><span style="color: #66cc66;">,</span>
    <span style="color: black;">&#123;</span>
        <span style="color: #483d8b;">'NAME'</span>: <span style="color: #483d8b;">'django.contrib.auth.password_validation.CommonPasswordValidator'</span><span style="color: #66cc66;">,</span>
    <span style="color: black;">&#125;</span><span style="color: #66cc66;">,</span>
    <span style="color: black;">&#123;</span>
        <span style="color: #483d8b;">'NAME'</span>: <span style="color: #483d8b;">'django.contrib.auth.password_validation.NumericPasswordValidator'</span><span style="color: #66cc66;">,</span>
    <span style="color: black;">&#125;</span><span style="color: #66cc66;">,</span>
<span style="color: black;">&#93;</span>
&nbsp;
<span style="color: #ff7700;font-weight:bold;">if</span> DEBUG:
    AUTH_PASSWORD_VALIDATORS <span style="color: #66cc66;">=</span> <span style="color: black;">&#91;</span><span style="color: black;">&#93;</span></pre></td></tr></table></div>

<p>Enfin, franchement, un validateur de mot de passe basé sur la présence de chiffres. Sérieux ? On a des validateurs basés sur une enthrophie générale depuis longtemps.</p>


feed_id: http://sametmax.com/?p=17638
title: Le bulletin sécu : https pour 0bin et leak de mots de passe 3 &#160; Recently updated !
uri: Le-bulletin-sécu-https-pour-0bin-et-leak-de-mots-de-passe-3-Rece
url: http://sametmax.com/le-bulletin-secu-https-pour-0bin-et-leak-de-mot-de-passe/
content:
<p>Quand letsencrypt est passé live, on m&#8217;a gentiment signalé qu&#8217;on avait plus d&#8217;excuses pour faire tourner 0bin en clair.</p>
<p>Bon, soit.</p>
<p>J&#8217;ai mis en place ça hier à titre de test : <a href="https://0bin.net/">https://0bin.net/</a>.</p>
<p>Pour le moment je vais pas faire de redirection car j&#8217;attends de voir si le truc tient. </p>
<p>La bonne nouvelle, c&#8217;est que comme l&#8217;installation est juste nginx + zerobin, il n&#8217;y a pas eut grand chose à faire : lancer un script, le laisser nous identifier et installer les dependances pour qu&#8217;il génère le certificat et faire pointer le dit certificat par nginx.</p>
<p>La moins bonne nouvelle c&#8217;est que notre archi pour <a href="http://sametmax.com">sametmax.com</a> et <a href="http://indexerror.net">indexerror.net</a> est basé sur client => varnish => nginx => worker => site.</p>
<p>Il faut donc mettre le certif au niveau de varnish.</p>
<p>Mais comme vous vous en doutez bien, il fallait que ça couille : support de https est un truc récent sur varnish et notre version ne le supporte pas. Donc il reste à :</p>
<ul>
<li>Up varnish. Compiler à la main, chercher un mec qui l’a fait, up le serveur ou autre.</li>
<li>Ou passer varnish derrière nginx et réécrire tout le VLC.</li>
<li>Ou mettre uniquement HTTPS pour les formulaires de connexions. C’est mieux que rien, mais ça laisse le cookie d’identification en clair et ça va demander de faire des règles milimétriaques.</li>
<li>Mettre <a href="https://github.com/varnish/hitch">un autre truc</a> en face de varnish juste pour le port 443. </li>
</ul>
<p>Oh, du boulot en plus, je ne m’y attendais tellement pas&#8230;</p>
<p>Merci à tous ceux qui ont proposé et fourni de l’aide néanmoins. C’est cool d’avoir du soutien parmi ses lecteurs.</p>
<p>Pendant ce temps, et ça n’a rien à voir, un lecteur s’est amusé à brute forcer des hashs de mots de passe qu’on avait leakés connement et a trouvé celui de max au bout de quelques jours. Il nous a gentiment envoyé le résultat de ses recherches par mail. Ce n’est pas ironique, hein. J’apprécie que quand quelqu’un trouve que tu es à poil il te le fasse savoir avec précision et honnêteté.</p>
<p>Du coup va falloir aussi changer tous les mots de passe du blog et de indexerror. </p>
<p>Comme ce n’est pas une question de sécurité nationale, je ne suis pas en mode bersek à vouloir faire ça dans la minute, mais sachez-le : changez vos mots de passe à l’occasion. Personne ne va violer votre sœur demain si vous ne le faites pas, mais mieux vaut prévenir que guérir.</p>
<p>Oh, du boulot en plus, je ne m’y attendais tellement pas&#8230;</p>


feed_id: http://sametmax.com/?p=17772
uri: La-communauté-JS-est-actuellement-une-machine-à-créer-de-la-dette-t
url: http://sametmax.com/la-communaute-js-est-actuellement-une-machine-a-creer-de-la-dette-technique/
title:
La communauté JS est actuellement une machine à créer de la dette technique 38 &#160; Recently updated !

content:
<p>Vous savez, quand on ne brule pas un Troll, ses blessures se soignent rapidement, et il attaque à nouveau.</p>
<p>Et vous savez également comme <a href="http://sametmax.com/un-gros-troll-de-plus-sur-javacscript/">j’aime troller JS</a>. </p>
<p>De plus, il y a quelque temps, je vous affirmais que <a href="http://sametmax.com/non-nodejs-nest-pas-mature/" title="Non, nodejs n’est pas mature">NodeJS n’était pas mature</a>.</p>
<p>Est-ce que l’écosystème JS a muri depuis ?</p>
<p>Et bien maintenant je crois qu’on a passé l’enfance, et qu’on est dans la phase de l’adolescence. Ca cri, ça bouge, ça a plein d’énergie, et ça mérite des baffes.</p>
<p>Je sais, je sais, je vais encore avoir une horde de fans boys qui aiment le typage mou et les champs de moustaches venir me dire que je devrais arrêter d’écrire, de coder et m’enfoncer la version imprimée de Wikipédia dans l’anus.</p>
<div id="attachment_17777" style="width: 714px" class="wp-caption aligncenter"><a href="media/2aa06994.h0dDER3.jpg" data-original-source="http://sametmax.com/wp-content/uploads/2016/01/h0dDER3.jpg" class="grouped_elements" rel="tc-fancybox-group17772"><img src="media/2aa06994.h0dDER3.jpg" data-original-source="http://sametmax.com/wp-content/uploads/2016/01/h0dDER3.jpg" alt="According to this, you&#039;re a hérétic" width="704" height="899" class="size-full wp-image-17777" /></a><p class="wp-caption-text">Comment on appelle l’adulation de quelque chose par une communauté en dépit de toute rationalité déjà ?</p></div>
<p>Mais dans l’histoire de JS, y a pas eu un moment qui ne méritait pas un bon article de ce genre. A croire que c’est volontaire.</p>
<p>En l’occurrence, j’ai eu envie d’écrire cet article à la suite de plusieurs évènements successifs:</p>
<ul>
<li>Je me suis mis à React, et ai du remettre à jour, encore une fois, tout mon env de dev JS.</li>
<li>J’ai discuté avec des clients qui ont voulu mettre ça en place et qui ont fait marche arrière, car impossible de choisir une stack.</li>
<li>J’ai fait des formations AngularJS 1 pour des grosses boites qui vont faire tout avec malgré le fait que le 2 arrivent et <a href="http://www.i-programmer.info/news/167-javascript/7932-angularjs-20-is-radically-different-.html">casse tout</a>.</li>
<li>Mozilla annonce <a href="https://news.ycombinator.com/item?id=10884893">la cloture de persona.org</a> (et merde&#8230;) et publie son code NodeJS en open source. Et c’est une très vieille version (0.4.5) ce qui fait qu’on lit des comments du genre : <q><em>it’s F/OSS, but I&#8217;d strongly suggest learning from Persona’s design rather than directly re-hosting the code</em></q></li>
<li>Je suis tombé sur <a href="https://medium.com/@wob/the-sad-state-of-web-development-1603a861d29f#.lwhulrh0i">un article déprimant sur l’état du front end</a> qui m’a semblé sortir directement de ma tête.</li>
<li>J’utilise <a href="https://noscript.net/">NoSript</a> depuis quelque temps et je constate un nombre incroyable de pages qui devraient être statiques ou quasi-statiques et qui sont inutilisables avec le JS désactivés.</li>
<li><a href="http://idlewords.com/talks/website_obesity.htm">Les pages Web deviennent de plus en plus obèses.</a></li>
</ul>
<div id="attachment_17778" style="width: 510px" class="wp-caption aligncenter"><a href="media/9e224a81.EBexR3B.gif" data-original-source="http://sametmax.com/wp-content/uploads/2016/01/EBexR3B.gif" class="grouped_elements" rel="tc-fancybox-group17772"><img src="media/9e224a81.EBexR3B.gif" data-original-source="http://sametmax.com/wp-content/uploads/2016/01/EBexR3B.gif" alt="Homme se servant du whisky" width="500" height="281" class="size-full wp-image-17778" /></a><p class="wp-caption-text">Laissez-moi reprendre un peu de force pour la suite</p></div>
<p>Donc, reprenons.</p>
<p>NodeJS est toujours splitté entre <a href="https://github.com/nodejs/node-convergence-archive">3 versions</a> : Node, Io.JS et convergence.</p>
<p>Il y a plus de frameworks front end que jamais : Angular, React, Amber, Backbone, Polymer, ExtJS, Aurelia, Durandal, Knockout, Mithril, MarionetteJS, Vue.js, Meteor, etc. Et je ne liste que les plus connus, car <a href="http://todomvc.com/">la liste est interminable</a>.</p>
<p>Ils ont tous des versions différentes, des addons (qui ont des versions et des dépendances), des docs et tutos répartis un peu partout..</p>
<p>Par dessus ça, on rajoute les langages qui compilent vers du JS qui eux aussi se multiplient comme les gremlins mouillés: Coffeescript, Dart, GWT, Closure, Haxe, Elm, TypeScript, Brython, Skulpt, PyJS, etc. Vous voulez, un listing complet ? <a href="https://github.com/jashkenas/coffeescript/wiki/List-of-languages-that-compile-to-JS">Attention ça pique les yeux</a>.</p>
<p>En clair, la seule chose que les gens aiment autant que de pallier l’absence de library standard de JS est de pallier la syntaxe de JS.</p>
<div id="attachment_17781" style="width: 610px" class="wp-caption aligncenter"><a href="media/464366dc.CXB8u6OWMAAJJlI.png" data-original-source="http://sametmax.com/wp-content/uploads/2016/01/CXB8u6OWMAAJJlI.png" class="grouped_elements" rel="tc-fancybox-group17772"><img src="media/464366dc.CXB8u6OWMAAJJlI.png" data-original-source="http://sametmax.com/wp-content/uploads/2016/01/CXB8u6OWMAAJJlI.png" alt="The 5 steps of software development" width="600" height="582" class="size-full wp-image-17781" /></a><p class="wp-caption-text">A cela s’ajoute le problème que visiblement les devs JS et le reste du monde n’en sont pas à la même phase. Ils en sont toujours au déni.</p></div>
<p>Mais on ne peut pas blâmer uniquement le langage. La communauté JS a pris la modularité tellement à l’extrême qu’aucune solution standard ne s’est démarquée pour rien : outils de build, libs de tests, solutions de routing, toolings fonctionnels, et même packages managers&#8230;</p>
<p>Même à l’intérieur d’un écosystème, c’est les poupées russes. Par exemple React peut être accompagné d’un pattern que Facebook appelle Flux. C’est du MVC monodirectionnel. Les modèles sont immutables. Le contrôleur est à base d’évènements. Les vues sont gérées par des widgets React. Rien de fou, mais ça faisait pas assez innovant alors ils ont inventé un nouveau nom.</p>
<p>Bref, ils ont leur propre implémentation, mais même la référence n’a pas gagné la guerre. Non, en JS, ça a déclenché immédiatement un concours de celui qui pisse le plus loin et vous pouvez (devez ?) choisir entre : Flux, redux, alt, reflux, flummox, fluxible, fluxxor, marty.js, fynx, MacFly, DeLorean.js, fluxify, fluxury, exim, fluxtore, Redx, fluxx. <a href="https://github.com/kriasoft/react-starter-kit/issues/22">Les noms ne sont pas une tentative d’humour de ma part</a>.</p>
<p>Ce qui donne les comments du genre :</p>
<blockquote><p>What a wonderful example of the paradox of choice!</p>
<p>No wonder that it’s easier to get started with Meteor + React, than with Flux + React.</p></blockquote>
<p>Pour rappel, Meteor est le truc le plus expérimental qui existe en termes de stack techno à l’heure actuelle.</p>
<p>Évidemment tous ces projets ont une documentation succincte, une durée de vie aléatoire, et on peut déjà lire des trucs comme :</p>
<blockquote><p>Flummox 4.0 will likely be the last major release. Use Redux instead.</p></blockquote>
<div id="attachment_17782" style="width: 455px" class="wp-caption aligncenter"><a href="media/6da76f2a.vuF9Nuq.gif" data-original-source="http://sametmax.com/wp-content/uploads/2016/01/vuF9Nuq.gif" class="grouped_elements" rel="tc-fancybox-group17772"><img src="media/6da76f2a.vuF9Nuq.gif" data-original-source="http://sametmax.com/wp-content/uploads/2016/01/vuF9Nuq.gif" alt="The future starts now" width="445" height="196" class="size-full wp-image-17782" /></a><p class="wp-caption-text">Ton projet</p></div>
<p>Et ils ne sont pas compatibles entre eux. Et en fait ils ne font pas exactement la même chose :</p>
<blockquote><p>how does redux &#8220;replace&#8221; flummox, exactly? Seems like they have two very different approaches</p></blockquote>
<p>Je vous mets les comments car je sais que vous n’avez pas le temps d’aller lire, et encore moins d’étudier chacun de ses projets pour décider duquel utiliser, déjà qu’il faut apprendre React&#8230; A vous ne saviez pas ce qu’était React ? Mais vous êtes à la bourre dites donc !</p>
<p>Rassurez-vous, choisir ce genre de techno a seulement un impact sur tout le code front de votre projet. C’est tout.</p>
<p>Mais les outils s’améliorent, et JS est de plus en plus facile à coder. Pas vrai ? Pas vrai ?</p>
<div id="attachment_17780" style="width: 350px" class="wp-caption aligncenter"><a href="media/25c13607.sEXYtO1.jpg" data-original-source="http://sametmax.com/wp-content/uploads/2016/01/sEXYtO1.jpg" class="grouped_elements" rel="tc-fancybox-group17772"><img src="media/25c13607.sEXYtO1.jpg" data-original-source="http://sametmax.com/wp-content/uploads/2016/01/sEXYtO1.jpg" alt="On a créé un robot qui adore jour du piano, et on lui a mis des moufles" width="340" height="355" class="size-full wp-image-17780" /></a><p class="wp-caption-text">L’avancée technique ne veut pas forcément dire que la vie est plus belle</p></div>
<p>Nope.</p>
<p>React fait ramer misérablement la console de debug de Firefox.</p>
<p>Les préprocesseurs ont tout envahi, et si vous choisissez 4 libs, l’un sera en ES6, l’autre en typescript et la troisième en Dart. Et il faut un setup de connard pour debug du code de préprocesseur confortablement : détecteur de changement, builder, injecteur de code, source mapper&#8230;</p>
<p>Les frameworks comme Angular ou React ont une gestion d’erreur bien à eux, qui affichent des messages chelous.</p>
<div id="attachment_17779" style="width: 510px" class="wp-caption aligncenter"><a href="media/ef245190.hZgVIXH.gif" data-original-source="http://sametmax.com/wp-content/uploads/2016/01/hZgVIXH.gif" class="grouped_elements" rel="tc-fancybox-group17772"><img src="media/ef245190.hZgVIXH.gif" data-original-source="http://sametmax.com/wp-content/uploads/2016/01/hZgVIXH.gif" alt="Homme transpercé au torax par un tuyau" width="500" height="269" class="size-full wp-image-17779" /></a><p class="wp-caption-text">Pourquoi ça marche pas ? Ben&#8230;</p></div>
<p>L’installation est devenue un enfer. Entre les dépendances dépréciées, les libs incompatibles, les différents outils de build, et les options de config et les plugins, c’est une merde incommensurable. Plusieurs standards d’installeurs (et outils joints) se tirent la bourre : <a href="https://addyosmani.com/writing-modular-js/">AMD, CommonJS et Harmony</a>. Vous vous souvenez du temps ou on copiait juste jQuery dans le répertoire static ?</p>
<p>Attendez, avec Webassembly on pourra même plus faire &#8220;read source&#8221;.</p>
<p>Donc JS&#8230;</p>
<p>On peut faire plus de choses qu’avant. De manière plus propre (enfin propre, on parle de JS là&#8230;).</p>
<p>Mais l’écosystème, c’est le bordel général, l’anarchie totale, et ça continue d’avancer, en laissant tout le reste derrière.</p>
<p>Les projets JS s’accumulent, de freelances en SS2I qui se pointent, codent ou (surtout) recodent, et repartent en laissant un projet qu’il sera imbuvable à reprendre, déprécié avant même d’être terminé, non documenté, non testé. </p>
<p>Un projet jetable.</p>
<div id="attachment_17776" style="width: 510px" class="wp-caption aligncenter"><a href="media/b0bfcdc1.gCXypHD.gif" data-original-source="http://sametmax.com/wp-content/uploads/2016/01/gCXypHD.gif" class="grouped_elements" rel="tc-fancybox-group17772"><img src="media/b0bfcdc1.gCXypHD.gif" data-original-source="http://sametmax.com/wp-content/uploads/2016/01/gCXypHD.gif" alt="Judge dread regarde bruler " width="500" height="211" class="size-full wp-image-17776" /></a><p class="wp-caption-text">Ca vallait le coup de quitter PHP</p></div>


feed_id: http://sametmax.com/?p=17796
title: En Python, les threads et l&#8217;asyncio s&#8217;utilisent ensemble 2 &#160; Recently updated !
uri: En-Python-les-threads-et-l-asyncio-s-utilisent-ensemble-2-Rece
url: http://sametmax.com/en-python-les-threads-et-lasyncio-sutilisent-ensemble/
content:
<p>En Python, les threads et l’asyncio s’utilisent ensemble</p>
<p>Je vois beaucoup dans les tutos ici et là des gens qui opposent asyncio avec l’ancienne manière de faire de l’IO non bloquante : les threads.</p>
<p>C’est une erreur : les deux méthodes ne sont pas opposées, elles sont complémentaires.</p>
<h2>Faire des requêtes pendant que ça bloque</h2>
<p>En effet, asyncio ne peut être non bloquant que sur le réseau : ça ne gère pas l’IO sur les fichiers. Par ailleurs, toute opération CPU bloque également la boucle d’évènement.</p>
<p>C’est très bien de ne pas bloquer sur le réseau, mais encore faut-il pouvoir faire la requête réseau.</p>
<p>Si votre programme est bloqué à attendre une compression zip qui dure 3 secondes, pendant ce temps, les opérations réseaux déjà lancées tournent bien. </p>
<p>MAIS IL N’EN LANCERA PAS DE NOUVELLES.</p>
<p>Pendant 3 secondes, aucune nouvelle requête ne sera faite puisque le programme ne fait qu’une chose : ziper. Ca marche pour d’autres choses hein : copie de fichier, gros calcul matheux, traverser une grande liste, etc.</p>
<p>Donc si vous avez fini toutes vos requêtes à la seconde 1, pendant 2 secondes, votre programme n’est pas utilisé à son plein potentiel.</p>
<p>Les threads ne permettent pas d’avancer plus vite, mais ils permettent de faire 2 travaux en parallèle. Par exemple, de lancer pendant ces 2 secondes des requêtes supplémentaires sur le réseau.</p>
<h2>Asyncio a de l’overhead</h2>
<p>On a vendu asyncio comme plus léger que les threads. <a href="http://techspot.zzzeek.org/2015/02/15/asynchronous-python-and-databases/">Ce n’est pas tout à fait exact</a>. asyncio a les avantages suivants :</p>
<ul>
<li>Pas besoin de préallouer des threads. Ca scale toujours pile poil, et ça bouffe moins de mémoire.</li>
<li>asyncio est plus performant quand la charge de travaille demande de très nombreux threads.</li>
<li>un algo asyncio est plus facile à conceptualiser.</li>
<li>il est plus difficile d’introduire des erreurs de concurrence.</li>
<li>asyncio est plus facile à débugger.</li>
</ul>
<p>Mais si on a quelques threads, le changement de contexte entre les threads est moins important que ce que coûte asyncio à faire tourner.</p>
<p>Pour certains travaux où le coût d’une opération réseau est faible, mais que cumulativement toutes les opérations ralentissent votre programme, un thread sera plus performant. </p>
<p>Par exemple, beaucoup d’opérations sur les bases de données tombent dans cette catégorie, à moins d’avoir des très longues requêtes.</p>
<p>Dans ce cas, avoir un thread dédié aux opérations de la base de données peut être une bonne décision.</p>
<h2>Quoi faire ?</h2>
<p>Si vous avez des opérations sur des fichiers ou de grosses opérations CPU, les faire travailler dans un thread peut booster votre programme. Ca tombe bien <a href="https://github.com/Tinche/aiofiles/">il y a une lib pour ça</a>.</p>
<p>Si vous avez beaucoup de petites opérations réseau, les grouper, dans un thread à part, peut booster votre programme. </p>
<p>Et asyncio pour le reste, par exemple des requêtes HTTPs, DNS, SMTP, FTP, SSH, etc.</p>
<p>On peut donc copieusement utiliser les deux en parallèle. La bonne nouvelle, c’est que Guido a designé asyncio pour ça:</p>

<div class="wp_syntax"><table><tr><td class="code"><pre class="python" style="font-family:monospace;"><span style="color: #ff7700;font-weight:bold;">import</span> asyncio
<span style="color: #ff7700;font-weight:bold;">from</span> concurrent.<span style="color: black;">futures</span> <span style="color: #ff7700;font-weight:bold;">import</span> ThreadPoolExecutor
&nbsp;
async <span style="color: #ff7700;font-weight:bold;">def</span> main<span style="color: black;">&#40;</span>loop<span style="color: #66cc66;">,</span> executor<span style="color: black;">&#41;</span>:
&nbsp;
    <span style="color: #808080; font-style: italic;"># ça c’est fait avec asyncio</span>
    await aiohttp.<span style="color: black;">get</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'http://bidule.com'</span><span style="color: black;">&#41;</span>
&nbsp;
    <span style="color: #808080; font-style: italic;"># ça c’est fait dans un thread</span>
    await loop.<span style="color: black;">run_in_executor</span><span style="color: black;">&#40;</span><span style="color: #008000;">None</span><span style="color: #66cc66;">,</span> gros_calcul<span style="color: #66cc66;">,</span> params<span style="color: black;">&#41;</span>
&nbsp;
loop <span style="color: #66cc66;">=</span> asyncio.<span style="color: black;">get_event_loop</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
loop.<span style="color: black;">run_until_complete</span><span style="color: black;">&#40;</span>main<span style="color: black;">&#40;</span>loop<span style="color: black;">&#41;</span><span style="color: black;">&#41;</span></pre></td></tr></table></div>

<p>Notez bien :</p>
<ul>
<li>Faites des tests pour voir si vous y gagner vraiment.</li>
<li>Ecrivez un petit wrapper sur tout ça car là c’est verbeux.</li>
<li>Vous pouvez passer un <code>ProcessPoolExecutor</code> ou un <code>ThreadPoolExecutor</code> pour choisir la stratégie de parallélisme à adapter : process, thread, nombres de workers, etc.</li>
</ul>
<p>Bien entendu, chaque fois que vous ajoutez un mécanisme de concurrence, vous ajoutez de la complexité, donc ne le faites que si c&#8217;est nécessaire.</p>
<p>Commencez avec un programme synchrone simple. Si c&#8217;est trop lent, ajouter les threads ou l&#8217;asyncio, si ça ne suffit pas, utilisez les deux.</p>


feed_id: http://sametmax.com/?p=17806
title: Mon prompt bash est tout cassé ! 5 &#160; Recently updated !
uri: Mon-prompt-bash-est-tout-cassé-5-Recently-updated
url: http://sametmax.com/mon-prompt-bash-est-tout-casse/
content:
<p>Ça m’était déjà arrivé plusieurs fois après avoir ajouté mon env virtuel et ma branch git dans mon prompt : soudainement il se met à faire n’importe quoi. Des mixes de caractères, des sauts de ligne qui se font pas, la fusion de la ligne de commande sur elle-même.</p>
<p>Inutilisable.</p>
<p>Aujourd’hui plutôt que de subir le problème, j’ai cherché une solution, <a href="http://superuser.com/questions/367275/linux-coloring-bash-prompt-will-break-carriage-return">et pouf</a> : quand on utilise des couleurs dans son prompt, il faut entourer tout le balisage de <code>[]</code>.</p>
<p>Mais le balisage qui colore le prompt est composé de séquences d’échappements, qui contiennent aussi <code>[]</code>, donc il faut mettre des anti-slash.</p>
<p>Du coup on passe de ça :</p>
<p><code>export PS1="\[\033[01;34m\]\$(basename '$VIRTUAL_ENV')\e[0m $PS1"</code></p>
<p>à :</p>
<p><code>export PS1="\[\033[01;34m\]\$(basename '$VIRTUAL_ENV')[\e[0m] $PS1"<br />
</code></p>
<p>à la solution qui marche :</p>
<p><code>export PS1="\[\033[01;34m\]\$(basename '$VIRTUAL_ENV')\[\e[0m\] $PS1"</code></p>
<p>Évidemment à appliquer à tous les codes d’échappement chelou qu’on a réparti un peu partout.</p>
<p>J’ai un tampon prêt à tirer pour le premier qui me parle de zsh.</p>


feed_id: http://sametmax.com/?p=17820
title: Passer son compte linux en superuser postgres
uri: Passer-son-compte-linux-en-superuser-postgres
url: http://sametmax.com/passer-son-compte-linux-en-superuser-postgres/
content:
<p>Dans la config par défaut, les roles postgres matchent les utilisateurs système sous Linux. Au départ, le seul super utilisateur est &#8220;postgres&#8221;, et à chaque fois qu&#8217;on veut lancer <code>psql</code> pour faire de l&#8217;admin il faut se connecter avec ce user.</p>
<p>Pour se simplifier la vie en dev:</p>

<div class="wp_syntax"><table><tr><td class="code"><pre class="bash" style="font-family:monospace;"><span style="color: #666666; font-style: italic;"># on se log come &quot;postgres&quot;</span>
<span style="color: #c20cb9; font-weight: bold;">sudo</span> <span style="color: #660033;">-i</span> <span style="color: #660033;">-u</span> postgres <span style="color: #666666; font-style: italic;"># sous les debian likes. sinon &quot;su&quot; marche aussi</span>
<span style="color: #666666; font-style: italic;"># on créé un utilisateur</span>
createuser <span style="color: #660033;">--interactive</span> <span style="color: #666666; font-style: italic;"># et on dit &quot;oui&quot; quand il demande de créer un super user</span>
<span style="color: #666666; font-style: italic;"># on créé une db à son nom</span>
createdb ton_username</pre></td></tr></table></div>

<p>Et voilà, on peut utiliser <code>psql</code> depuis son compte.</p>


feed_id: http://sametmax.com/?p=17824
title: Les enums en Python 1
uri: Les-enums-en-Python-1
url: http://sametmax.com/les-enums-en-python/
content:
<p>Je n’ai jamais eu besoin d’un type exactement comme les d’Enum en Python de ma vie. Jamais eu le cas d’utilisation. Alors quand la feature <a href="https://www.python.org/dev/peps/pep-0435/">est arrivé en python 3.4</a>, cela a été un non-évènement pour moi.</p>
<p>Pourquoi faire ?</p>
<p>Je comprends parfaitement que des langages comme le C ou le Java en aient besoin. Leur syntaxe amène à ce genre de chose. Leur typage aussi. Mais Python ? Il y a tellement de manières de représenter les données, de les manipuler, de les passer. </p>
<p>Les enums c’étaient vraiment pour la déco à mes yeux.</p>
<p>Mais la fonctionnalité <a href="a fait débat">a fait débat</a>. Si, si, on peut devenir tout rouge à propos d’une séquence de constantes.</p>
<p>Regardons un peu plus près de quoi il est question.</p>
<h2>L’ancien statuquo</h2>
<p>D’abord, en Python, on définit généralement les constantes ainsi :</p>

<div class="wp_syntax"><table><tr><td class="code"><pre class="python" style="font-family:monospace;">FOO <span style="color: #66cc66;">=</span> <span style="color: #ff4500;">1</span>
BAR <span style="color: #66cc66;">=</span> <span style="color: #ff4500;">2</span> DOH <span style="color: #66cc66;">=</span> <span style="color: #ff4500;">3</span>
OYO <span style="color: #66cc66;">=</span> <span style="color: #ff4500;">4</span></pre></td></tr></table></div>

<p>Ce ne sont pas vraiment des constantes. Il n’y a rien qui ne puisse être réassigné en Python, même si parfois ça demande un peu de bidouillage, mais c’est une convention que les gens respectent.</p>
<p>Comme les modules sont des singleton, on peut facilement faire:</p>

<div class="wp_syntax"><table><tr><td class="code"><pre class="python" style="font-family:monospace;"><span style="color: #ff7700;font-weight:bold;">import</span> module <span style="color: #ff7700;font-weight:bold;">print</span><span style="color: black;">&#40;</span>module.<span style="color: black;">FOO</span><span style="color: black;">&#41;</span></pre></td></tr></table></div>

<p>Parfois on a besoin d’un peu plus. On veut plusieurs groupes de constantes. Alors on peut utiliser une classe.</p>

<div class="wp_syntax"><table><tr><td class="code"><pre class="python" style="font-family:monospace;"><span style="color: #ff7700;font-weight:bold;">class</span> Stuff:     FOO <span style="color: #66cc66;">=</span> <span style="color: #ff4500;">1</span>     BAR <span style="color: #66cc66;">=</span> <span style="color: #ff4500;">2</span>     DOH <span style="color: #66cc66;">=</span> <span style="color: #ff4500;">3</span>
OYO <span style="color: #66cc66;">=</span> <span style="color: #ff4500;">4</span></pre></td></tr></table></div>

<p>Et on peut faire:</p>

<div class="wp_syntax"><table><tr><td class="code"><pre class="python" style="font-family:monospace;"><span style="color: #ff7700;font-weight:bold;">from</span> module <span style="color: #ff7700;font-weight:bold;">import</span> Stuff <span style="color: #ff7700;font-weight:bold;">print</span><span style="color: black;">&#40;</span>Stuff.<span style="color: black;">FOO</span><span style="color: black;">&#41;</span></pre></td></tr></table></div>

<p>Après, il y a toujours les variantes des gens qui veulent itérer dessus.</p>

<div class="wp_syntax"><table><tr><td class="code"><pre class="python" style="font-family:monospace;"><span style="color: #ff7700;font-weight:bold;">from</span> module <span style="color: #ff7700;font-weight:bold;">import</span> Stuff <span style="color: #ff7700;font-weight:bold;">for</span> attr<span style="color: #66cc66;">,</span> value <span style="color: #ff7700;font-weight:bold;">in</span> Stuff.<span style="color: #0000cd;">__dict__</span>.<span style="color: black;">items</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>:     <span style="color: #ff7700;font-weight:bold;">print</span><span style="color: black;">&#40;</span>attr<span style="color: #66cc66;">,</span> value<span style="color: black;">&#41;</span></pre></td></tr></table></div>

<p>Et puis il y a les gens qui veulent itérer dessus de manière ordonnée. On leur conseille généralement d’utiliser <code>namedtuple</code>:</p>

<div class="wp_syntax"><table><tr><td class="code"><pre class="python" style="font-family:monospace;"><span style="color: #ff7700;font-weight:bold;">from</span> <span style="color: #dc143c;">collections</span> <span style="color: #ff7700;font-weight:bold;">import</span> namedtuple
Stuff <span style="color: #66cc66;">=</span> namedtuple<span style="color: black;">&#40;</span><span style="color: #483d8b;">'Stuff'</span><span style="color: #66cc66;">,</span> <span style="color: #483d8b;">'FOO BAR DOH OYO'</span><span style="color: black;">&#41;</span><span style="color: black;">&#40;</span>*<span style="color: #008000;">range</span><span style="color: black;">&#40;</span><span style="color: #ff4500;">1</span><span style="color: #66cc66;">,</span> <span style="color: #ff4500;">5</span><span style="color: black;">&#41;</span><span style="color: black;">&#41;</span> <span style="color: #ff7700;font-weight:bold;">for</span> val <span style="color: #ff7700;font-weight:bold;">in</span> Stuff:     <span style="color: #ff7700;font-weight:bold;">print</span><span style="color: black;">&#40;</span>val<span style="color: black;">&#41;</span></pre></td></tr></table></div>

<p>Mais certains vont vouloir les noms avec les valeurs. Ils se tourneront peut être vers <code>OrderedDict</code>:</p>

<div class="wp_syntax"><table><tr><td class="code"><pre class="python" style="font-family:monospace;"><span style="color: #ff7700;font-weight:bold;">from</span> <span style="color: #dc143c;">collections</span> <span style="color: #ff7700;font-weight:bold;">import</span> OrderedDict
Stuff <span style="color: #66cc66;">=</span> OrderedDict<span style="color: black;">&#40;</span><span style="color: black;">&#40;</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'FOO'</span><span style="color: #66cc66;">,</span> <span style="color: #ff4500;">1</span><span style="color: black;">&#41;</span><span style="color: #66cc66;">,</span> <span style="color: black;">&#40;</span><span style="color: #483d8b;">'BAR'</span><span style="color: #66cc66;">,</span> <span style="color: #ff4500;">2</span><span style="color: black;">&#41;</span><span style="color: #66cc66;">,</span> <span style="color: black;">&#40;</span><span style="color: #483d8b;">'DOH'</span><span style="color: #66cc66;">,</span> <span style="color: #ff4500;">3</span><span style="color: black;">&#41;</span><span style="color: #66cc66;">,</span> <span style="color: black;">&#40;</span><span style="color: #483d8b;">'OYO'</span><span style="color: #66cc66;">,</span> <span style="color: #ff4500;">4</span><span style="color: black;">&#41;</span><span style="color: black;">&#41;</span><span style="color: black;">&#41;</span> <span style="color: #ff7700;font-weight:bold;">for</span> attr<span style="color: #66cc66;">,</span> value <span style="color: #ff7700;font-weight:bold;">in</span> Stuff.<span style="color: black;">items</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>:     <span style="color: #ff7700;font-weight:bold;">print</span><span style="color: black;">&#40;</span>attr<span style="color: #66cc66;">,</span> value<span style="color: black;">&#41;</span></pre></td></tr></table></div>

<p>Mais pas d’accès direct par attribut, et il faut faire gaffe à la saisie ou quand on change les valeurs, etc.</p>
<p>Bref, les quelques personnes qui voulaient les enums n’étaient parfaitement satisfaites de ces solutions.</p>
<h2>Ce que font les enums en Python</h2>
<p>En gros, les Enums sont des classes bridées qui représentent des itérables de constantes. Chaque constante est un couple clé/valeur.</p>

<div class="wp_syntax"><table><tr><td class="code"><pre class="python" style="font-family:monospace;"><span style="color: #ff7700;font-weight:bold;">from</span> enum <span style="color: #ff7700;font-weight:bold;">import</span> Enum
&nbsp;
<span style="color: #ff7700;font-weight:bold;">class</span> Stuff<span style="color: black;">&#40;</span>Enum<span style="color: black;">&#41;</span>:     FOO <span style="color: #66cc66;">=</span> <span style="color: #ff4500;">1</span>     BAR <span style="color: #66cc66;">=</span> <span style="color: #ff4500;">2</span>     DOH <span style="color: #66cc66;">=</span> <span style="color: #ff4500;">3</span>     OYO <span style="color: #66cc66;">=</span> <span style="color: #ff4500;">4</span>
&nbsp;
<span style="color: #ff7700;font-weight:bold;">for</span> member <span style="color: #ff7700;font-weight:bold;">in</span> Stuff:      <span style="color: #ff7700;font-weight:bold;">print</span><span style="color: black;">&#40;</span>member.<span style="color: black;">name</span><span style="color: #66cc66;">,</span> member.<span style="color: black;">value</span><span style="color: black;">&#41;</span></pre></td></tr></table></div>

<p>Les enums peuvent être héritées (mais de manière limitée) et implémenter un peu de logique puisque ce sont finalement des classes.</p>
<p>Il existe un type <code>IntEnum</code> pour que chaque membre soit comparable à un int directement plutôt que de voir chercher <code>.value</code>, qui par ailleurs peut être n’importe quel type avec <code>Enum</code>.</p>
<p>Et on a une syntaxe raccourcie:</p>

<div class="wp_syntax"><table><tr><td class="code"><pre class="python" style="font-family:monospace;">Stuff <span style="color: #66cc66;">=</span> Enum<span style="color: black;">&#40;</span><span style="color: #483d8b;">'Stuff'</span><span style="color: #66cc66;">,</span> <span style="color: #483d8b;">'FOO BAR DOH OYO'</span><span style="color: black;">&#41;</span></pre></td></tr></table></div>

<p>Bref, la feature ne fait pas grand-chose, en tout cas rien de ce qu’on ne pouvait faire avant. Mais elle a quelques avantages :</p>
<ul>
<li>Les enums sont une forme de documentation par le code. Quand on en lit une, on sait que ça va être un groupe de valeurs de configuration.</li>
<li>Ca s’inspecte bien et l’affichage est clair. </li>
<li>C’est picklable.</li>
<li>Ca permet de faire des wrappers pour les langages qui eux utilisent beaucoup les enums ou des structs. Comme Python sert souvent de glue, c’est logique de vouloir un type qui matche.</li>
<li>On peut choisir entre des membres comparables entre enums (IntEnum) et avec les int, ou pas (Enum).</li>
<li>Les membres peuvent avoir des alias.</li>
</ul>
<p>Mais tout ce temps après avoir vu les enums introduites, je n’en ai toujours pas l’usage. Jamais assez de constantes, ou alors quand j’en ai, je peux en faire des tuples ou des dicos sans avoir besoin d’un import.</p>
<p>Je suppose que je fais pas assez d’extensions C.</p>
<p>Quoiqu’il en soit généralement quand je vois des gens demander des enums, ce sont souvent pour de mauvaises raisons, pour reproduire ce qu’ils ont l’habitude d’utiliser dans un autre langage. Mais il y a vraiment peu de use cases typiquement Python pour les enums.</p>
<p>Allez, si vous avez quelques flags à mettre quelque part ou une API à wrapper, mettre une enum ne fera pas de mal.</p>


feed_id: http://sametmax.com/?p=17836
title: Enfin un moyen d&#8217;afficher une vidéo avec des cases à cocher (PyQt4 et OpenCV inside) 4
uri: Enfin-un-moyen-d-afficher-une-vidéo-avec-des-cases-à-cocher-(PyQt4
url:
http://sametmax.com/enfin-un-moyen-dafficher-une-video-avec-des-cases-a-cocher-pyqt4-et-opencv-inside/

content:
<p style="text-align: center;"><em><small>Ceci est un post invité de <a href="http://sametmax.com/author/01ivier/">01ivier</a> posté sous licence <a href="http://creativecommons.org/licenses/by/3.0/">creative common 3.0 unported</a>.</small></em></p>
<p>Bonsjours les amis,</p>
<p>J&#8217;espère que vous allez bien et que vous n&#8217;avez pas trop pensé que j&#8217;étais mort rapport au fait que ça faisait longtemps que je n&#8217;avais pas posté d&#8217;article.</p>
<p>&nbsp;</p>
<h3>Où je mentionne plusieurs fois le mot JavaScript.</h3>
<p>Le fait est que j&#8217;étais trop occupé à faire du <strong>JavaScript</strong>.</p>
<p>Bon, ce n&#8217;est pas exactement vrai, mais, je voulais vérifier une théorie selon laquelle dès qu&#8217;<a href="http://sametmax.com/la-communaute-js-est-actuellement-une-machine-a-creer-de-la-dette-technique/" target="_blank">on parle de <strong>JavaScript</strong> sur Sam&amp;Max,</a> le nombre des commentaires explosent (je n&#8217;ai pas de théorie sur le fait qu&#8217;il faille l&#8217;écrire en <strong>gras</strong>, mais je trouve ça <strong>joli</strong> (<strong>sauf quand</strong> il <strong>y en</strong> a <strong>partout</strong> (parce <strong>qu&#8217;après</strong> ça <strong>fait trop</strong>))).</p>
<p>Mais, dire que j&#8217;étais trop occupé à faire du <strong>JavaScript</strong> n&#8217;est pas précisément faux non plus dans la mesure où je me suis mis à <a href="http://p5js.org/">p5.js</a>, qui est au <strong>JavaScript</strong> ce que <a href="https://processing.org/" target="_blank">Processing</a> est au <strong>Java</strong>, à savoir un environnement de développement simplifié pour les pucelles et autres puceaux de la programmation.</p>
<p><strong>p5.js</strong> qui m&#8217;a permis d&#8217;assouvir un de mes fantasmes : <strong>afficher un cheval qui court avec des cases à cocher dans une page web.</strong>.</p>
<div id="attachment_17847" style="width: 576px" class="wp-caption aligncenter"><a href="media/1fc13ecb.MuybridgeRaceHorse-16.gif" data-original-source="http://sametmax.com/wp-content/uploads/2016/01/MuybridgeRaceHorse-16.gif" class="grouped_elements" rel="tc-fancybox-group17836"><img src="media/1fc13ecb.MuybridgeRaceHorse-16.gif" data-original-source="http://sametmax.com/wp-content/uploads/2016/01/MuybridgeRaceHorse-16.gif" alt="Un cheval qui court en cases à cocher d'après les célèbres photographies d&#039;Easweard Muybridge, désormais dans le domaine publique." width="566" height="384" class="size-full wp-image-17847" /></a><p class="wp-caption-text">Un cheval qui court en cases à cocher d&#8217;après les photos d&#8217;<a href="https://fr.wikipedia.org/wiki/Eadweard_Muybridge">E.Muybridge</a>, désormais dans le domaine publique.</p></div>
<p>Je vous ai mis un GIF animé, pour faire de l&#8217;animation, mais vous pouvez le voir en vrai <a href="http://yamatierea.org/CheckHorse/" target="_blank">ici</a> avec vos cases à cocher à vous.</p>
<p>(Sauf pour celles et ceux pour qui ça ne marchera pas, bien évidemment.)</p>
<p>(Voilà pour la partie <strong>JavaScript</strong>. Je ne vais plus en parler. Les personnes concernées peuvent donc se rendre dès à présent à la section des commentaires. Merci.)</p>
<p>&nbsp;</p>
<h3>Où je m&#8217;attelle à une tâche bien plus sérieuse.</h3>
<p>J&#8217;étais content, mais, passé ces enfantillages, il me fallait désormais m&#8217;atteler à une tâche bien plus sérieuse : afficher un cheval qui court avec des cases à cocher en <strong>Python</strong>.</p>
<p>Pour cela, j&#8217;ai choisi d&#8217;utiliser <a href="https://wiki.python.org/moin/PyQt4">PyQt4</a> parce que c&#8217;est <strong>la meilleure librairie pour créer des interfaces graphiques en Python</strong>.</p>
<p>&#8230;</p>
<p>Ah Ah Ah&#8230;<br />
Nan, mais, comme si j&#8217;en savais quelque chose&#8230;<br />
C&#8217;est juste qu&#8217;un ami avait déjà réussi à afficher un bouton avec&#8230;<br />
Donc, je me suis dit que c&#8217;était pour moi&#8230;<br />
Bon, en fait, ce n&#8217;est pas pour moi&#8230;<br />
Mais, que les choses soient claires, c&#8217;est l&#8217;informatique en général et la programmation en particulier qui ne sont pas pour moi&#8230;<br />
Je veux dire, quand je vais lire <a href="https://stackoverflow.com/">Stack Overflow</a>, j&#8217;ai de la peine pour moi&#8230;<br />
C&#8217;est à dire que je me fais vraiment pitié tant je comprends que dalle&#8230;<br />
Il m&#8217;est déjà arrivé de mettre une dizaine de secondes pour savoir si je lisais bien du Python (et s&#8217;en était)&#8230;<br />
Nan, c&#8217;est moche&#8230;<br />
Mais bon, je ne sais faire que de l&#8217;informatique (on me souffle à l&#8217;oreille que je sais aussi faire du <a href="http://wiki.labomedia.org/index.php/Chirashi_au_saumon">Chirashi</a> et c&#8217;est vrai)<br />
Donc, je fais de l&#8217;informatique&#8230; (et du Chirashi)<br />
Et j&#8217;en chie&#8230;<br />
Mais à la fin je gagne&#8230;<br />
Et je me paye même le luxe d&#8217;en faire un article sur S&#038;M&#8230;<br />
Pourquoi je vous raconte ça ?<br />
Aucune idée&#8230;<br />
Toujours est-il que j&#8217;ai choisi <strong>PyQt4</strong>&#8230;<br />
Que ça n&#8217;est pas pour moi&#8230;<br />
Mais que [SPOILER ALERT] ça a fini par marcher&#8230;<br />
&#8230;</p>
<p>&nbsp;</p>
<h3>Où j&#8217;affiche une fenêtre. UNE FE-NÊ-TRE. Oui messieurs, dames.</h3>
<p>D&#8217;abord, il faut installer <strong>PyQt4</strong>&#8230;</p>

<div class="wp_syntax"><table><tr><td class="code"><pre class="bash" style="font-family:monospace;"><span style="color: #c20cb9; font-weight: bold;">sudo</span> <span style="color: #c20cb9; font-weight: bold;">apt-get install</span> python-qt4</pre></td></tr></table></div>

<p>&#8230;et on ne peut pas dire que ce soit trop compliqué.</p>
<p>Et, voici le code minimal pour afficher une fenêtre :</p>

<div class="wp_syntax"><table><tr><td class="code"><pre class="python" style="font-family:monospace;"><span style="color: #808080; font-style: italic;">#-*- coding: utf-8 -*-</span>
<span style="color: #ff7700;font-weight:bold;">from</span> PyQt4 <span style="color: #ff7700;font-weight:bold;">import</span> QtGui
&nbsp;
<span style="color: #808080; font-style: italic;"># On crée une appli Qt</span>
application <span style="color: #66cc66;">=</span> QtGui.<span style="color: black;">QApplication</span><span style="color: black;">&#40;</span><span style="color: black;">&#91;</span><span style="color: black;">&#93;</span><span style="color: black;">&#41;</span>
<span style="color: #808080; font-style: italic;"># On fout quelque chose dedans</span>
fond <span style="color: #66cc66;">=</span> QtGui.<span style="color: black;">QWidget</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
<span style="color: #808080; font-style: italic;"># On l'affiche</span>
fond.<span style="color: black;">show</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
<span style="color: #808080; font-style: italic;"># On lance l'appli</span>
application.<span style="color: black;">exec_</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span></pre></td></tr></table></div>

<p>Les connaisseurs apprécieront mon désormais célèbre style procédural de champion du monde.<br />
Mais bon. Ça marche où ça marche pas ?</p>
<div id="attachment_17863" style="width: 812px" class="wp-caption aligncenter"><a href="media/5bf44646.fenetre-pyQT4.png" data-original-source="http://sametmax.com/wp-content/uploads/2016/01/fenetre-pyQT4.png" class="grouped_elements" rel="tc-fancybox-group17836"><img src="media/5bf44646.fenetre-pyQT4.png" data-original-source="http://sametmax.com/wp-content/uploads/2016/01/fenetre-pyQT4.png" alt="Une fenêtre" width="802" height="389" class="size-full wp-image-17863" /></a><p class="wp-caption-text">Une fenêtre</p></div>
<p>Ça marche.</p>
<p>&nbsp;</p>
<h3>Où j&#8217;affiche un bouton dans la fenêtre.</h3>
<p>Toujours en procédural, voici comment on ajoute un <strong>bouton</strong> à notre fenêtre (que l&#8217;on customize un peu au passage).<br />
(J&#8217;insiste pour le procédural car, comme PyQt4 est conçu pour être utilisé en Programmation Orientée Objet, vous ne verrez pas ça tous les jours)</p>

<div class="wp_syntax"><table><tr><td class="code"><pre class="python" style="font-family:monospace;"><span style="color: #808080; font-style: italic;">#-*- coding: utf-8 -*-</span>
<span style="color: #ff7700;font-weight:bold;">from</span> PyQt4 <span style="color: #ff7700;font-weight:bold;">import</span> QtGui
&nbsp;
app <span style="color: #66cc66;">=</span> QtGui.<span style="color: black;">QApplication</span><span style="color: black;">&#40;</span><span style="color: black;">&#91;</span><span style="color: black;">&#93;</span><span style="color: black;">&#41;</span>
&nbsp;
<span style="color: #808080; font-style: italic;"># On crée un fond et lui donne une taille, une position sur l'écran et un titre</span>
fond <span style="color: #66cc66;">=</span> QtGui.<span style="color: black;">QWidget</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>  
fond.<span style="color: black;">resize</span><span style="color: black;">&#40;</span><span style="color: #ff4500;">300</span><span style="color: #66cc66;">,</span> <span style="color: #ff4500;">200</span><span style="color: black;">&#41;</span>
fond.<span style="color: black;">move</span><span style="color: black;">&#40;</span><span style="color: #ff4500;">100</span><span style="color: #66cc66;">,</span> <span style="color: #ff4500;">100</span><span style="color: black;">&#41;</span> 
fond.<span style="color: black;">setWindowTitle</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">&quot;C'est formidable&quot;</span><span style="color: black;">&#41;</span> 
&nbsp;
<span style="color: #808080; font-style: italic;"># On crée un bouton sur notre fond et on lui donne une position sur celui-ci</span>
bouton <span style="color: #66cc66;">=</span> QtGui.<span style="color: black;">QPushButton</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'Mon petit bouton'</span><span style="color: #66cc66;">,</span>fond<span style="color: black;">&#41;</span>
bouton.<span style="color: black;">move</span><span style="color: black;">&#40;</span><span style="color: #ff4500;">80</span><span style="color: #66cc66;">,</span> <span style="color: #ff4500;">80</span><span style="color: black;">&#41;</span>
&nbsp;
fond.<span style="color: black;">show</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
app.<span style="color: black;">exec_</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span></pre></td></tr></table></div>

<p>Et hop&#8230;</p>
<div id="attachment_17876" style="width: 312px" class="wp-caption aligncenter"><a href="media/425a4dff.bouton-pyQt4.gif" data-original-source="http://sametmax.com/wp-content/uploads/2016/01/bouton-pyQt4.gif" class="grouped_elements" rel="tc-fancybox-group17836"><img src="media/425a4dff.bouton-pyQt4.gif" data-original-source="http://sametmax.com/wp-content/uploads/2016/01/bouton-pyQt4.gif" alt="Un petit bouton dans une fenêtre" width="302" height="229" class="size-full wp-image-17876" /></a><p class="wp-caption-text">Un petit bouton dans une fenêtre</p></div>
<p>La première fois que ça a marché, j&#8217;ai cliqué 25 fois sur le bouton et j&#8217;en garde un très bon souvenir.</p>
<p>&nbsp;</p>
<h3>Où j&#8217;affiche plein de cases à cocher dans la fenêtre.</h3>
<p>Bon, on peut afficher tout un tas de bordel dans une fenêtre, vous vous en doutez. Des images, du texte, des champs texte, des sliders, des menus déroulants, des labels, des séparations&#8230;<br />
Mais, ça n&#8217;est pas le propos ici.<br />
Nous ce qu&#8217;on veut, ce sont des cases à cocher. Plein de cases à cocher.</p>
<p>J&#8217;ai commencé par les placer dans une grille, mais, je me suis rendu compte qu&#8217;en leur donnant des positions absolues en pixels, il était possible de les faire se toucher, voire se chevaucher. Ce qui, quand on veut afficher un cheval, apporte un petit plus indéniable.<br />
(chevaucher/cheval/humour)</p>
<p>Allons y donc pour nos cases à cocher.<br />
(Je vous recolle tout à chaque fois parce que je sais qu&#8217;il y a des goulus parmi vous.)</p>

<div class="wp_syntax"><table><tr><td class="code"><pre class="python" style="font-family:monospace;"><span style="color: #808080; font-style: italic;">#-*- coding: utf-8 -*-</span>
<span style="color: #ff7700;font-weight:bold;">from</span> PyQt4 <span style="color: #ff7700;font-weight:bold;">import</span> QtGui
&nbsp;
<span style="color: #808080; font-style: italic;"># On définie les dimensions de notre bazar</span>
largeur <span style="color: #66cc66;">=</span> <span style="color: #ff4500;">40</span>
hauteur <span style="color: #66cc66;">=</span> <span style="color: #ff4500;">30</span>
tailleCase <span style="color: #66cc66;">=</span><span style="color: #ff4500;">14</span>
&nbsp;
app <span style="color: #66cc66;">=</span> QtGui.<span style="color: black;">QApplication</span><span style="color: black;">&#40;</span><span style="color: black;">&#91;</span><span style="color: black;">&#93;</span><span style="color: black;">&#41;</span>
&nbsp;
fond <span style="color: #66cc66;">=</span> QtGui.<span style="color: black;">QWidget</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>  
fond.<span style="color: black;">resize</span><span style="color: black;">&#40;</span>largeur*tailleCase+<span style="color: #ff4500;">6</span><span style="color: #66cc66;">,</span> hauteur*tailleCase+<span style="color: #ff4500;">6</span><span style="color: black;">&#41;</span> <span style="color: #808080; font-style: italic;"># le +6 c'est pour tenir compte des marges</span>
fond.<span style="color: black;">setWindowTitle</span><span style="color: black;">&#40;</span>u<span style="color: #483d8b;">&quot;Plein de cases à cocher&quot;</span><span style="color: black;">&#41;</span> 
&nbsp;
<span style="color: #808080; font-style: italic;"># On parcourt les ordonnées</span>
<span style="color: #ff7700;font-weight:bold;">for</span> j <span style="color: #ff7700;font-weight:bold;">in</span> <span style="color: #008000;">range</span><span style="color: black;">&#40;</span>hauteur<span style="color: black;">&#41;</span>:
&nbsp;
    <span style="color: #808080; font-style: italic;"># On parcourt les abscisses</span>
    <span style="color: #ff7700;font-weight:bold;">for</span> i <span style="color: #ff7700;font-weight:bold;">in</span> <span style="color: #008000;">range</span><span style="color: black;">&#40;</span>largeur<span style="color: black;">&#41;</span>:
&nbsp;
        <span style="color: #808080; font-style: italic;"># On crée une case à cocher sur notre fond</span>
        check_box <span style="color: #66cc66;">=</span> QtGui.<span style="color: black;">QCheckBox</span><span style="color: black;">&#40;</span>fond<span style="color: black;">&#41;</span>
        <span style="color: #808080; font-style: italic;"># Et on la positionne</span>
        check_box.<span style="color: black;">move</span><span style="color: black;">&#40;</span>i*tailleCase<span style="color: #66cc66;">,</span> j*tailleCase<span style="color: black;">&#41;</span>
&nbsp;
fond.<span style="color: black;">show</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
app.<span style="color: black;">exec_</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span></pre></td></tr></table></div>

<div id="attachment_17886" style="width: 578px" class="wp-caption aligncenter"><a href="media/f6659113.CheckBox-Qt4.png" data-original-source="http://sametmax.com/wp-content/uploads/2016/01/CheckBox-Qt4.png" class="grouped_elements" rel="tc-fancybox-group17836"><img src="media/f6659113.CheckBox-Qt4.png" data-original-source="http://sametmax.com/wp-content/uploads/2016/01/CheckBox-Qt4.png" alt="Le mot "Joie" écrit avec des cases à cocher." width="568" height="455" class="size-full wp-image-17886" /></a><p class="wp-caption-text">Le mot “Joie” écrit à la main avec des cases à cocher.</p></div>
<p>&nbsp;</p>
<h3>Où j&#8217;affiche plein de cases à cocher dans la fenêtre mais en utilisant cette fois la Programmation Orientée Objet.</h3>
<p>On arrive au bout. Mais, ce qu&#8217;il faut comprendre avec pyQt c&#8217;est que quand le programme arrive à l’instruction <code>app.exec_()</code> il démarre une boucle. On est alors un peu coincé pour parler avec l&#8217;interface.<br />
Il est possible de cliquer sur les cases à la main, soit, mais, nous ce qu&#8217;on veut, c&#8217;est que ce soit un bout de code qui le fasse à notre place (car ayant bien mis 2 minutes pour composer une seule image affichant le mot &#8220;Joie&#8221;, j&#8217;ai des doutes pour le 25 fps).</p>
<p>Dans tous les cas, il n&#8217;est plus possible de rester en procédural. Voici donc la version POO du précédent script :</p>

<div class="wp_syntax"><table><tr><td class="code"><pre class="python" style="font-family:monospace;"><span style="color: #808080; font-style: italic;">#-*- coding: utf-8 -*-</span>
<span style="color: #ff7700;font-weight:bold;">from</span> PyQt4 <span style="color: #ff7700;font-weight:bold;">import</span> QtGui
<span style="color: #ff7700;font-weight:bold;">import</span> <span style="color: #dc143c;">sys</span>
&nbsp;
largeur <span style="color: #66cc66;">=</span> <span style="color: #ff4500;">40</span>
hauteur <span style="color: #66cc66;">=</span> <span style="color: #ff4500;">30</span>
tailleCase <span style="color: #66cc66;">=</span> <span style="color: #ff4500;">14</span>
&nbsp;
<span style="color: #ff7700;font-weight:bold;">class</span> CheckBoxVideo<span style="color: black;">&#40;</span>QtGui.<span style="color: black;">QWidget</span><span style="color: black;">&#41;</span>:
&nbsp;
    <span style="color: #808080; font-style: italic;"># On initialise la class, j'imagine...</span>
    <span style="color: #ff7700;font-weight:bold;">def</span> <span style="color: #0000cd;">__init__</span><span style="color: black;">&#40;</span><span style="color: #008000;">self</span><span style="color: black;">&#41;</span>:
        <span style="color: #008000;">super</span><span style="color: black;">&#40;</span>CheckBoxVideo<span style="color: #66cc66;">,</span> <span style="color: #008000;">self</span><span style="color: black;">&#41;</span>.<span style="color: #0000cd;">__init__</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
        <span style="color: #008000;">self</span>.<span style="color: black;">interface</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
&nbsp;
    <span style="color: #808080; font-style: italic;"># On initialise l'interface    </span>
    <span style="color: #ff7700;font-weight:bold;">def</span> interface<span style="color: black;">&#40;</span><span style="color: #008000;">self</span><span style="color: black;">&#41;</span>:
&nbsp;
        <span style="color: #008000;">self</span>.<span style="color: black;">resize</span><span style="color: black;">&#40;</span>largeur*tailleCase+<span style="color: #ff4500;">6</span><span style="color: #66cc66;">,</span> hauteur*tailleCase+<span style="color: #ff4500;">6</span><span style="color: black;">&#41;</span>
        <span style="color: #008000;">self</span>.<span style="color: black;">setWindowTitle</span><span style="color: black;">&#40;</span>u<span style="color: #483d8b;">&quot;Plein de cases à cocher, mais en POO.&quot;</span><span style="color: black;">&#41;</span>
&nbsp;
        <span style="color: #ff7700;font-weight:bold;">for</span> j <span style="color: #ff7700;font-weight:bold;">in</span> <span style="color: #008000;">range</span><span style="color: black;">&#40;</span>hauteur<span style="color: black;">&#41;</span>:
&nbsp;
            <span style="color: #ff7700;font-weight:bold;">for</span> i <span style="color: #ff7700;font-weight:bold;">in</span> <span style="color: #008000;">range</span><span style="color: black;">&#40;</span>largeur<span style="color: black;">&#41;</span>:
&nbsp;
                check_box <span style="color: #66cc66;">=</span> QtGui.<span style="color: black;">QCheckBox</span><span style="color: black;">&#40;</span><span style="color: #008000;">self</span><span style="color: black;">&#41;</span>
                check_box.<span style="color: black;">move</span><span style="color: black;">&#40;</span>i*tailleCase<span style="color: #66cc66;">,</span> j*tailleCase<span style="color: black;">&#41;</span>     
&nbsp;
        <span style="color: #008000;">self</span>.<span style="color: black;">show</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
&nbsp;
&nbsp;
<span style="color: #ff7700;font-weight:bold;">if</span> __name__ <span style="color: #66cc66;">==</span> <span style="color: #483d8b;">&quot;__main__&quot;</span>:
      appli <span style="color: #66cc66;">=</span> QtGui.<span style="color: black;">QApplication</span><span style="color: black;">&#40;</span><span style="color: #dc143c;">sys</span>.<span style="color: black;">argv</span><span style="color: black;">&#41;</span>
      ckbx <span style="color: #66cc66;">=</span> CheckBoxVideo<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
      <span style="color: #dc143c;">sys</span>.<span style="color: black;">exit</span><span style="color: black;">&#40;</span>appli.<span style="color: black;">exec_</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span><span style="color: black;">&#41;</span></pre></td></tr></table></div>

<div id="attachment_17894" style="width: 578px" class="wp-caption aligncenter"><a href="media/dac08c37.CheckBox2-pyQt4.png" data-original-source="http://sametmax.com/wp-content/uploads/2016/01/CheckBox2-pyQt4.png" class="grouped_elements" rel="tc-fancybox-group17836"><img src="media/dac08c37.CheckBox2-pyQt4.png" data-original-source="http://sametmax.com/wp-content/uploads/2016/01/CheckBox2-pyQt4.png" alt="Le mot &quot;Bonheur&quot; écrit en cases à cocher mais en utilisant cette fois -ci la programmation orientée objet." width="568" height="455" class="size-full wp-image-17894" /></a><p class="wp-caption-text">Le mot &#8220;Bonheur&#8221; écrit en cases à cocher mais en utilisant cette fois la programmation orientée objet.</p></div>
<p>J&#8217;ai ajouté un petit <code>if __name__ == "__main__"</code> histoire de laisser croire que je comprends ce que je fais.<br />
Et puis j&#8217;ai intégré la librairie <code>sys</code> pour être conforme avec tous les exemples de tous les tutos que j&#8217;ai trouvé. Mais c&#8217;est vraiment par respect pour les devs qui se sont donné du mal à les écrire. Parce que, vous pouvez essayer, ça marche très bien sans.<br />
Bref.</p>
<p>&nbsp;</p>
<h3>Où, en faisant ce qu&#8217;il ne faut pas faire, je constate qu&#8217;il ne faut pas le faire.</h3>
<p>Je vous épargnerai la liste des trucs ridicules que j&#8217;ai essayé pour entrer dans cette boucle et modifier l&#8217;état des cases à cocher et n&#8217;évoquerai que les solutions que j&#8217;ai trouvé pour faire marcher mon bazar&#8230; pendant 20 secondes.</p>
<p>En effet, à force de chercher, je suis tombé sur ce fils de <a href="https://stackoverflow.com/questions/6783194/background-thread-with-qthread-in-pyqt">Stack Overflow</a> où la réponse la mieux notée donne trois moyens différents de créer des threads avec Qt.</p>
<p>J&#8217;ai méticuleusement tout recopié à l&#8217;arrache et testé chacune des solutions.<br />
Toutes m&#8217;ont permis de voir ma silhouette en cases à cocher avant de me retourner un délicat <code>segfault</code> au bout de 20 secondes.</p>
<p>Vraisemblablement, il y a quelque chose que je faisais mal.<br />
Chose qui a été confirmée par un article intitulé : <a href="http://blog.qt.io/blog/2010/06/17/youre-doing-it-wrong/">&#8220;You&#8217;re doing it wrong&#8230;&#8221;</a></p>
<p>C&#8217;est à ce moment que j&#8217;ai décidé de me coucher, vu que le Soleil se levait.</p>
<p>&nbsp;</p>
<h3>Où j&#8217;ai la confirmation que dormir peut s&#8217;avérer parfois utile pour se reposer.</h3>
<p>Si j&#8217;étais mort dans mon sommeil ce jour là, mes derniers mots auraient été  : &#8220;Putain de boucle de merde&#8221;.<br />
Mais je me suis réveillé et je me suis dis que, tout de même, il devait y avoir un moyen simple de faire exécuter du code dans cette boucle principale.</p>
<p>Et là, avec un cerveau frais, je me suis souvenu de <a href="http://zetcode.com/gui/pyqt4/widgets/">cet exemple expliquant comment animer une barre de progression</a>.<br />
Passer des valeurs à un objet pour en changer son état, c&#8217;est un peu ce que je cherchais à faire, non ?<br />
Non.<br />
C&#8217;était exactement ce que je cherchais à faire.</p>
<p>Le principe est de créer un <code>timer</code> et d&#8217;écouter ses tours d&#8217;horloge pour déclencher des événements.<br />
À noter la nécessité d&#8217;importer <strong>QtCore</strong> pour cela.</p>

<div class="wp_syntax"><table><tr><td class="code"><pre class="python" style="font-family:monospace;"><span style="color: #808080; font-style: italic;">#-*- coding: utf-8 -*-</span>
<span style="color: #ff7700;font-weight:bold;">from</span> PyQt4 <span style="color: #ff7700;font-weight:bold;">import</span> QtGui<span style="color: #66cc66;">,</span> QtCore
<span style="color: #ff7700;font-weight:bold;">import</span> <span style="color: #dc143c;">sys</span>
&nbsp;
largeurOut <span style="color: #66cc66;">=</span> <span style="color: #ff4500;">40</span>
hauteurOut <span style="color: #66cc66;">=</span> <span style="color: #ff4500;">30</span>
tailleCase <span style="color: #66cc66;">=</span> <span style="color: #ff4500;">14</span>
&nbsp;
&nbsp;
<span style="color: #ff7700;font-weight:bold;">class</span> CheckBoxVideo<span style="color: black;">&#40;</span>QtGui.<span style="color: black;">QWidget</span><span style="color: black;">&#41;</span>:
&nbsp;
    <span style="color: #ff7700;font-weight:bold;">def</span> <span style="color: #0000cd;">__init__</span><span style="color: black;">&#40;</span><span style="color: #008000;">self</span><span style="color: black;">&#41;</span>:
        <span style="color: #008000;">super</span><span style="color: black;">&#40;</span>CheckBoxVideo<span style="color: #66cc66;">,</span> <span style="color: #008000;">self</span><span style="color: black;">&#41;</span>.<span style="color: #0000cd;">__init__</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
        <span style="color: #008000;">self</span>.<span style="color: black;">interface</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
&nbsp;
    <span style="color: #ff7700;font-weight:bold;">def</span> interface<span style="color: black;">&#40;</span><span style="color: #008000;">self</span><span style="color: black;">&#41;</span>:
&nbsp;
        <span style="color: #008000;">self</span>.<span style="color: black;">resize</span><span style="color: black;">&#40;</span>largeurOut*tailleCase+<span style="color: #ff4500;">6</span><span style="color: #66cc66;">,</span> hauteurOut*tailleCase+<span style="color: #ff4500;">6</span><span style="color: black;">&#41;</span>
        <span style="color: #008000;">self</span>.<span style="color: black;">setWindowTitle</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">&quot;OMG ! Mais que se passe-t-il dans la console ?&quot;</span><span style="color: black;">&#41;</span>
&nbsp;
        <span style="color: #ff7700;font-weight:bold;">for</span> j <span style="color: #ff7700;font-weight:bold;">in</span> <span style="color: #008000;">range</span><span style="color: black;">&#40;</span>hauteurOut<span style="color: black;">&#41;</span>:
&nbsp;
            <span style="color: #ff7700;font-weight:bold;">for</span> i <span style="color: #ff7700;font-weight:bold;">in</span> <span style="color: #008000;">range</span><span style="color: black;">&#40;</span>largeurOut<span style="color: black;">&#41;</span>:
&nbsp;
                check_box <span style="color: #66cc66;">=</span> QtGui.<span style="color: black;">QCheckBox</span><span style="color: black;">&#40;</span><span style="color: #008000;">self</span><span style="color: black;">&#41;</span>
                check_box.<span style="color: black;">move</span><span style="color: black;">&#40;</span>i*tailleCase<span style="color: #66cc66;">,</span> j*tailleCase<span style="color: black;">&#41;</span>
&nbsp;
        <span style="color: #808080; font-style: italic;"># On crée le timer.</span>
        <span style="color: #008000;">self</span>.<span style="color: black;">timer</span> <span style="color: #66cc66;">=</span> QtCore.<span style="color: black;">QBasicTimer</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
        <span style="color: #808080; font-style: italic;"># Et on le lance en renseignant un délais pour chaque tour d'horloge.</span>
        <span style="color: #808080; font-style: italic;"># Ici, 40 millisecondes, pour tenter (naïvement) d'obtenir du 25 images par seconde.</span>
        <span style="color: #008000;">self</span>.<span style="color: black;">timer</span>.<span style="color: black;">start</span><span style="color: black;">&#40;</span><span style="color: #ff4500;">40</span><span style="color: #66cc66;">,</span> <span style="color: #008000;">self</span><span style="color: black;">&#41;</span>
&nbsp;
        <span style="color: #008000;">self</span>.<span style="color: black;">show</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
&nbsp;
    <span style="color: #808080; font-style: italic;"># Et voici la méthode qui écoute le timer.</span>
    <span style="color: #ff7700;font-weight:bold;">def</span> timerEvent<span style="color: black;">&#40;</span><span style="color: #008000;">self</span><span style="color: #66cc66;">,</span> e<span style="color: black;">&#41;</span>:
&nbsp;
        <span style="color: #ff7700;font-weight:bold;">print</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">&quot;Ce message va s'écrire en console toutes les 40 ms.&quot;</span><span style="color: black;">&#41;</span>
&nbsp;
&nbsp;
<span style="color: #ff7700;font-weight:bold;">if</span> __name__ <span style="color: #66cc66;">==</span> <span style="color: #483d8b;">&quot;__main__&quot;</span>:
      appli <span style="color: #66cc66;">=</span> QtGui.<span style="color: black;">QApplication</span><span style="color: black;">&#40;</span><span style="color: #dc143c;">sys</span>.<span style="color: black;">argv</span><span style="color: black;">&#41;</span>
      ckbx <span style="color: #66cc66;">=</span> CheckBoxVideo<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
      <span style="color: #dc143c;">sys</span>.<span style="color: black;">exit</span><span style="color: black;">&#40;</span>appli.<span style="color: black;">exec_</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span><span style="color: black;">&#41;</span></pre></td></tr></table></div>

<div id="attachment_17921" style="width: 1062px" class="wp-caption aligncenter"><a href="media/a41a53ff.CheckBox4-pyQt4.png" data-original-source="http://sametmax.com/wp-content/uploads/2016/01/CheckBox4-pyQt4.png" class="grouped_elements" rel="tc-fancybox-group17836"><img src="media/a41a53ff.CheckBox4-pyQt4.png" data-original-source="http://sametmax.com/wp-content/uploads/2016/01/CheckBox4-pyQt4.png" alt="Des cases à cocher dans une fenêtre et des messages dans une console." width="1052" height="455" class="size-full wp-image-17921" /></a><p class="wp-caption-text">Des cases à cocher dans une fenêtre et des messages dans une console.</p></div>
<p>&nbsp;</p>
<h3>Où j&#8217;évoque ma jeunesse.</h3>
<p>Il s&#8217;agissait maintenant de trouver un moyen de traiter du flux vidéo.<br />
Mais comme je suis un ouf et que non seulement j&#8217;avais déjà réussi à <strong>afficher de la vidéo 3-bit dans mon terminal</strong> , mais qu&#8217;en plus je vous en avais fait part <a href="http://sametmax.com/ou-il-est-presente-une-methode-en-python-pour-afficher-de-la-video-3-bit-dans-son-terminal/">ici-même</a>, et bien je suis allé copier/coller mon pâté. Tout simplement.</p>
<p>Pour celles et ceux qui n&#8217;ont pas eu la force psychologique de cliquer sur le lien, sachez que la solution que j&#8217;avais retenue à l&#8217;époque pour lire un fichier vidéo afin d&#8217;en traiter les données était <a href="https://opencv-python-tutroals.readthedocs.org/en/latest/py_tutorials/py_tutorials.html">OpenCV</a>, et que c&#8217;est tout aussi pertinent maintenant.<br />
(Enfin, ça marche. Ce qui, à mon niveau, est la définition même de la pertinence.)</p>
<p>&nbsp;</p>
<h3>Où il est de nouveau affiché un cheval qui court en cases à cocher, mais en Python cette fois.</h3>
<p>Et voilà&#8230;<br />
C&#8217;est terminé&#8230;</p>
<p>Voici le script après l&#8217;intégration du code exploitant OpenCV :</p>

<div class="wp_syntax"><table><tr><td class="code"><pre class="python" style="font-family:monospace;"><span style="color: #808080; font-style: italic;">#-*- coding: utf-8 -*-</span>
<span style="color: #ff7700;font-weight:bold;">from</span> PyQt4 <span style="color: #ff7700;font-weight:bold;">import</span> QtGui<span style="color: #66cc66;">,</span> QtCore
<span style="color: #ff7700;font-weight:bold;">import</span> <span style="color: #dc143c;">sys</span>
<span style="color: #ff7700;font-weight:bold;">import</span> cv2
&nbsp;
largeur <span style="color: #66cc66;">=</span> <span style="color: #ff4500;">50</span>
hauteur <span style="color: #66cc66;">=</span> <span style="color: #ff4500;">34</span>
tailleCase <span style="color: #66cc66;">=</span> <span style="color: #ff4500;">14</span>
&nbsp;
<span style="color: #808080; font-style: italic;"># On crée une liste qui va stocker les QCheckBox</span>
listeCB <span style="color: #66cc66;">=</span> <span style="color: black;">&#91;</span><span style="color: black;">&#93;</span>
&nbsp;
<span style="color: #808080; font-style: italic;"># On définit un seuil pour pour cocher ou non les cases</span>
seuil <span style="color: #66cc66;">=</span> <span style="color: #ff4500;">150</span>
&nbsp;
<span style="color: #808080; font-style: italic;"># On définit le fichier à lire.</span>
video <span style="color: #66cc66;">=</span> cv2.<span style="color: black;">VideoCapture</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'cheval_qui_court.mjpg'</span><span style="color: black;">&#41;</span>
&nbsp;
<span style="color: #ff7700;font-weight:bold;">class</span> CheckBoxVideo<span style="color: black;">&#40;</span>QtGui.<span style="color: black;">QWidget</span><span style="color: black;">&#41;</span>:
&nbsp;
    <span style="color: #ff7700;font-weight:bold;">def</span> <span style="color: #0000cd;">__init__</span><span style="color: black;">&#40;</span><span style="color: #008000;">self</span><span style="color: black;">&#41;</span>:
        <span style="color: #008000;">super</span><span style="color: black;">&#40;</span>CheckBoxVideo<span style="color: #66cc66;">,</span> <span style="color: #008000;">self</span><span style="color: black;">&#41;</span>.<span style="color: #0000cd;">__init__</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
        <span style="color: #008000;">self</span>.<span style="color: black;">interface</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
&nbsp;
    <span style="color: #ff7700;font-weight:bold;">def</span> interface<span style="color: black;">&#40;</span><span style="color: #008000;">self</span><span style="color: black;">&#41;</span>:
&nbsp;
        <span style="color: #008000;">self</span>.<span style="color: black;">resize</span><span style="color: black;">&#40;</span>largeur*tailleCase+<span style="color: #ff4500;">6</span><span style="color: #66cc66;">,</span> hauteur*tailleCase+<span style="color: #ff4500;">6</span><span style="color: black;">&#41;</span>
        <span style="color: #008000;">self</span>.<span style="color: black;">setWindowTitle</span><span style="color: black;">&#40;</span>u<span style="color: #483d8b;">&quot;Un cheval qui court en cases à cocher&quot;</span><span style="color: black;">&#41;</span>
&nbsp;
        <span style="color: #ff7700;font-weight:bold;">for</span> j <span style="color: #ff7700;font-weight:bold;">in</span> <span style="color: #008000;">range</span><span style="color: black;">&#40;</span>hauteur<span style="color: black;">&#41;</span>: 
            <span style="color: #ff7700;font-weight:bold;">for</span> i <span style="color: #ff7700;font-weight:bold;">in</span> <span style="color: #008000;">range</span><span style="color: black;">&#40;</span>largeur<span style="color: black;">&#41;</span>: 
                check_box <span style="color: #66cc66;">=</span> QtGui.<span style="color: black;">QCheckBox</span><span style="color: black;">&#40;</span><span style="color: #008000;">self</span><span style="color: black;">&#41;</span>
                check_box.<span style="color: black;">move</span><span style="color: black;">&#40;</span>i*tailleCase<span style="color: #66cc66;">,</span> j*tailleCase<span style="color: black;">&#41;</span>
                <span style="color: #808080; font-style: italic;"># On ajoute chaque QCheckBox dans notre liste</span>
                listeCB.<span style="color: black;">append</span><span style="color: black;">&#40;</span>check_box<span style="color: black;">&#41;</span>   
&nbsp;
        <span style="color: #008000;">self</span>.<span style="color: black;">timer</span> <span style="color: #66cc66;">=</span> QtCore.<span style="color: black;">QBasicTimer</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
        <span style="color: #008000;">self</span>.<span style="color: black;">timer</span>.<span style="color: black;">start</span><span style="color: black;">&#40;</span><span style="color: #ff4500;">40</span><span style="color: #66cc66;">,</span> <span style="color: #008000;">self</span><span style="color: black;">&#41;</span>
        <span style="color: #008000;">self</span>.<span style="color: black;">show</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
&nbsp;
    <span style="color: #ff7700;font-weight:bold;">def</span> timerEvent<span style="color: black;">&#40;</span><span style="color: #008000;">self</span><span style="color: #66cc66;">,</span> e<span style="color: black;">&#41;</span>:
&nbsp;
        <span style="color: #808080; font-style: italic;"># On lit une frame de la vidéo</span>
        ret<span style="color: #66cc66;">,</span>img <span style="color: #66cc66;">=</span> video.<span style="color: black;">read</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
        <span style="color: #808080; font-style: italic;"># On la passe en niveau de gris</span>
        gray <span style="color: #66cc66;">=</span> cv2.<span style="color: black;">cvtColor</span><span style="color: black;">&#40;</span>img<span style="color: #66cc66;">,</span> cv2.<span style="color: black;">COLOR_BGR2GRAY</span><span style="color: black;">&#41;</span>
        <span style="color: #808080; font-style: italic;"># On la retaille pour que le nombre de pixel correspond au nombre de cases à cocher</span>
        gray <span style="color: #66cc66;">=</span> cv2.<span style="color: black;">resize</span><span style="color: black;">&#40;</span>gray<span style="color: #66cc66;">,</span> <span style="color: black;">&#40;</span>largeur<span style="color: #66cc66;">,</span> hauteur<span style="color: black;">&#41;</span><span style="color: black;">&#41;</span> 
&nbsp;
        <span style="color: #808080; font-style: italic;"># On parcourt notre liste</span>
        <span style="color: #ff7700;font-weight:bold;">for</span> index<span style="color: #66cc66;">,</span> checkBox <span style="color: #ff7700;font-weight:bold;">in</span> <span style="color: #008000;">enumerate</span><span style="color: black;">&#40;</span>listeCB<span style="color: black;">&#41;</span>:
&nbsp;
            <span style="color: #808080; font-style: italic;"># On transforme les index en coordonnées            </span>
            x <span style="color: #66cc66;">=</span> index%largeur
            y <span style="color: #66cc66;">=</span> index/largeur
            <span style="color: #808080; font-style: italic;"># On récupère le niveau de gris du pixel concerné</span>
            valeur <span style="color: #66cc66;">=</span> gray.<span style="color: black;">item</span><span style="color: black;">&#40;</span>y<span style="color: #66cc66;">,</span> x<span style="color: black;">&#41;</span>
&nbsp;
            <span style="color: #808080; font-style: italic;"># En fonction de la valeur on coche ou non la case à cocher</span>
            <span style="color: #ff7700;font-weight:bold;">if</span> <span style="color: black;">&#40;</span>valeur <span style="color: #66cc66;">&gt;</span> seuil<span style="color: black;">&#41;</span>:
              checkBox.<span style="color: black;">setCheckState</span><span style="color: black;">&#40;</span><span style="color: #ff4500;">0</span><span style="color: black;">&#41;</span>
            <span style="color: #ff7700;font-weight:bold;">else</span>:
              checkBox.<span style="color: black;">setCheckState</span><span style="color: black;">&#40;</span><span style="color: #ff4500;">2</span><span style="color: black;">&#41;</span>
&nbsp;
            <span style="color: #808080; font-style: italic;"># Les plus attentifs auront remarqué que l'état de la case à cocher ne dépend pas d'un booléen.</span>
            <span style="color: #808080; font-style: italic;"># En effet, il existe un état intermédiaire, le (1), que j'ai appelé l'état de Schrödinger, où </span>
            <span style="color: #808080; font-style: italic;"># la case à cocher est partiellement cochée. Un peu comme si elle était à la fois cochée et non cochée.</span>
            <span style="color: #808080; font-style: italic;"># (Et j'interdis à quiconque d'évoquer, qu'en fait, elle n'est ni l'une, ni l'autre.)</span>
&nbsp;
&nbsp;
<span style="color: #ff7700;font-weight:bold;">if</span> __name__ <span style="color: #66cc66;">==</span> <span style="color: #483d8b;">&quot;__main__&quot;</span>:
      appli <span style="color: #66cc66;">=</span> QtGui.<span style="color: black;">QApplication</span><span style="color: black;">&#40;</span><span style="color: #dc143c;">sys</span>.<span style="color: black;">argv</span><span style="color: black;">&#41;</span>
      ckbx <span style="color: #66cc66;">=</span> CheckBoxVideo<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
      <span style="color: #dc143c;">sys</span>.<span style="color: black;">exit</span><span style="color: black;">&#40;</span>appli.<span style="color: black;">exec_</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span><span style="color: black;">&#41;</span></pre></td></tr></table></div>

<p>Et voici le cheval qui court en cases à cocher :</p>
<p><iframe src="https://player.vimeo.com/video/152516007" width="1170" height="654"frameborder="0" title="Cheval qui court en cases &agrave; cocher." webkitallowfullscreen mozallowfullscreen allowfullscreen></iframe></p>
<p>Je concède qu&#8217;arrivé là, il n&#8217;aurait pas été totalement farfelu d&#8217;ajouter un slider pour gérer le niveau du seuil.<br />
Mais j&#8217;avais un peu peur de rendre le truc utile, vous voyez.<br />
Donc j&#8217;ai laissé en l&#8217;état.</p>
<p>&nbsp;</p>
<h3>Où je conclue.</h3>
<p>Je pense qu&#8217;il est assez clair désormais qu&#8217;il est possible de sauver l&#8217;Humanité en programmant en Python.<br />
Je vous encourage donc à le faire.</p>
<p>À bientôt ou tard.</p>
<p>&nbsp;</p>
<h3>Où j&#8217;ajoute tout de même un bonus.</h3>
<p>Et voici un cheval qui court avec des cases à cocher qui se chevauchent.<br />
Ne me remerciez pas, ça me fait plaisir.</p>
<p><a href="media/095fe799.cheval.gif" data-original-source="http://sametmax.com/wp-content/uploads/2016/01/cheval.gif" class="grouped_elements" rel="tc-fancybox-group17836"><img src="media/095fe799.cheval.gif" data-original-source="http://sametmax.com/wp-content/uploads/2016/01/cheval.gif" alt="cheval" width="120" height="114" class="aligncenter size-full wp-image-17945" /></a></p>


feed_id: http://sametmax.com/?p=17976
title: Comment mocker une coroutine ? &#160; Recently updated !
uri: Comment-mocker-une-coroutine-Recently-updated
url: http://sametmax.com/comment-mocker-une-coroutine/
content:
<p>Dans <a href="http://sametmax.com/un-gros-guide-bien-gras-sur-les-tests-unitaires-en-python-partie-5/">le guide sur les tests</a> en python (que je dois toujours terminer, je sais&#8230;), je vous parle des objets mocks.</p>
<p>Si vous avez eu le plaisir de jouer avec <code>asyncio</code>, vous avez du noter que <code>unittest.mock</code> n&#8217;a aucun outil pour gérer gérer les coroutines.</p>
<p>En attendant que ce soit intégré à la stdlib, voici une petite recette :</p>

<div class="wp_syntax"><table><tr><td class="code"><pre class="python" style="font-family:monospace;"><span style="color: #ff7700;font-weight:bold;">import</span> asyncio
<span style="color: #ff7700;font-weight:bold;">from</span> <span style="color: #dc143c;">unittest</span>.<span style="color: black;">mock</span> <span style="color: #ff7700;font-weight:bold;">import</span> Mock
&nbsp;
<span style="color: #808080; font-style: italic;"># on utilise toute la machinerie du Mock original</span>
<span style="color: #ff7700;font-weight:bold;">class</span> AMock<span style="color: black;">&#40;</span>Mock<span style="color: black;">&#41;</span>:
&nbsp;
&nbsp;
    <span style="color: #ff7700;font-weight:bold;">def</span> <span style="color: #0000cd;">__call__</span><span style="color: black;">&#40;</span><span style="color: #008000;">self</span><span style="color: #66cc66;">,</span> *args<span style="color: #66cc66;">,</span> **kwargs<span style="color: black;">&#41;</span>:
        <span style="color: #808080; font-style: italic;"># sauf qu'à l'appel on créé une fonction coroutine</span>
        <span style="color: #66cc66;">@</span>asyncio.<span style="color: black;">coroutine</span>
        <span style="color: #ff7700;font-weight:bold;">def</span> coro<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>:
            <span style="color: #808080; font-style: italic;"># Qui fait le vrai Mock.__call__ (et donc popule l'historique </span>
            <span style="color: #808080; font-style: italic;"># des appels), mais seulement après l'évent loop l'ait éxécuté</span>
            <span style="color: #008000;">super</span><span style="color: black;">&#40;</span>AMock<span style="color: #66cc66;">,</span> <span style="color: #008000;">self</span><span style="color: black;">&#41;</span>.<span style="color: #0000cd;">__call__</span><span style="color: black;">&#40;</span>*args<span style="color: #66cc66;">,</span> **kwargs<span style="color: black;">&#41;</span>
&nbsp;
        <span style="color: #808080; font-style: italic;"># On appelle la fonction coroutine pour générer une coroutine</span>
        <span style="color: #808080; font-style: italic;"># (les coroutines marchent comme les générateurs)</span>
        <span style="color: #ff7700;font-weight:bold;">return</span> coro<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span></pre></td></tr></table></div>

<p>Je propose qu&#8217;en l&#8217;honneur de ce bidouillage, on l&#8217;appelle&#8230; mockoroutine !</p>
<p>Ca va s&#8217;utiliser comme ça:</p>

<div class="wp_syntax"><table><tr><td class="code"><pre class="python" style="font-family:monospace;">mockorourine <span style="color: #66cc66;">=</span> AMock<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
<span style="color: #ff7700;font-weight:bold;">yield</span> <span style="color: #ff7700;font-weight:bold;">from</span> mockorourine<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span></pre></td></tr></table></div>

<p>Après le <code>yield from</code>, <code>mockorourine.call_count == 1</code>, et <code>mockorourine.assert_called_once_with()</code>passe. </p>
<p>Si vous êtes en 3.5+, on peut même faire:</p>

<div class="wp_syntax"><table><tr><td class="code"><pre class="python" style="font-family:monospace;"><span style="color: #ff7700;font-weight:bold;">class</span> AMock<span style="color: black;">&#40;</span>Mock<span style="color: black;">&#41;</span>:
&nbsp;
    <span style="color: #ff7700;font-weight:bold;">def</span> <span style="color: #0000cd;">__call__</span><span style="color: black;">&#40;</span><span style="color: #008000;">self</span><span style="color: #66cc66;">,</span> *args<span style="color: #66cc66;">,</span> **kwargs<span style="color: black;">&#41;</span>:
        async <span style="color: #ff7700;font-weight:bold;">def</span> coro<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>:
            <span style="color: #008000;">super</span><span style="color: black;">&#40;</span>AMock<span style="color: #66cc66;">,</span> <span style="color: #008000;">self</span><span style="color: black;">&#41;</span>.<span style="color: #0000cd;">__call__</span><span style="color: black;">&#40;</span>*args<span style="color: #66cc66;">,</span> **kwargs<span style="color: black;">&#41;</span>
        <span style="color: #ff7700;font-weight:bold;">return</span> coro<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
&nbsp;
    <span style="color: #ff7700;font-weight:bold;">def</span> __await__<span style="color: black;">&#40;</span><span style="color: #008000;">self</span><span style="color: black;">&#41;</span>:
        <span style="color: #808080; font-style: italic;"># on delegue le await à la couroutine créée par __call__</span>
        <span style="color: #ff7700;font-weight:bold;">return</span> <span style="color: #008000;">self</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>.__await__<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span></pre></td></tr></table></div>

<p>Puis:</p>

<div class="wp_syntax"><table><tr><td class="code"><pre class="python" style="font-family:monospace;">await AMock<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span></pre></td></tr></table></div>


feed_id: http://sametmax.com/?p=17985
title: Installer des packages depuis le shell Python &#160; Recently updated !
uri: Installer-des-packages-depuis-le-shell-Python-Recently-updated
url: http://sametmax.com/installer-des-packages-depuis-le-shell-python/
content:
<p>J&#8217;améliore tout le temps mon <a href="http://sametmax.com/nouveau-fichier-de-start-up-python/">fichier de démarrage de Python</a>. </p>
<p>Aujourd&#8217;hui je lui ai rajouté une petite fonction pour installer des packages avec pip depuis le shell. En effet, souvent il m&#8217;arrive de le lancer, puis d&#8217;importer un truc qui n&#8217;est pas là. Alors il faut sortir, installer, revenir. </p>
<p>Chiant.</p>
<p>Du coup :</p>

<div class="wp_syntax"><table><tr><td class="code"><pre class="python" style="font-family:monospace;"><span style="color: #ff7700;font-weight:bold;">def</span> pip_install<span style="color: black;">&#40;</span>*packages<span style="color: black;">&#41;</span>:
    <span style="color: #483d8b;">&quot;&quot;&quot; Install packages diretly in the shell &quot;&quot;&quot;</span>
    <span style="color: #ff7700;font-weight:bold;">for</span> name <span style="color: #ff7700;font-weight:bold;">in</span> packages:
        <span style="color: #dc143c;">cmd</span> <span style="color: #66cc66;">=</span> <span style="color: black;">&#91;</span><span style="color: #dc143c;">sys</span>.<span style="color: black;">executable</span><span style="color: #66cc66;">,</span> <span style="color: #483d8b;">'-m'</span><span style="color: #66cc66;">,</span> <span style="color: #483d8b;">'pip'</span><span style="color: #66cc66;">,</span> <span style="color: #483d8b;">'install'</span><span style="color: #66cc66;">,</span> name<span style="color: black;">&#93;</span>
        <span style="color: #ff7700;font-weight:bold;">if</span> <span style="color: #ff7700;font-weight:bold;">not</span> <span style="color: #008000;">hasattr</span><span style="color: black;">&#40;</span><span style="color: #dc143c;">sys</span><span style="color: #66cc66;">,</span> <span style="color: #483d8b;">'real_prefix'</span><span style="color: black;">&#41;</span>:
            <span style="color: #808080; font-style: italic;"># Si pas dans un virtual env, install juste</span>
            <span style="color: #808080; font-style: italic;"># dans le dossier utilisateur</span>
            <span style="color: #dc143c;">cmd</span>.<span style="color: black;">append</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'--user'</span><span style="color: black;">&#41;</span>
        <span style="color: #dc143c;">subprocess</span>.<span style="color: black;">call</span><span style="color: black;">&#40;</span><span style="color: #dc143c;">cmd</span><span style="color: black;">&#41;</span></pre></td></tr></table></div>

<p>Et dans le shell :</p>

<div class="wp_syntax"><table><tr><td class="code"><pre class="python" style="font-family:monospace;"><span style="color: #66cc66;">&gt;&gt;&gt;</span> pip_install<span style="color: black;">&#40;</span><span style="color: #483d8b;">'arrow'</span><span style="color: #66cc66;">,</span> <span style="color: #483d8b;">'requests'</span><span style="color: #66cc66;">,</span> <span style="color: #483d8b;">'minibelt'</span><span style="color: black;">&#41;</span>
<span style="color: #66cc66;">&gt;&gt;&gt;</span> <span style="color: #ff7700;font-weight:bold;">import</span> arrow</pre></td></tr></table></div>


feed_id: http://sametmax.com/?p=17993
title: Les plans à quatre ne sont pas mystérieux
uri: Les-plans-à-quatre-ne-sont-pas-mystérieux
url: http://sametmax.com/les-plans-a-quatre-ne-sont-pas-mysterieux/
content:
<p>Avec <a href="http://sametmax.com/comment-arrivent-les-plans-a-3/">Comment arrivent les plans à 3</a> et <a href="http://sametmax.com/le-sperme-ca-attache-au-fond/">Le sperme ça attache au fond</a>, je vous avais déjà montré que les expériences sexuelles qu’on qualifie d’inhabituelles n’arrivent pas suite à des rituels d’incantations mystiques.</p>
<p>Il en va de même pour le plan à quatre.</p>
<p>Ce soir-là je raccompagne chez elle une demoiselle très expérimentée dans le domaine de la coquinerie. Arrivés devant le portail, l’attendent deux de ses amis qui, et c’est important, ne s’étaient pas concertés pour venir. L’un amène l’apéritif.</p>
<p>À ce stade-là, le quatuor en rut majeur n’est pas programmé.</p>
<p>Néanmoins, je connaissais les personnes. Je savais qu’elles étaient sexuellement à l’aise et avaient déjà fait des choses de ce genre: strip-poker, striptease, plan à trois&#8230; Leurs histoires sont connues, et certaines avaient couché ensemble. </p>
<p>J’ai donc mentalement pris note qu’un travail d’équipe était possible.</p>
<p>Nous avons servi quelques verres, sur des discussions anodines.</p>
<p>La maitresse des lieux annonce qu’elle a mal aux pieds, et me demande un massage, activité pour laquelle tout mon entourage sait que je suis très qualifié. On concocte nos propres huiles dans la famille.</p>
<p>Maitresse coquine partage son enthousiasme à haute voix, et invite sa consœur à essayer. Je change donc de pieds, et j’y vois une opportunité.</p>
<p>Je propose ainsi au 4e de profiter de l’occasion pour apprendre quelques mouvements afin de ne pas laisser les orteils de ma cliente précédente refroidir.</p>
<p>Là on a une bonne base : 4 adultes consentants et expérimentés, dans l’intimité d’un appartement, ayant bu, se tripotant les uns les autres.</p>
<p>Parti de là, le reste n’est que gamineries, littéralement. Fausses excuses pour se toucher plus haut, « t’es pas cap » et autre « allezzzzzz ». J’embrasse l’une. On pousse un peu les filles à s’embrasser.</p>
<p>C’est très peu subtil, personne n’est dupe, mais c’est la nature humaine : on ne peut pas juste dire « allez on s’encule ! ». Il faut un prétexte, fût-il bidon.</p>
<p>L’un fera celle qui est un peu gênée.</p>
<p>L’autre fera celui qui est un peu étonné.</p>
<p>Et c’est tout.</p>
<p>Notez encore une fois le contexte habituel :</p>
<ul>
<li>Des gens à l’aise avec le sexe. C’est la moitié du taff. Il n’y en a pas tant que ça. Votre sexualité dépend éminemment des gens que vous fréquentez. Vous voulez peut être faire un gang bang avec votre bonasse de voisine de pallier, mais si elle est du genre à pas oser parler de son vibro, ce n’est pas la peine. Sauf si vous pouvez expliquer à la police ce que fait tout ce GHB dans le tiroir de votre salle de bain.</li>
<li>On escalade petit à petit, et on utilise des prétextes. Oui c’est nul. Oui c’est hypocrite et enfantin. Non il n’y a pas moyen de le contourner. Dans la communauté gay, c’est possible. Dans la communauté nudiste aussi. Mais dans un groupe hétérogène, ça fait parti des conventions sociales.</li>
<li>Un cadre intime: les acteurs doivent avoir l’impression qu’on ne peut pas les voir ou les déranger.</li>
<li>De l’alcool. Ça aide. Mais pas indispensable.</li>
<li>On accepte que les gens puissent dire non. C’est une des choses les plus difficiles. C’est la même chose en drague. À un moment, on fait connaitre ses intentions, et la personne peut dire non. Et là votre égo, et surtout votre réputation peut en prendre un coup. Pas de baise sans prise de risque. Risque d’autant diminué que vous êtes en compagnie de gens expérimentés qui ne vous reprocheront pas d’essayer. Alors que la personne prude vous taillera un costard. Retour au point 1.</li>
</ul>
<p>Bref, ça baise, ça suce, ça lèche, ça caresse. Tout le monde est très gentil, sourit beaucoup. Un des acteurs a du mal à bander. C’est normal. Ces plans à plusieurs foutent la pression, et ça arrive régulièrement. Il faut rassurer la personne, et proposer des activités qui n’impliquent pas de pénétration en attendant.</p>
<p>Et là vous voyez, pas de mystère. Pas de secrets incroyables.</p>
<p>Je vous le répète : le sexe, c’est la dinette des adultes.</p>


feed_id: http://sametmax.com/?p=17997
title: Paramètres sympas pour pytest &#160; Recently updated !
uri: Paramètres-sympas-pour-pytest-Recently-updated
url: http://sametmax.com/parametres-sympas-pour-pytest/
content:
<p>En Python, <a href="http://sametmax.com/un-gros-guide-bien-gras-sur-les-tests-unitaires-en-python-partie-3/">pas la peine de faire des tests sans pytest</a>.</p>
<p>Et avec quelques options, pytest devient encore plus pratique:</p>

<div class="wp_syntax"><table><tr><td class="code"><pre class="bash" style="font-family:monospace;">py.test <span style="color: #660033;">-vv</span> <span style="color: #660033;">--capture</span>=no <span style="color: #660033;">--showlocals</span> <span style="color: #660033;">--exitfirst</span></pre></td></tr></table></div>

<p>Détaillons.</p>
<p><code>-vv</code> déclenche le mode extra-verbeux, qui affiche le nom de chaque test qui passe ainsi que plein de détails en cas d&#8217;échec.</p>
<p><code>--capture=no</code> permet d&#8217;utiliser <code>print()</code> et <code>pdb</code> dans vos tests unitaires.</p>
<p><code>--showlocals</code> affiche les variables locales de tout test qui échoue.</p>
<p><code>--exitfirst</code> arrête les tests dès le premier échec plutôt que de faire toute la liste.</p>
<p>Grace au paramètre <code>addopts</code>, vous pouvez <a href="http://sametmax.com/parametres-par-defaut-pour-la-commande-py-test/">passez ces paramètres par défaut</a> à py.test.</p>
<p>Soit par la variable d&#8217;environnement <code>PYTEST_ADDOPTS</code>, par exemple en mettant dans votre bashrc:</p>

<div class="wp_syntax"><table><tr><td class="code"><pre class="bash" style="font-family:monospace;"><span style="color: #7a0874; font-weight: bold;">export</span> <span style="color: #007800;">PYTEST_ADDOPTS</span>=<span style="color: #ff0000;">&quot;-vv --capture=no --showlocals --exitfirst&quot;</span></pre></td></tr></table></div>

<p>Ou en mettant dans le fichier de config (comme tox.ini)</p>
<pre>[pytest]
addopts = -vv --capture=no --showlocals --exitfirst</pre>


feed_id: http://sametmax.com/?p=18009
title: Evolution de Python 2 &#160; Recently updated !
uri: Evolution-de-Python-2-Recently-updated
url: http://sametmax.com/evolution-de-python/
content:
<p>La communauté Python est assez d’accord ces derniers temps. Maintenant que le plus gros de la débâcle Python2/3 est derrière nous (en attendant le contre coup des retardataires de 2020) et qu’on a un modèle d’IO async bien clean, les trucs qui fâchent sont assez clairement délimités:</p>
<ul>
<li>Le packaging.</li>
<li>Le multi-CPU.</li>
<li>Les perfs.</li>
</ul>
<p>Sur ces questions, du travail est activement en cours.</p>
<p>Pour le multi CPU, un modèle propre permettra d’utiliser plusieurs interpréteurs en parallèle en partageant des valeurs<br />
sans avoir à les sérialiser.</p>
<p>Pour les perfs, ce sera un taf plus long, mais:</p>
<ul>
<li>Les opérations sur les bytes ont <a href="https://docs.python.org/3.6/whatsnew/3.6.html#optimizations">des perfs bien améliorées</a> pour la 3.6.</li>
<li>Victor Stinner et Yury Selivan travaillent sur des trucs très cools. Jetez un coup d’œil aux <a href="https://www.python.org/community/lists/">mailling lists</a> sur les sujets &#8220;FAT Python&#8221;, &#8220;PEP 509/510/511&#8243; et &#8220;Explicit variable cappture list&#8221; pour voir tout un tas d’optimisations prévues, ou permettant d’en prévoir.</li>
<li><a href="https://github.com/Microsoft/Pyjion">Pyjion</a>, la JIT de Microsoft, un des projets les plus prometteurs pour accélérer Python, commence à faire parler d&#8217;elle.</li>
<li>Dropbox, chez qui travaille Guido Van Rossum, bosse sur <a href="https://github.com/dropbox/pyston">son implémentation de Python également</a>.</li>
<li>Et bien entendu Pypy continue son bonhomme de chemin.</li>
</ul>
<p>Bref, y a du potentiel.</p>
<p>Pour le packaging, <a href="https://t.co/KzRwV7PoW8">les wheels vont enfin arriver sous Linux</a>, ce qui fait qu’on pourra bientôt pip installer des binaires partout. nuikta, le compilateur Python, <a href="http://nuitka.net/">supporte maintenant await/async</a>.</p>
<p>On est sur la bonne route.</p>
<p>L’année 2016 va être trop cool, et dans mon enthousiasme, j’aimerais écrire à propos de choses que j’aimerais vraiment voir arriver dans Python.</p>
<h2>try/except error inline</h2>
<p>Beaucoup de codes en Python ressemblent à ça :</p>

<div class="wp_syntax"><table><tr><td class="code"><pre class="python" style="font-family:monospace;"><span style="color: #ff7700;font-weight:bold;">try</span>:
    val <span style="color: #66cc66;">=</span> faire un truc
<span style="color: #ff7700;font-weight:bold;">except</span> MonErrorALaNoix:
    val <span style="color: #66cc66;">=</span> <span style="color: #483d8b;">&quot;valeur par default&quot;</span></pre></td></tr></table></div>

<p>Par exemple :</p>

<div class="wp_syntax"><table><tr><td class="code"><pre class="python" style="font-family:monospace;"><span style="color: #ff7700;font-weight:bold;">try</span>:
    val <span style="color: #66cc66;">=</span> stuff<span style="color: black;">&#91;</span>index<span style="color: black;">&#93;</span>
<span style="color: #ff7700;font-weight:bold;">except</span> <span style="color: black;">&#40;</span><span style="color: #008000;">IndexError</span><span style="color: #66cc66;">,</span> <span style="color: #008000;">TypeError</span><span style="color: black;">&#41;</span>:
    val <span style="color: #66cc66;">=</span> <span style="color: #008000;">None</span></pre></td></tr></table></div>

<p>Ce sont des opérations si courantes qu’on a plein de raccourcis comme <code>dict.get</code> ou <code>next(i, None)</code>. En effet, en Python <code>try</code>/<code>except</code> n’est pas juste un mécanisme de gestion d’erreur, <strong>c’est un mécanisme de contrôle de flux à part entière.</strong></p>
<p>Car franchement, ça fait chier de se taper 4 lignes pour écrire ça. En effet, on a bien les expressions ternaires pour les <code>if</code>/<code>else</code>:</p>

<div class="wp_syntax"><table><tr><td class="code"><pre class="python" style="font-family:monospace;">val <span style="color: #66cc66;">=</span> truc <span style="color: #ff7700;font-weight:bold;">if</span> bidule <span style="color: #ff7700;font-weight:bold;">else</span> machine</pre></td></tr></table></div>

<p>Et bien il exist <a href="https://www.python.org/dev/peps/pep-0463/">un PEP</a> (rejeté) qui propose ça:</p>

<div class="wp_syntax"><table><tr><td class="code"><pre class="python" style="font-family:monospace;">val <span style="color: #66cc66;">=</span> faire un truc <span style="color: #ff7700;font-weight:bold;">except</span> MonErrorALaNoix: <span style="color: #483d8b;">&quot;valeur par default&quot;</span></pre></td></tr></table></div>

<p>J’adore. C’est pratique, générique, propre.</p>
<p>Bien entendu ça peut être abusé, comme les expressions ternaires, pour faire de la merde illisible. Mais j&#8217;ai rarement vu le cas pour les précédentes, donc ça devrait aller.</p>
<h2>slices sur les générateurs</h2>
<p>Les générateurs, c’est formidable. C’est iterable. On peut les utiliser partout où on utilise les listes.</p>
<p>Sauf si on doit les limiter en taille.</p>
<p>Alors là, c’est relou.</p>
<p>Par exemple, récupérer les carrés des nombres pairs entre 0 et 100, puis limiter le tout a 10 éléments après les 5e:</p>

<div class="wp_syntax"><table><tr><td class="code"><pre class="python" style="font-family:monospace;"><span style="color: #ff7700;font-weight:bold;">from</span> <span style="color: #dc143c;">itertools</span> <span style="color: #ff7700;font-weight:bold;">import</span> islice
g <span style="color: #66cc66;">=</span> <span style="color: black;">&#40;</span>x * x <span style="color: #ff7700;font-weight:bold;">for</span> x <span style="color: #ff7700;font-weight:bold;">in</span> <span style="color: #008000;">range</span><span style="color: black;">&#40;</span><span style="color: #ff4500;">100</span><span style="color: black;">&#41;</span> <span style="color: #ff7700;font-weight:bold;">if</span> x % <span style="color: #ff4500;">2</span> <span style="color: #66cc66;">==</span> <span style="color: #ff4500;">0</span><span style="color: black;">&#41;</span>
g <span style="color: #66cc66;">=</span> islice<span style="color: black;">&#40;</span>g<span style="color: #66cc66;">,</span> <span style="color: #ff4500;">5</span><span style="color: #66cc66;">,</span> <span style="color: #ff4500;">15</span><span style="color: black;">&#41;</span></pre></td></tr></table></div>

<p>Ca serait tellement plus simple de pouvoir faire:</p>

<div class="wp_syntax"><table><tr><td class="code"><pre class="python" style="font-family:monospace;">g <span style="color: #66cc66;">=</span> <span style="color: black;">&#40;</span>x * x <span style="color: #ff7700;font-weight:bold;">for</span> x <span style="color: #ff7700;font-weight:bold;">in</span> <span style="color: #008000;">range</span><span style="color: black;">&#40;</span><span style="color: #ff4500;">100</span><span style="color: black;">&#41;</span> <span style="color: #ff7700;font-weight:bold;">if</span> x % <span style="color: #ff4500;">2</span> <span style="color: #66cc66;">==</span> <span style="color: #ff4500;">0</span><span style="color: black;">&#41;</span><span style="color: black;">&#91;</span><span style="color: #ff4500;">5</span>:<span style="color: #ff4500;">10</span><span style="color: black;">&#93;</span></pre></td></tr></table></div>

<h2>callable dans les slices</h2>
<p>Si vous voulez tous les carrés des nombres au-dessus de 5, vous pouvez faire:</p>

<div class="wp_syntax"><table><tr><td class="code"><pre class="python" style="font-family:monospace;"><span style="color: black;">&#40;</span>x * x <span style="color: #ff7700;font-weight:bold;">for</span> x <span style="color: #ff7700;font-weight:bold;">in</span> numbres <span style="color: #ff7700;font-weight:bold;">if</span> x &amp;gt<span style="color: #66cc66;">;</span> <span style="color: #ff4500;">5</span><span style="color: black;">&#41;</span></pre></td></tr></table></div>

<p>Mais si vous voulez tous les nombres à partir du moment où vous rencontrez un nombre au-dessus de 5 ?</p>

<div class="wp_syntax"><table><tr><td class="code"><pre class="python" style="font-family:monospace;"><span style="color: #ff7700;font-weight:bold;">from</span> <span style="color: #dc143c;">itertools</span> <span style="color: #ff7700;font-weight:bold;">import</span> dropwhile
numbers <span style="color: #66cc66;">=</span> dropwhile<span style="color: black;">&#40;</span><span style="color: #ff7700;font-weight:bold;">lambda</span> x: x &amp;gt<span style="color: #66cc66;">;</span> <span style="color: #ff4500;">5</span><span style="color: #66cc66;">,</span> numbers<span style="color: black;">&#41;</span>
<span style="color: black;">&#40;</span>x * x <span style="color: #ff7700;font-weight:bold;">for</span> x <span style="color: #ff7700;font-weight:bold;">in</span> numbres<span style="color: black;">&#41;</span></pre></td></tr></table></div>

<p>Alors certes, je ne suis pas pour transformer Python en Haskel et balancer des formules magiques comme:</p>

<div class="wp_syntax"><table><tr><td class="code"><pre class="python" style="font-family:monospace;"><span style="color: black;">&#40;</span>x * x <span style="color: #ff7700;font-weight:bold;">for</span> x <span style="color: #ff7700;font-weight:bold;">in</span> numbers<span style="color: black;">&#91;</span>a -&amp;gt<span style="color: #66cc66;">;</span> a &amp;gt<span style="color: #66cc66;">;</span> <span style="color: #ff4500;">5</span><span style="color: black;">&#93;</span><span style="color: black;">&#41;</span></pre></td></tr></table></div>

<p>Mais juste m’éviter l’import et pouvoir faire ça:</p>

<div class="wp_syntax"><table><tr><td class="code"><pre class="python" style="font-family:monospace;"><span style="color: #ff7700;font-weight:bold;">def</span> start<span style="color: black;">&#40;</span>x<span style="color: black;">&#41;</span>:
    <span style="color: #ff7700;font-weight:bold;">return</span> x &amp;gt<span style="color: #66cc66;">;</span> <span style="color: #ff4500;">5</span>
<span style="color: black;">&#40;</span>x * x <span style="color: #ff7700;font-weight:bold;">for</span> x <span style="color: #ff7700;font-weight:bold;">in</span> numbers<span style="color: black;">&#91;</span>start:<span style="color: black;">&#93;</span><span style="color: black;">&#41;</span></pre></td></tr></table></div>

<p>Ca serait cool.</p>
<h2>with var dans les expressions génératrices</h2>
<p>Je suis hésitant sur cette feature car c’est très tentant de faire de one liners trop longs avec, mais:</p>

<div class="wp_syntax"><table><tr><td class="code"><pre class="python" style="font-family:monospace;"><span style="color: black;">&#40;</span>x.<span style="color: black;">split</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span><span style="color: black;">&#91;</span><span style="color: #ff4500;">1</span><span style="color: black;">&#93;</span> <span style="color: #ff7700;font-weight:bold;">for</span> x <span style="color: #ff7700;font-weight:bold;">in</span> truc <span style="color: #ff7700;font-weight:bold;">if</span> x.<span style="color: black;">split</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span><span style="color: black;">&#91;</span><span style="color: #ff4500;">1</span><span style="color: black;">&#93;</span> <span style="color: #66cc66;">==</span> <span style="color: #ff4500;">1</span><span style="color: black;">&#41;</span></pre></td></tr></table></div>

<p>C’est con de faire 2 fois <code>split</code> quand même.</p>

<div class="wp_syntax"><table><tr><td class="code"><pre class="python" style="font-family:monospace;"><span style="color: black;">&#40;</span>y <span style="color: #ff7700;font-weight:bold;">for</span> x <span style="color: #ff7700;font-weight:bold;">in</span> truc 
   <span style="color: #ff7700;font-weight:bold;">with</span> x.<span style="color: black;">split</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span><span style="color: black;">&#91;</span><span style="color: #ff4500;">1</span><span style="color: black;">&#93;</span> <span style="color: #ff7700;font-weight:bold;">as</span> y 
   <span style="color: #ff7700;font-weight:bold;">if</span> y <span style="color: #66cc66;">==</span> <span style="color: #ff4500;">1</span><span style="color: black;">&#41;</span></pre></td></tr></table></div>

<p>Bon, ça peut rapidement devenir illisible, donc à méditer.</p>
<h2>async optionnel</h2>
<p>Je cherche toujours à comprendre pourquoi <code>async</code> est nécessaire.</p>
<p>Avec les générateurs, la présence de <code>yield</code> fait détecter automatiquement la fonction comme fonction génératrice.</p>
<p>Avec <code>await</code>, ça devrait être pareil:</p>

<div class="wp_syntax"><table><tr><td class="code"><pre class="python" style="font-family:monospace;"><span style="color: #ff7700;font-weight:bold;">def</span> stuff<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>:
    await bidule <span style="color: #808080; font-style: italic;"># bim, c’est une coroutine !</span></pre></td></tr></table></div>

<p>Pas besoin de <code>async</code>. Si on veut faire une coroutine sans un seul <code>await</code>, il y a toujours <code>@asyncio.coroutine</code>.</p>
<p><code>async</code> reste très utile pour <code>async for</code> et <code>async with</code>, mais perso j’aurais préféré avoir un await with et un await for et pas de <code>async</code>.</p>
<p>Après, le prenez pas mal hein. J’adore <code>async</code>/<code>await</code>. Vive <code>asyncio</code> ! Mais la syntaxe pourrait être plus naturelle, plus proche du comportement des générateurs, d&#8217;autant que ça se base dessus.</p>


feed_id: http://sametmax.com/?p=18018
title: Nouvelle release de crossbar: historique des events et crypto
uri: Nouvelle-release-de-crossbar-historique-des-events-et-crypto
url: http://sametmax.com/nouvelle-release-de-crossbar-historique-des-events-et-crypto/
content:
<p>Je suis méga à la bourre. Entre <a href="https://github.com/sametmax/0bin/">les tickets github qui s’accumulent</a>, <a href="http://sametmax.com/un-petit-dashboard-de-monitoring-avec-django-et-wamp/#comment-174036">les comments auxquels j’ai toujours pas répondu</a> et <a href="http://sametmax.com/129-drafts/">la liste d’articles à écrire</a> qui augmente au lieu de diminuer (mais comment, bordel, comment ?), j’ai vraiment du mal à suivre.</p>
<p>Je dois toujours un article sur Polymer + Crossbar à Tavendo. Et il faut que je fasse un tuto sur l’authentification également (en attendant, <a href="https://github.com/crossbario/crossbarexamples/tree/master/authentication">y a des exemples plus à jour</a>). </p>
<p>Fichtre.</p>
<p>En attendant, je vais en profiter pour faire un article vite fait sur la dernière release, puisque <a href="http://sametmax.com/crossbar-le-futur-des-applications-web-python/">Crossbar</a>, le routeur WAMP, <a href="https://groups.google.com/forum/#!topic/autobahnws/R86_F8jd17s">passe en 0.12</a>.</p>
<p>Comme d’hab, correction de bugs, amélioration du support de Python 3, plus de docs et d’exemples, blablabla&#8230;</p>
<p>Mais ce qui est vraiment intéressant, <a href="https://github.com/crossbario/crossbarexamples/tree/master/event-history">c’est l’historique des évènements</a>. </p>
<p>Normalement un évènement est éphémère, dans le sens où une fois qu’il a été propagé, vous ne pouvez plus le recevoir. Si vous arrivez après la fête, c’est terminé.</p>
<p>C’est un problème, par exemple si vous redémarrez un client qui a besoin de ces évènements. Ou si vous implémentez un client qui veut savoir ce qui vient de se passer avant de se pointer, comme dans le cas d’un chat : on veut avoir les derniers messages postés.</p>
<p>Par défaut, l’historique n’est pas activé, puisqu’il y un cout pour chaque pub/sub. On doit explicitement le demander pour chaque event dans le fichier de config :</p>

<div class="wp_syntax"><table><tr><td class="code"><pre class="javascript" style="font-family:monospace;"><span style="color: #009900;">&#123;</span>
   <span style="color: #3366CC;">&quot;name&quot;</span><span style="color: #339933;">:</span> <span style="color: #3366CC;">&quot;realm1&quot;</span><span style="color: #339933;">,</span>
   <span style="color: #3366CC;">&quot;roles&quot;</span><span style="color: #339933;">:</span> <span style="color: #009900;">&#91;</span>
   <span style="color: #009900;">&#93;</span><span style="color: #339933;">,</span>
   <span style="color: #3366CC;">&quot;store&quot;</span><span style="color: #339933;">:</span> <span style="color: #009900;">&#123;</span>
      <span style="color: #3366CC;">&quot;type&quot;</span><span style="color: #339933;">:</span> <span style="color: #3366CC;">&quot;memory&quot;</span><span style="color: #339933;">,</span> # ou stocker l<span style="color: #3366CC;">'historique
      &quot;event-history&quot;: [
         {
            &quot;uri&quot;: &quot;mon.uri.pour.un.event&quot;, # quel type event
            &quot;limit&quot;: 10000 # combien d’events stocker
         }
      ]
   }
}</span></pre></td></tr></table></div>

<p><code>type</code> n’accepte pour le moment que <code>memory</code>, qui est une simple liste en mémoire dans crossbar, et bouffe donc de la RAM. On perd aussi l’historique au redémarrage du routeur, mais ça à l’avantage d’être très rapide.</p>
<p>Pour la prochaine version, Tavendo va implémenter un stockage dans une base <code>lmdb</code> et si ils font une belle API, on peut s’attendre à voir fleurir des backends pour SQLAlchemy, Django, Redis, etc.</p>
<p><code>event-history</code> prend un liste d’events (les URIs peuvent utiliser <a href="http://sametmax.com/pendant-ce-temps-a-vera-cruz/">les jokers introduits dans la version précédente</a>), on met la limite du nombre total d’events à stocker pour cet URI en particulier.</p>
<p>Pour profiter de l’historique côté client, il faut obligatoirement avoir un abonnement à un event dont les messages sont sauvegardés. On ne peut pas récupérer l’historique de messages auxquels ont est pas abonnés : forcer l’abonnement oblige en effet le client à passer le check des permissions.</p>
<p>Par exemple, en JS, d’abord on s’inscrit:</p>

<div class="wp_syntax"><table><tr><td class="code"><pre class="python" style="font-family:monospace;">var promise <span style="color: #66cc66;">=</span> session.<span style="color: black;">subscribe</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'mon.uri.pour.un.event'</span><span style="color: #66cc66;">,</span>
   function <span style="color: black;">&#40;</span>args<span style="color: #66cc66;">,</span> kwargs<span style="color: #66cc66;">,</span> details<span style="color: black;">&#41;</span> <span style="color: black;">&#123;</span>
      // bon là vous faites bien ce que vous voulez avec les nouveaux events
      //<span style="color: #66cc66;">,</span> car ça n’a rien à voir avec l’historique
   <span style="color: black;">&#125;</span>
<span style="color: black;">&#41;</span></pre></td></tr></table></div>

<p>Puis on demande les events:</p>

<div class="wp_syntax"><table><tr><td class="code"><pre class="python" style="font-family:monospace;">promise <span style="color: #66cc66;">=</span> promise.<span style="color: black;">then</span><span style="color: black;">&#40;</span>function <span style="color: black;">&#40;</span>sub<span style="color: black;">&#41;</span> <span style="color: black;">&#123;</span>
      // L’abonnement retourne un objet <span style="color: #483d8b;">&quot;subcription&quot;</span> qui possède l’<span style="color: #008000;">id</span>
      // dont on a besoin pour demander l’historique des events.
      // On fait un petit RPC sur la meta API <span style="color: #483d8b;">'wamp.subscription.get_events'</span>
      // qui demande aux routeurs tous les X derniers events qui matchent
      // notre abo. <span style="color: black;">Ici</span> x <span style="color: #66cc66;">=</span> <span style="color: #ff4500;">10</span>
      <span style="color: #ff7700;font-weight:bold;">return</span> session.<span style="color: black;">call</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'wamp.subscription.get_events'</span><span style="color: #66cc66;">,</span> <span style="color: black;">&#91;</span>sub.<span style="color: #008000;">id</span><span style="color: #66cc66;">,</span> <span style="color: #ff4500;">10</span><span style="color: black;">&#93;</span><span style="color: black;">&#41;</span><span style="color: #66cc66;">;</span>
<span style="color: black;">&#41;</span></pre></td></tr></table></div>

<p>Et enfin, on a droit à l’histo:</p>

<div class="wp_syntax"><table><tr><td class="code"><pre class="python" style="font-family:monospace;">promise.<span style="color: black;">then</span><span style="color: black;">&#40;</span>function <span style="color: black;">&#40;</span>history<span style="color: black;">&#41;</span> <span style="color: black;">&#123;</span>
    console.<span style="color: black;">log</span><span style="color: black;">&#40;</span>history.<span style="color: black;">length</span><span style="color: #66cc66;">,</span> <span style="color: #483d8b;">&quot; events:&quot;</span><span style="color: black;">&#41;</span><span style="color: #66cc66;">;</span>
    history.<span style="color: black;">forEach</span><span style="color: black;">&#40;</span>function<span style="color: black;">&#40;</span>event<span style="color: black;">&#41;</span><span style="color: black;">&#123;</span>
        console.<span style="color: black;">log</span><span style="color: black;">&#40;</span>event.<span style="color: black;">timestamp</span><span style="color: #66cc66;">,</span> event.<span style="color: black;">publication</span><span style="color: #66cc66;">,</span> event.<span style="color: black;">args</span><span style="color: black;">&#91;</span><span style="color: #ff4500;">0</span><span style="color: black;">&#93;</span><span style="color: black;">&#41;</span><span style="color: #66cc66;">;</span>
    <span style="color: black;">&#125;</span><span style="color: black;">&#41;</span>
 <span style="color: black;">&#125;</span><span style="color: black;">&#41;</span><span style="color: #66cc66;">;</span></pre></td></tr></table></div>

<p> En Python, le code pour récupérer l’histo est logiquement:</p>

<div class="wp_syntax"><table><tr><td class="code"><pre class="python" style="font-family:monospace;"><span style="color: #ff7700;font-weight:bold;">import</span> asyncio
&nbsp;
<span style="color: #ff7700;font-weight:bold;">from</span> <span style="color: #dc143c;">os</span> <span style="color: #ff7700;font-weight:bold;">import</span> environ
<span style="color: #ff7700;font-weight:bold;">from</span> autobahn.<span style="color: black;">asyncio</span>.<span style="color: black;">wamp</span> <span style="color: #ff7700;font-weight:bold;">import</span> ApplicationSession<span style="color: #66cc66;">,</span> ApplicationRunner
&nbsp;
&nbsp;
<span style="color: #ff7700;font-weight:bold;">class</span> Component<span style="color: black;">&#40;</span>ApplicationSession<span style="color: black;">&#41;</span>:
&nbsp;
    async <span style="color: #ff7700;font-weight:bold;">def</span> onJoin<span style="color: black;">&#40;</span><span style="color: #008000;">self</span><span style="color: #66cc66;">,</span> details<span style="color: black;">&#41;</span>:
&nbsp;
&nbsp;
        <span style="color: #ff7700;font-weight:bold;">def</span> on_event<span style="color: black;">&#40;</span>i<span style="color: black;">&#41;</span>:
            <span style="color: #ff7700;font-weight:bold;">print</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">&quot;Got: {}&quot;</span>.<span style="color: black;">format</span><span style="color: black;">&#40;</span>i<span style="color: black;">&#41;</span><span style="color: black;">&#41;</span>
&nbsp;
        <span style="color: #808080; font-style: italic;"># pareil on shoppe la subscription</span>
        sub <span style="color: #66cc66;">=</span> await <span style="color: #008000;">self</span>.<span style="color: black;">subscribe</span><span style="color: black;">&#40;</span>on_event<span style="color: #66cc66;">,</span> u<span style="color: #483d8b;">'mon.uri.pour.un.event'</span><span style="color: black;">&#41;</span>
        <span style="color: #808080; font-style: italic;"># et on demande la liste des 10 derniers events pour cet abo</span>
        events <span style="color: #66cc66;">=</span> await <span style="color: #008000;">self</span>.<span style="color: black;">call</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'wamp.subscription.get_events'</span><span style="color: #66cc66;">,</span> sub.<span style="color: #008000;">id</span><span style="color: #66cc66;">,</span> <span style="color: #ff4500;">10</span><span style="color: black;">&#41;</span>
        <span style="color: #808080; font-style: italic;"># et on boucle. Et on kiff await parceque putain c’est pratique.</span>
        <span style="color: #ff7700;font-weight:bold;">for</span> event <span style="color: #ff7700;font-weight:bold;">in</span> events:
            <span style="color: #ff7700;font-weight:bold;">print</span><span style="color: black;">&#40;</span>event<span style="color: black;">&#91;</span><span style="color: #483d8b;">'timestamp'</span><span style="color: black;">&#93;</span><span style="color: #66cc66;">,</span> event<span style="color: black;">&#91;</span><span style="color: #483d8b;">'publication'</span><span style="color: black;">&#93;</span><span style="color: #66cc66;">,</span> event<span style="color: black;">&#91;</span><span style="color: #483d8b;">'args'</span><span style="color: black;">&#93;</span><span style="color: black;">&#91;</span><span style="color: #ff4500;">0</span><span style="color: black;">&#93;</span><span style="color: black;">&#41;</span>
&nbsp;
    <span style="color: #ff7700;font-weight:bold;">def</span> onDisconnect<span style="color: black;">&#40;</span><span style="color: #008000;">self</span><span style="color: black;">&#41;</span>:
        asyncio.<span style="color: black;">get_event_loop</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>.<span style="color: black;">stop</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
&nbsp;
&nbsp;
<span style="color: #ff7700;font-weight:bold;">if</span> __name__ <span style="color: #66cc66;">==</span> <span style="color: #483d8b;">'__main__'</span>:
    runner <span style="color: #66cc66;">=</span> ApplicationRunner<span style="color: black;">&#40;</span><span style="color: #483d8b;">&quot;ws://127.0.0.1:8080/ws&quot;</span><span style="color: #66cc66;">,</span> <span style="color: #483d8b;">'realm1'</span><span style="color: black;">&#41;</span>
    runner.<span style="color: black;">run</span><span style="color: black;">&#40;</span>Component<span style="color: black;">&#41;</span></pre></td></tr></table></div>

<p>L’autre point phare de cette release, c’est la dépréciation de Mozilla Persona comme méthode d’authentification (le projet est dead) et la promotion de deux nouvelles méthodes: les certificats TLS et les paires de clés publiques/privées (Curve25519 pour le moment).</p>
<p>C’est une très bonne nouvelle, car ça veut dire plus de mots de passe dans les fichiers de configuration en production pour identifier vos propres clients qui ont forcément des privilèges supplémentaires.</p>
<p>Je reviendrais là dessus en faisant le tuto sur l’authentification.</p>
<p> Une release chouette donc. Mais qui introduit plein de dépendances à des extensions en C qui pourraient être optionnelles, ce qui rend l’installation plus compliquée. Je suis en train de discuter avec la team pour voir si on peut arranger ça, mais Tobias à l’air plutôt pour les garder. Si vous aussi vous voulez garder la simplicité de la base pure Python, <a href="https://github.com/crossbario/crossbar/issues/626">rejoignez la discussion</a>.</p>
<p> Enfin, on a pu voir l’annonce d’une feature très intéressante : le chiffrement end-to-end des messages WAMP. Ça, c’est chouette. C’est pas encore implémenté, mais ça veut dire que la prochaine release, vous pourrez probablement envoyer des messages à travers le serveur WAMP sans que celui-ci puisse les lire. </p>


feed_id: http://sametmax.com/?p=18034
title: Le don du mois : nuitka 2 &#160; Recently updated !
uri: Le-don-du-mois-nuitka-2-Recently-updated
url: http://sametmax.com/le-don-du-mois-nuitka/
content:
<p>Comme je vous l&#8217;ai dit <a href="http://sametmax.com/evolution-de-python/">dernièrement</a>, le packaging et les performances sont deux points qui méritent d&#8217;être améliorés en Python.</p>
<p><a href="http://nuitka.net/">Nuikta</a> est un projet qui vise à tacler ces deux problèmes d&#8217;un coup en compilant le code Python pour obtenir un exécutable autonome sous forme de code machine.</p>
<p>Plus besoin de s&#8217;inquiéter d&#8217;avoir la bonne version de Python installée. Plus besoin de se soucier d&#8217;avoir Python, ou une lib quelconque installée. Et en prime, le compilateur implémente le plus d&#8217;optimisations qu&#8217;il peut.</p>
<p>Nuitka est un projet en cours et son équipe est petite. Les progères sont sérieux, mais lents. Histoire d&#8217;encourager tout ça, je fais un don de 50€ à l&#8217;auteur.</p>
<p>Pour donner à votre tour, <a href="http://nuitka.net/pages/donations.html">c&#8217;est par ici</a>.</p>


feed_id: http://sametmax.com/?p=18044
title: Le piège d&#8217;écrire du code couplé à une implémentation
uri: Le-piège-d-écrire-du-code-couplé-à-une-implémentation
url: http://sametmax.com/le-piege-decrire-du-code-couple-a-une-implementation/
content:
<p>On a reproché à la communauté de Twisted que c’était un silo fermé. Une lib écrite pour Twisted ne marchait que pour Twisted.</p>
<p>Puis on a reproché à la communauté de gevent la même chose.</p>
<p>Et maintenant la communauté d’asyncio recommence à faire la même erreur.</p>
<p>Regardez, <a href="https://github.com/aio-libs">pleins de libs compatibles asyncio</a>, c’est génial non ?</p>
<p>Je ne vais pas dire non. Ça boost l’utilisabilité, l’adoption, etc.</p>
<p>Mais c’est aussi un énorme travail qui passe à côté de toute l’expérience (bugs, cas extrêmes, best practices, perfs&#8230;) des communautés précédentes. Et qui a une date de péremption, qui sera foutu à la poubelle à la prochaine vague.</p>
<p>Pourquoi ?</p>
<p>Parce que toutes ces libs se concentrent sur l’implémentation.</p>
<p>Vous ne pouvez pas réutiliser le code d’une lib STMP Twisted, car elle est liée au reactor. Pourtant cette lib contient des milliers d’infos utiles, la gestion de ce serveur bizarre, la correction de cette erreur de calcul de date, qui n’ont rien à voir avec Twisted.</p>
<p>C’est la même chose avec ces libs pour asyncio.</p>
<p>Que faire alors ?</p>
<p>Et bien d’abord écrire une lib neutre. Qui contient des choses comme :</p>
<ul>
<li>Le parsing des packets.</li>
<li>Le workflow.</li>
<li>Les constantes.</li>
<li>Les convertisseurs de données.</li>
<li>Les vérificateurs de données.</li>
<li>Les bases de connaissances sur le monde extérieur.</li>
</ul>
<p>Il faut écrire cette lib de manière à ce qu’elle puisse être réutilisée dans tout contexte. À base de callbacks simples, de hooks, de points d’entrées.</p>
<p>Puis, vous rajoutez dans un sous-module, le support pour votre plateforme favorite. Un adaptateur qui utilise ces hooks pour Twisted, ou asyncio, ou gevent.</p>
<p>Cela a de multiples bénéfices:</p>
<ul>
<li>Quand vous changez de plateforme ou que le nouveau joujou à la mode sort, une grande partie du travail peut être réutilisé.</li>
<li>Toute la partie neutre de la lib peut être réutilisée par toute la communauté Python, pas juste celle qui utilise la même plateforme.</li>
<li>Cela encourage les contributions à votre lib, puisque n’importe qui peut rajouter un module pour une plateforme et l’enrichir, et corriger des bugs sur la partie neutre.</li>
<li>Votre lib est beaucoup plus facile à tester.</li>
<li>Votre lib va cumuler du savoir sur la problématique qu’elle résout pendant bien plus de temps, puisqu’elle traverse les modes des implémentations. Au long terme, se sera la lib la plus stable et complète.</li>
</ul>
<p>Toutes les plateformes ont une manière ou un autre pour attaquer ce problème. Twisted par exemple a une classe protocole qui est indépendante du reactor. Oui, mais elle est dépendante de la manière de penser en Twisted. Personne ne documente ces protocoles de manière neutre. Personne n’utilise ces protocoles de manière neutre.</p>
<p>gevent utilise carrément le monkey patching pour essayer de se rendre transparent. Évidemment ça veut dire que c’est très dépendant de l’implémentation. Si CPython change, ça casse. Si on utilise une implémentation différente de Python, ça ne marche pas. Si on fait des expérimentations comme actuellement sur le JIT, les résultats sont imprévisibles.</p>
<p><code>async</code>/<code>await</code> a l’énorme bénéfice de proposer une interface commune à tout travail asynchrone. Fut-ce de l’IO, du thread, du multi-processing, du sous-processing, du multi-interpretteur ou des callbacks ordinaires&#8230; Cela va donc énormément gommer ces problèmes de compatibilité, même si la séparation des responsabilités que je recommande n’est pas suivie. Mais pour le moment tout le monde n’implémente pas <code>__await__</code>. Et si <code>__await__</code> lance le code sur l’autre plateforme, ça fait un truc en plus à gérer. Ce n’est pas tout à faire neutre.</p>
<p>Attention, je comprends très bien que cette séparation que je recommande ne soit pas suivie.</p>
<p>C’est très difficile de faire une API agnostique par rapport à la plateforme. Ça demande beaucoup plus de taf, de connaissance, etc. Je suis le premier à ne pas le faire pas fainéantise ou ignorance. </p>
<p>Mais il faut bien comprendre qu’à chaque fois, on réinvente la roue, une roue jetable par ailleurs.</p>
<p>Bien entendu, je dis ça pour l’async, mais c’est vrai pour tout.</p>
<p>Par exemple, des centaines de code ont leur propre moyen de définir un schéma et valider les données en entrée. Les ORM sont particulièrement coupable de cela, les libs de form aussi, mais on a tous codé ce genre de truc. C’est idiot, c’est un code qui n’a pas à être lié à une plateforme.</p>
<p>Des centaines de libs ont leur code de persistance lié à une plateforme. Même celles qui utilisent un ORM, au final, se lient à certaines bases de données (raison pour laquelle je suis <a href="https://facebook.github.io/react/blog/2015/05/01/graphql-introduction.html">GraphQL</a> de très près).</p>
<p>La généricité a ses limites, et c’est toujours un compromis entre le cout immédiat, et le bénéfice futur. Si on fait tout générique, on se retrouve avec un truc qui évolue à 2 à l’heure et qui a 15 surcouches pour faire un print. On se retrouve avec Zope. Dont personne, et c’est ironique, ne réutilise les composants parce que c’est devenu trop compliqué de les comprendre.</p>
<p>Car évidemment, qui dit découplage, dit doc bien faire, qui explique clairement comment bénéficier de ce découplage. Mais dit aussi que le code utilisant les adapteurs doit être aussi simple que si on avait un fort couplage, ce qui est dur à faire.</p>
<p>Et on tombe ici sur un autre problème : la compétence pour faire ce genre de code. Si il faut 10 ans d’expérience pour faire une libre propre, alors on va réduire considérablement le nombre de personnes qui vont oser code des libs.</p>
<p>Aussi cet article n’est en aucun cas un savon que je souhaite passer aux auteurs. Merci de coder ces libs. Merci de donner de votre temps. </p>
<p>Non, cet article est juste là pour dire : <em>dans la mesure du possible, il est très bénéfique sur le long terme de se découpler de la plateforme</em>.</p>


feed_id: http://sametmax.com/?p=18067
title: Histoire de ne pas perdre le fil : TrackingFields 1 &#160; Recently updated !
uri: Histoire-de-ne-pas-perdre-le-fil-TrackingFields-1-Recently-update
url: http://sametmax.com/histoire-de-ne-pas-perdre-le-fil-trackingfields/
content:
<p style="text-align: center;"><em><small>Ceci est un post invité de <a href="http://sametmax.com/author/Foxmask/">Foxmask</a> posté sous licence <a href="http://creativecommons.org/licenses/by/3.0/">creative common 3.0 unported</a>.</small></em></p>
<p><strong>Préambule</strong></p>
<p>Je sais bien qu&#8217;une partie de ce billet ne plaira pas à Sam&#038;Max (thanks to the Django CBV &#038; Mixin:)</p>
<p><strong>Introduction</strong></p>
<p>La première partie va planter le décors en commençant par vous montrer comment s&#8217;articule une application avec formulaire composé d&#8217;un sous formulaire en sus (j&#8217;expliquerai pourquoi après :)</p>
<p>Pour ce faire, je vous emmène dans l&#8217;univers du 7° art, viendez on va refaire StarWars!</p>
<p>Un modèle, un formulaire, une vue, un template et ca sera fini.</p>
<p>le <strong>models.py</strong></p>

<div class="wp_syntax"><table><tr><td class="code"><pre class="python" style="font-family:monospace;">    <span style="color: #ff7700;font-weight:bold;">from</span> django.<span style="color: black;">db</span> <span style="color: #ff7700;font-weight:bold;">import</span> models
&nbsp;
&nbsp;
    <span style="color: #ff7700;font-weight:bold;">class</span> Movie<span style="color: black;">&#40;</span>models.<span style="color: black;">Model</span><span style="color: black;">&#41;</span>:
        <span style="color: #483d8b;">&quot;&quot;&quot;
            Movie
        &quot;&quot;&quot;</span>
        name <span style="color: #66cc66;">=</span> models.<span style="color: black;">CharField</span><span style="color: black;">&#40;</span>max_length<span style="color: #66cc66;">=</span><span style="color: #ff4500;">200</span><span style="color: #66cc66;">,</span> unique<span style="color: #66cc66;">=</span><span style="color: #008000;">True</span><span style="color: black;">&#41;</span>
        description <span style="color: #66cc66;">=</span> models.<span style="color: black;">CharField</span><span style="color: black;">&#40;</span>max_length<span style="color: #66cc66;">=</span><span style="color: #ff4500;">200</span><span style="color: black;">&#41;</span>
&nbsp;
        <span style="color: #ff7700;font-weight:bold;">def</span> <span style="color: #0000cd;">__str__</span><span style="color: black;">&#40;</span><span style="color: #008000;">self</span><span style="color: black;">&#41;</span>:
            <span style="color: #ff7700;font-weight:bold;">return</span> <span style="color: #483d8b;">&quot;%s&quot;</span> % <span style="color: #008000;">self</span>.<span style="color: black;">name</span>
&nbsp;
&nbsp;
    <span style="color: #ff7700;font-weight:bold;">class</span> Episode<span style="color: black;">&#40;</span>models.<span style="color: black;">Model</span><span style="color: black;">&#41;</span>:
        <span style="color: #483d8b;">&quot;&quot;&quot;
           Episode - for Trilogy and So on ;)
        &quot;&quot;&quot;</span>
        name <span style="color: #66cc66;">=</span> models.<span style="color: black;">CharField</span><span style="color: black;">&#40;</span>max_length<span style="color: #66cc66;">=</span><span style="color: #ff4500;">200</span><span style="color: black;">&#41;</span>
        scenario <span style="color: #66cc66;">=</span> models.<span style="color: black;">TextField</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
        movie <span style="color: #66cc66;">=</span> models.<span style="color: black;">ForeignKey</span><span style="color: black;">&#40;</span>Movie<span style="color: black;">&#41;</span>
&nbsp;
        <span style="color: #ff7700;font-weight:bold;">def</span> <span style="color: #0000cd;">__str__</span><span style="color: black;">&#40;</span><span style="color: #008000;">self</span><span style="color: black;">&#41;</span>:
            <span style="color: #ff7700;font-weight:bold;">return</span> <span style="color: #483d8b;">&quot;%s&quot;</span> % <span style="color: #008000;">self</span>.<span style="color: black;">name</span></pre></td></tr></table></div>

<p>le <strong>forms.py</strong>, tout rikiki :</p>

<div class="wp_syntax"><table><tr><td class="code"><pre class="python" style="font-family:monospace;">    <span style="color: #ff7700;font-weight:bold;">from</span> django <span style="color: #ff7700;font-weight:bold;">import</span> forms
    <span style="color: #ff7700;font-weight:bold;">from</span> django.<span style="color: black;">forms</span>.<span style="color: black;">models</span> <span style="color: #ff7700;font-weight:bold;">import</span> inlineformset_factory
&nbsp;
    <span style="color: #ff7700;font-weight:bold;">from</span> starwars.<span style="color: black;">models</span> <span style="color: #ff7700;font-weight:bold;">import</span> Movie<span style="color: #66cc66;">,</span> Episode
&nbsp;
&nbsp;
    <span style="color: #ff7700;font-weight:bold;">class</span> MovieForm<span style="color: black;">&#40;</span>forms.<span style="color: black;">ModelForm</span><span style="color: black;">&#41;</span>:
&nbsp;
        <span style="color: #ff7700;font-weight:bold;">class</span> Meta:
            <span style="color: #483d8b;">&quot;&quot;&quot;
                As I have to use : &quot;exclude&quot; or &quot;fields&quot;
                As I'm very lazy, I dont want to fill the list in the &quot;fields&quot;
                so I say that I just want to exclude ... nothing :P
            &quot;&quot;&quot;</span>
            model <span style="color: #66cc66;">=</span> Movie
            exclude <span style="color: #66cc66;">=</span> <span style="color: black;">&#91;</span><span style="color: black;">&#93;</span>
&nbsp;
    <span style="color: #808080; font-style: italic;"># a formset based on the model of the Mother &quot;Movie&quot; and Child &quot;Episode&quot; + 1 new empty lines</span>
    <span style="color: #808080; font-style: italic;"># for more details, have a look at https://docs.djangoproject.com/fr/1.9/topics/forms/modelforms/#inline-formsets</span>
    EpisodeFormSet <span style="color: #66cc66;">=</span> inlineformset_factory<span style="color: black;">&#40;</span>Movie<span style="color: #66cc66;">,</span> Episode<span style="color: #66cc66;">,</span> fields<span style="color: #66cc66;">=</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'name'</span><span style="color: #66cc66;">,</span> <span style="color: #483d8b;">'scenario'</span><span style="color: black;">&#41;</span><span style="color: #66cc66;">,</span> extra<span style="color: #66cc66;">=</span><span style="color: #ff4500;">1</span><span style="color: black;">&#41;</span></pre></td></tr></table></div>

<p>la vue <strong>views.py</strong>, très sèche, très DRY ;)</p>

<div class="wp_syntax"><table><tr><td class="code"><pre class="python" style="font-family:monospace;">    <span style="color: #ff7700;font-weight:bold;">from</span> django.<span style="color: black;">http</span> <span style="color: #ff7700;font-weight:bold;">import</span> HttpResponseRedirect
    <span style="color: #ff7700;font-weight:bold;">from</span> django.<span style="color: black;">core</span>.<span style="color: black;">urlresolvers</span> <span style="color: #ff7700;font-weight:bold;">import</span> reverse
    <span style="color: #ff7700;font-weight:bold;">from</span> django.<span style="color: black;">views</span>.<span style="color: black;">generic</span> <span style="color: #ff7700;font-weight:bold;">import</span> CreateView<span style="color: #66cc66;">,</span> UpdateView<span style="color: #66cc66;">,</span> ListView
&nbsp;
    <span style="color: #ff7700;font-weight:bold;">from</span> starwars.<span style="color: black;">models</span> <span style="color: #ff7700;font-weight:bold;">import</span> Movie
    <span style="color: #ff7700;font-weight:bold;">from</span> starwars.<span style="color: black;">forms</span> <span style="color: #ff7700;font-weight:bold;">import</span> MovieForm<span style="color: #66cc66;">,</span> EpisodeFormSet
&nbsp;
&nbsp;
    <span style="color: #ff7700;font-weight:bold;">class</span> MovieMixin<span style="color: black;">&#40;</span><span style="color: #008000;">object</span><span style="color: black;">&#41;</span>:
        model <span style="color: #66cc66;">=</span> Movie
        form_class <span style="color: #66cc66;">=</span> MovieForm
&nbsp;
        <span style="color: #ff7700;font-weight:bold;">def</span> get_context_data<span style="color: black;">&#40;</span><span style="color: #008000;">self</span><span style="color: #66cc66;">,</span> **kw<span style="color: black;">&#41;</span>:
            <span style="color: #483d8b;">&quot;&quot;&quot; init form with data if any &quot;&quot;&quot;</span>
            context <span style="color: #66cc66;">=</span> <span style="color: #008000;">super</span><span style="color: black;">&#40;</span>MovieMixin<span style="color: #66cc66;">,</span> <span style="color: #008000;">self</span><span style="color: black;">&#41;</span>.<span style="color: black;">get_context_data</span><span style="color: black;">&#40;</span>**kw<span style="color: black;">&#41;</span>
            <span style="color: #ff7700;font-weight:bold;">if</span> <span style="color: #008000;">self</span>.<span style="color: black;">request</span>.<span style="color: black;">POST</span>:
                context<span style="color: black;">&#91;</span><span style="color: #483d8b;">'episode_form'</span><span style="color: black;">&#93;</span> <span style="color: #66cc66;">=</span> EpisodeFormSet<span style="color: black;">&#40;</span><span style="color: #008000;">self</span>.<span style="color: black;">request</span>.<span style="color: black;">POST</span><span style="color: black;">&#41;</span>
            <span style="color: #ff7700;font-weight:bold;">else</span>:
                context<span style="color: black;">&#91;</span><span style="color: #483d8b;">'episode_form'</span><span style="color: black;">&#93;</span> <span style="color: #66cc66;">=</span> EpisodeFormSet<span style="color: black;">&#40;</span>instance<span style="color: #66cc66;">=</span><span style="color: #008000;">self</span>.<span style="color: #008000;">object</span><span style="color: black;">&#41;</span>
            <span style="color: #ff7700;font-weight:bold;">return</span> context
&nbsp;
        <span style="color: #ff7700;font-weight:bold;">def</span> get_success_url<span style="color: black;">&#40;</span><span style="color: #008000;">self</span><span style="color: black;">&#41;</span>:
            <span style="color: #483d8b;">&quot;&quot;&quot; where to go back, once data are validated &quot;&quot;&quot;</span>
            <span style="color: #ff7700;font-weight:bold;">return</span> reverse<span style="color: black;">&#40;</span><span style="color: #483d8b;">&quot;home&quot;</span><span style="color: black;">&#41;</span>
&nbsp;
        <span style="color: #ff7700;font-weight:bold;">def</span> form_valid<span style="color: black;">&#40;</span><span style="color: #008000;">self</span><span style="color: #66cc66;">,</span> form<span style="color: black;">&#41;</span>:
            <span style="color: #483d8b;">&quot;&quot;&quot; form validation &quot;&quot;&quot;</span>
            formset <span style="color: #66cc66;">=</span> EpisodeFormSet<span style="color: black;">&#40;</span><span style="color: black;">&#40;</span><span style="color: #008000;">self</span>.<span style="color: black;">request</span>.<span style="color: black;">POST</span> <span style="color: #ff7700;font-weight:bold;">or</span> <span style="color: #008000;">None</span><span style="color: black;">&#41;</span><span style="color: #66cc66;">,</span> instance<span style="color: #66cc66;">=</span><span style="color: #008000;">self</span>.<span style="color: #008000;">object</span><span style="color: black;">&#41;</span>
            <span style="color: #ff7700;font-weight:bold;">if</span> formset.<span style="color: black;">is_valid</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>:
                <span style="color: #008000;">self</span>.<span style="color: #008000;">object</span> <span style="color: #66cc66;">=</span> form.<span style="color: black;">save</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
                formset.<span style="color: black;">instance</span> <span style="color: #66cc66;">=</span> <span style="color: #008000;">self</span>.<span style="color: #008000;">object</span>
                formset.<span style="color: black;">save</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
&nbsp;
            <span style="color: #ff7700;font-weight:bold;">return</span> HttpResponseRedirect<span style="color: black;">&#40;</span>reverse<span style="color: black;">&#40;</span><span style="color: #483d8b;">'home'</span><span style="color: black;">&#41;</span><span style="color: black;">&#41;</span>
&nbsp;
&nbsp;
    <span style="color: #ff7700;font-weight:bold;">class</span> Movies<span style="color: black;">&#40;</span>ListView<span style="color: black;">&#41;</span>:
        model <span style="color: #66cc66;">=</span> Movie
        context_object_name <span style="color: #66cc66;">=</span> <span style="color: #483d8b;">&quot;movies&quot;</span>
        template_name <span style="color: #66cc66;">=</span> <span style="color: #483d8b;">&quot;base.html&quot;</span>
&nbsp;
&nbsp;
    <span style="color: #ff7700;font-weight:bold;">class</span> MovieCreate<span style="color: black;">&#40;</span>MovieMixin<span style="color: #66cc66;">,</span> CreateView<span style="color: black;">&#41;</span>:
        <span style="color: #483d8b;">&quot;&quot;&quot;
            MovieMixin manages everything for me ...
        &quot;&quot;&quot;</span>
        <span style="color: #ff7700;font-weight:bold;">pass</span>
&nbsp;
&nbsp;
    <span style="color: #ff7700;font-weight:bold;">class</span> MovieUpdate<span style="color: black;">&#40;</span>MovieMixin<span style="color: #66cc66;">,</span> UpdateView<span style="color: black;">&#41;</span>:
        <span style="color: #483d8b;">&quot;&quot;&quot;
            ... and as I'm DRY I wont repeat myself myself myself ;)
        &quot;&quot;&quot;</span>
        <span style="color: #ff7700;font-weight:bold;">pass</span></pre></td></tr></table></div>

<p><strong>Pour finir de planter le décors et les costumes (merci <a href="https://fr.wikipedia.org/wiki/Au_th%C3%A9%C3%A2tre_ce_soir#Anecdotes">Roger Hart et Donald Cardwell</a>)</strong></p>
<p>le template <strong>base.html</strong></p>

<div class="wp_syntax"><table><tr><td class="code"><pre class="html" style="font-family:monospace;">    &lt;!DOCTYPE html&gt;
    &lt;html lang=&quot;fr&quot;&gt;
    &lt;head&gt;
        &lt;title&gt;Manage stories for StarWars&lt;/title&gt;
    &lt;/head&gt;
    &lt;body&gt;
    &lt;h1&gt;Stories Manager for Starwars&lt;/h1&gt;
    {% block content %}
    &lt;a href=&quot;{% url 'movie_create' %}&quot;&gt;Add a movie&lt;/a&gt;&lt;br/&gt;
    &lt;h2&gt;Movie list&lt;/h2&gt;
    &lt;ul&gt;
    {% for movie in movies %}
    &lt;li&gt;&lt;a href=&quot;{% url 'movie_edit' movie.id %}&quot;&gt;{{ movie.name }}&lt;/a&gt;&lt;/li&gt;
    {% endfor %}
    &lt;/ul&gt;
    {% endblock %}
    &lt;/body&gt;
    &lt;/html&gt;</pre></td></tr></table></div>

<p>enfin <strong>movie_form.html</strong> (le template utilisé par les UpdateView &#038; CreateView)</p>

<div class="wp_syntax"><table><tr><td class="code"><pre class="html" style="font-family:monospace;">    {% extends &quot;base.html&quot; %}
    {% block content %}
    &lt;form method=&quot;post&quot; action=&quot;&quot;&gt;
        {% csrf_token %}
        {{ formset.management_form }}
        &lt;table&gt;
        {{ form.as_table }}
        &lt;/table&gt;
        &lt;table&gt;
        {{ episode_form.as_table }}
        &lt;/table&gt;
        &lt;button&gt;Save&lt;/button&gt;
    &lt;/form&gt;
    {% endblock %}</pre></td></tr></table></div>

<p><strong>Mise à jour de la base de données</strong></p>
<p>cela s&#8217;impose :</p>

<div class="wp_syntax"><table><tr><td class="code"><pre class="shell" style="font-family:monospace;">(starwars) foxmask@foxmask:~/DjangoVirtualEnv/starwars/starwars $  ./manage.py migrate
&nbsp;
Operations to perform:
  Synchronize unmigrated apps: messages, starwars, staticfiles
  Apply all migrations: contenttypes, admin, sessions, auth
Synchronizing apps without migrations:
  Creating tables...
    Creating table starwars_movie
    Creating table starwars_episode
    Running deferred SQL...
  Installing custom SQL...</pre></td></tr></table></div>

<p>Voilà le tout est prêt (après le lancement du serveur bien sûr), je peux allégrement créer ma double trilogie pépère tel George Lucas.</p>
<p><strong>Trackons l&#8217;impie</strong></p>
<p>Seulement un jour arrive où, moi, George Lucas, je vends StarWars à Walt Disney, mais comme je ne veux pas rater de ce qu&#8217;ils vont faire de mon &#8220;bébé&#8221;, je rajoute un &#8220;tracker de modifications&#8221; à mon application, pour ne pas perdre le &#8220;field&#8221; de l&#8217;Histoire. </p>
<p><strong>Installation de Tracking Fields</strong></p>
<p>en prérequis <strong>django-tracking-fields</strong> requiert <strong>django-cuser</strong>, donc ze pip qui va bien donne :</p>

<div class="wp_syntax"><table><tr><td class="code"><pre class="shell" style="font-family:monospace;">    (starwars) foxmask@foxmask:~/DjangoVirtualEnv/starwars/starwars $ pip install django-tracking-fields django-cuser
    Collecting django-tracking-fields
      Downloading django-tracking-fields-1.0.6.tar.gz (58kB)
        100% |████████████████████████████████| 61kB 104kB/s 
    Collecting django-cuser
      Downloading django-cuser-2014.9.28.tar.gz
    Requirement already satisfied (use --upgrade to upgrade): Django&gt;=1.5 in /home/foxmask/DjangoVirtualEnv/starwars/lib/python3.5/site-packages (from django-cuser)
    Installing collected packages: django-tracking-fields, django-cuser
      Running setup.py install for django-tracking-fields ... done
      Running setup.py install for django-cuser ... done
    Successfully installed django-cuser-2014.9.28 django-tracking-fields-1.0.6</pre></td></tr></table></div>

<p>comme de coutume, après un <code>pip install</code>, une modification dans le <code>settings.py</code> suit,</p>

<div class="wp_syntax"><table><tr><td class="code"><pre class="python" style="font-family:monospace;">INSTALLED_APPS <span style="color: #66cc66;">=</span> <span style="color: black;">&#40;</span>
        ...
        <span style="color: #483d8b;">'cuser'</span><span style="color: #66cc66;">,</span>
        <span style="color: #483d8b;">'tracking_fields'</span><span style="color: #66cc66;">,</span>
        ...
    <span style="color: black;">&#41;</span>
    MIDDLEWARE_CLASSES <span style="color: #66cc66;">=</span> <span style="color: black;">&#40;</span>
        <span style="color: #483d8b;">'django.contrib.sessions.middleware.SessionMiddleware'</span><span style="color: #66cc66;">,</span>
        <span style="color: #483d8b;">'django.middleware.common.CommonMiddleware'</span><span style="color: #66cc66;">,</span>
        <span style="color: #483d8b;">'django.middleware.csrf.CsrfViewMiddleware'</span><span style="color: #66cc66;">,</span>
        <span style="color: #483d8b;">'django.contrib.auth.middleware.AuthenticationMiddleware'</span><span style="color: #66cc66;">,</span>
        <span style="color: #483d8b;">'django.contrib.auth.middleware.SessionAuthenticationMiddleware'</span><span style="color: #66cc66;">,</span>
        <span style="color: #483d8b;">'django.contrib.messages.middleware.MessageMiddleware'</span><span style="color: #66cc66;">,</span>
        <span style="color: #483d8b;">'django.middleware.clickjacking.XFrameOptionsMiddleware'</span><span style="color: #66cc66;">,</span>
        <span style="color: #483d8b;">'django.middleware.security.SecurityMiddleware'</span><span style="color: #66cc66;">,</span>
        <span style="color: #483d8b;">'cuser.middleware.CuserMiddleware'</span><span style="color: #66cc66;">,</span>  <span style="color: #808080; font-style: italic;">## &lt;=== ne pas oublier, pour chopper le user qui fait le con avec mes films;)</span>
    <span style="color: black;">&#41;</span></pre></td></tr></table></div>

<p>le petit migrate qui va bien aussi pour ajouter les tables pour les modèles de <code>django-tracking-fields</code></p>

<div class="wp_syntax"><table><tr><td class="code"><pre class="shell" style="font-family:monospace;">    (starwars) foxmask@foxmask:~/DjangoVirtualEnv/starwars/starwars $  ./manage.py migrate
    Operations to perform:
      Synchronize unmigrated apps: staticfiles, messages, cuser, starwars
      Apply all migrations: auth, sessions, contenttypes, tracking_fields, admin
    Synchronizing apps without migrations:
      Creating tables...
        Running deferred SQL...
      Installing custom SQL...
    Running migrations:
      Rendering model states... DONE
      Applying tracking_fields.0001_initial... OK
      Applying tracking_fields.0002_auto_20160203_1048... OK</pre></td></tr></table></div>

<p>et nous voilà prêt à joueur les trackers.</p>
<p><strong>Utilisation</strong></p>
<p>On ne peut pas rêver plus simple, cela se résume à un décorateur sur le modele qui identifie quelles données sont modifiées, et un field <code>histo</code> qui va lier le modele TrackingEvent de l&#8217;application TrackingFields, à ma table à surveiller. Et là, bien que le modele ait été modifié, inutile de faire un nouveau <code>python manage.py migrate</code>, rien ne bougera, <code>histo</code> sera une <a href="https://docs.djangoproject.com/fr/1.8/ref/contrib/contenttypes/#generic-relations">GenericRelation()</a>. En effet, TrackingEvent repose sur <a href="https://docs.djangoproject.com/fr/1.9/ref/contrib/contenttypes/">ContenType</a> aka &#8220;L&#8217;Infrastructure des Types de Contenu&#8221;. Si vous avez déjà tripoté la gestion des permissions, vous avez déjà dû vous y frotter;)</p>
<p>Pour la faire courte, en clair ça donne :</p>
<p>le <strong>models.py</strong> arrangé pour l&#8217;occasion du décorateur</p>

<div class="wp_syntax"><table><tr><td class="code"><pre class="python" style="font-family:monospace;">    <span style="color: #ff7700;font-weight:bold;">from</span> django.<span style="color: black;">db</span> <span style="color: #ff7700;font-weight:bold;">import</span> models
    <span style="color: #ff7700;font-weight:bold;">from</span> django.<span style="color: black;">contrib</span>.<span style="color: black;">contenttypes</span>.<span style="color: black;">fields</span> <span style="color: #ff7700;font-weight:bold;">import</span> GenericRelation
&nbsp;
    <span style="color: #ff7700;font-weight:bold;">from</span> tracking_fields.<span style="color: black;">decorators</span> <span style="color: #ff7700;font-weight:bold;">import</span> track
    <span style="color: #ff7700;font-weight:bold;">from</span> tracking_fields.<span style="color: black;">models</span> <span style="color: #ff7700;font-weight:bold;">import</span> TrackingEvent
&nbsp;
&nbsp;
    <span style="color: #66cc66;">@</span>track<span style="color: black;">&#40;</span><span style="color: #483d8b;">'name'</span><span style="color: #66cc66;">,</span> <span style="color: #483d8b;">'description'</span><span style="color: black;">&#41;</span>   <span style="color: #808080; font-style: italic;"># decorator added</span>
    <span style="color: #ff7700;font-weight:bold;">class</span> Movie<span style="color: black;">&#40;</span>models.<span style="color: black;">Model</span><span style="color: black;">&#41;</span>:
        <span style="color: #483d8b;">&quot;&quot;&quot;
            Movie
        &quot;&quot;&quot;</span>
        name <span style="color: #66cc66;">=</span> models.<span style="color: black;">CharField</span><span style="color: black;">&#40;</span>max_length<span style="color: #66cc66;">=</span><span style="color: #ff4500;">200</span><span style="color: #66cc66;">,</span> unique<span style="color: #66cc66;">=</span><span style="color: #008000;">True</span><span style="color: black;">&#41;</span>
        description <span style="color: #66cc66;">=</span> models.<span style="color: black;">CharField</span><span style="color: black;">&#40;</span>max_length<span style="color: #66cc66;">=</span><span style="color: #ff4500;">200</span><span style="color: black;">&#41;</span>
        <span style="color: #808080; font-style: italic;"># to get the changes made on movie</span>
        histo <span style="color: #66cc66;">=</span> GenericRelation<span style="color: black;">&#40;</span>TrackingEvent<span style="color: #66cc66;">,</span> content_type_field<span style="color: #66cc66;">=</span><span style="color: #483d8b;">'object_content_type'</span><span style="color: black;">&#41;</span>
&nbsp;
        <span style="color: #ff7700;font-weight:bold;">def</span> episodes<span style="color: black;">&#40;</span><span style="color: #008000;">self</span><span style="color: black;">&#41;</span>:
            <span style="color: #ff7700;font-weight:bold;">return</span> Episode.<span style="color: black;">objects</span>.<span style="color: #008000;">filter</span><span style="color: black;">&#40;</span>movie<span style="color: #66cc66;">=</span><span style="color: #008000;">self</span><span style="color: black;">&#41;</span>
&nbsp;
        <span style="color: #ff7700;font-weight:bold;">def</span> <span style="color: #0000cd;">__str__</span><span style="color: black;">&#40;</span><span style="color: #008000;">self</span><span style="color: black;">&#41;</span>:
            <span style="color: #ff7700;font-weight:bold;">return</span> <span style="color: #483d8b;">&quot;%s&quot;</span> % <span style="color: #008000;">self</span>.<span style="color: black;">name</span>
&nbsp;
    <span style="color: #66cc66;">@</span>track<span style="color: black;">&#40;</span><span style="color: #483d8b;">'name'</span><span style="color: #66cc66;">,</span> <span style="color: #483d8b;">'scenario'</span><span style="color: black;">&#41;</span>   <span style="color: #808080; font-style: italic;"># decorator added</span>
    <span style="color: #ff7700;font-weight:bold;">class</span> Episode<span style="color: black;">&#40;</span>models.<span style="color: black;">Model</span><span style="color: black;">&#41;</span>:
        <span style="color: #483d8b;">&quot;&quot;&quot;
           Episode - for Trilogy and So on ;)
        &quot;&quot;&quot;</span>
        name <span style="color: #66cc66;">=</span> models.<span style="color: black;">CharField</span><span style="color: black;">&#40;</span>max_length<span style="color: #66cc66;">=</span><span style="color: #ff4500;">200</span><span style="color: black;">&#41;</span>
        scenario <span style="color: #66cc66;">=</span> models.<span style="color: black;">TextField</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
        movie <span style="color: #66cc66;">=</span> models.<span style="color: black;">ForeignKey</span><span style="color: black;">&#40;</span>Movie<span style="color: black;">&#41;</span>
        <span style="color: #808080; font-style: italic;"># to get the changes made on episode</span>
        histo <span style="color: #66cc66;">=</span> GenericRelation<span style="color: black;">&#40;</span>TrackingEvent<span style="color: #66cc66;">,</span> content_type_field<span style="color: #66cc66;">=</span><span style="color: #483d8b;">'object_content_type'</span><span style="color: black;">&#41;</span>
&nbsp;
        <span style="color: #ff7700;font-weight:bold;">def</span> <span style="color: #0000cd;">__str__</span><span style="color: black;">&#40;</span><span style="color: #008000;">self</span><span style="color: black;">&#41;</span>:
            <span style="color: #ff7700;font-weight:bold;">return</span> <span style="color: #483d8b;">&quot;%s&quot;</span> % <span style="color: #008000;">self</span>.<span style="color: black;">name</span></pre></td></tr></table></div>

<p>bon là c&#8217;est simplissime comme une recette de pate à crêpes: 3 imports de rigueur, le décorateur et la GenericRelation() on mélange le tout et ca donne ce qui suit J&#8217;ai, au passage, rajouté une fonction episodes à ma classe Movie, dont je vous reparlerai plus bas.</p>
<p>le template de la <strong>DetailView</strong> (pour afficher uniquement les details d&#8217;un film) qui va bien</p>

<div class="wp_syntax"><table><tr><td class="code"><pre class="html" style="font-family:monospace;">    &lt;table&gt;
       &lt;caption&gt;History of the modification of {{ object }} &lt;/caption&gt;
       &lt;thead&gt;
       &lt;tr&gt;&lt;th&gt;Old Value&lt;/th&gt;&lt;th&gt;New Value&lt;/th&gt;&lt;th&gt;By&lt;/th&gt;&lt;th&gt;at&lt;/th&gt;&lt;/tr&gt;
       &lt;/thead&gt;
       &lt;tbody&gt;
    {% for h in object.histo.all %}
       {% for f in h.fields.all %}
           &lt;tr&gt;&lt;td&gt;{{ f.old_value }}&lt;/td&gt;&lt;td&gt;{{ f.new_value }}&lt;/td&gt;&lt;td&gt;{{ h.user }}&lt;/td&gt;&lt;td&gt;{{ h.date }}&lt;/td&gt;&lt;/tr&gt;
       {% endfor %}
    {% endfor %}
       &lt;/tbody&gt;
    &lt;/table&gt;</pre></td></tr></table></div>

<p>A présent si je me rends dans ma page pour modifier le scénario d&#8217;un Episode, mon template ci dessus, ne m&#8217;affichera pas ces modications ! Pourquoi bou diou ? Parce qu&#8217;ici j&#8217;affiche &#8220;l&#8217;histo&#8221; de <em>Movie</em> pas de <del datetime="2016-02-06T14:44:25+00:00">Episode</del>&#8230; On comprend à présent ici mon intéret pour le sous formulaire. Le &#8220;problème&#8221; aurait été masqué si je m&#8217;étais arrêté à un seul simple formulaire. </p>
<p><strong>Corrigeons</strong></p>
<p>c&#8217;est là qu&#8217;entre en jeu la fonction <code>episodes</code> à ma classe <code>Movie</code>, pour me permettre d&#8217;itérer dessus et afficher tout le toutim</p>
<p>le template de la <strong>DetailView</strong> qui va bien (bis)</p>

<div class="wp_syntax"><table><tr><td class="code"><pre class="html" style="font-family:monospace;">    &lt;table&gt;
        &lt;caption&gt;History of the modifications of {{ object }} &lt;/caption&gt;
        &lt;thead&gt;
            &lt;tr&gt;&lt;th&gt;Old Value&lt;/th&gt;&lt;th&gt;New Value&lt;/th&gt;&lt;th&gt;By&lt;/th&gt;&lt;th&gt;at&lt;/th&gt;&lt;/tr&gt;
        &lt;/thead&gt;
        &lt;tbody&gt;
    {% for h in object.histo.all %}
       {% for f in h.fields.all %}
           &lt;tr&gt;&lt;td&gt;{{ f.old_value }}&lt;/td&gt;&lt;td&gt;{{ f.new_value }}&lt;/td&gt;&lt;td&gt;{{ h.user }}&lt;/td&gt;&lt;td&gt;{{ h.date }}&lt;/td&gt;&lt;/tr&gt;
       {% endfor %}
    {% endfor %}
        &lt;/tbody&gt;
    &lt;/table&gt;
    {% for ep in object.episodes %}
        {% if ep.histo.all %}
    &lt;table&gt;
        &lt;caption&gt;history of the modifications of Episode&lt;/caption&gt;
        &lt;thead&gt;
            &lt;tr&gt;&lt;th&gt;Old Value&lt;/th&gt;&lt;th&gt;New Value&lt;/th&gt;&lt;th&gt;By&lt;/th&gt;&lt;th&gt;at&lt;/th&gt;&lt;/tr&gt;
        &lt;/thead&gt;
        &lt;tbody&gt;
            {% for h in ep.histo.all %}
                {% for f in h.fields.all %}
                {% if f.old_value == f.new_value %} {# they are the same when the new value is created to avoid to display &quot;null&quot; #}
                {% else %}
                &lt;tr&gt;&lt;td&gt;{{ f.old_value }}&lt;/td&gt;&lt;td&gt;{{ f.new_value }}&lt;/td&gt;&lt;td&gt;{{ h.user }}&lt;/td&gt;&lt;td&gt;{{ h.date }}&lt;/td&gt;&lt;/tr&gt;
                {% endif %}
                {%  endfor %}
            {% endfor %}
        &lt;/tbody&gt;
     &lt;/table&gt;
        {% endif %}
    {% endfor %}</pre></td></tr></table></div>

<p>Voili voilou ! Et en prime, si vous êtes curieux, coté admin, vous avez aussi la liste de toutes les modifications si besoin ;)</p>
<p>Aux utilisateurs avertis qui diraient :</p>
<blockquote><p>pourquoi l&#8217;avoir recodé coté front puisque c&#8217;est déjà géré coté admin sans lever le petit doigt ?</p></blockquote>
<p>Parce que George Lucas veut montrer les modifications apportées à son bébé StarWars par Walt Disney, au monde entier pardis !</p>
<p>Ah un détail en passant : dans l&#8217;admin la vue qui affiche la liste des modifications donne : &#8220;Episode Object&#8221; ou &#8220;Movie Object&#8221;. Pour eviter ça, zavez dû remarquer que j&#8217;ai mis la fonction <code>__str__</code> dans mes modèles ce qui vous rendra une valeur plus &#8220;lisible&#8221; sur ce qui a été modifié.</p>
<p><strong>Conclusion :</strong></p>
<p>Dans la vraie vie de votre serviteur, ne se voyait pas créer un modele &#8220;history&#8221; lié &#8220;physiquement&#8221; par une FK à chaque modèle, entreprenait de chercher au travers de la toile quelques ressources.</p>
<p>C&#8217;est finallement sur <a href="irc://irc.freenode.net/#django-fr">#django-fr@freenode</a> qu&#8217;il a posé la question et a obtenu de <a href="https://github.com/gagaro">Gagaro</a> le grââl : une application nommée <a href="https://github.com/makinacorpus/django-tracking-fields">tracking-fields</a>, dont il est l&#8217;auteur.</p>
<p>Pour une fois qu&#8217;il fait sa faignasse en ne codant pas tout by himself, <a href="https://github.com/makinacorpus/django-tracking-fields">ça fait plaisir de tomber sur une appli pareille</a> !</p>
<p>Si vous voulez jouer avec le code de ce gestionnaire de films <a href="https://github.com/foxmask/tracking-starwars">c&#8217;est par ici la bonne soupe</a></p>


feed_id: http://sametmax.com/?p=18160
title: Embarquer un fichier non Python proprement 4 &#160; Recently updated !
uri: Embarquer-un-fichier-non-Python-proprement-4-Recently-updated
url: http://sametmax.com/embarquer-un-fichier-non-python-proprement/
content:
<p>Ah, le packaging Python, c&#8217;est toujours pas fun.</p>
<p>Parmi les nombres problèmes, la question suivante se pose souvent : comment je livre proprement un fichier de données fourni avec mon package Python ?</p>
<p>Le problème est double:</p>
<ul>
<li>Comment s&#8217;assurer que setup.py l&#8217;inclut bien ?</li>
<li>Comment trouver et lire le fichier proprement ?</li>
</ul>
<p>On pourrait croire qu&#8217;un truc aussi basique serait simple, mais non.</p>
<p>Ca ne l&#8217;est pas car un package Python peut être livré sous forme de code source, ou d&#8217;un binaire, ou d&#8217;une archive eggs/whl/pyz&#8230; Et en prime il peut être transformé par les packageurs des repos debian, centos, homebrew, etc.</p>
<p>Mais skippy a la solution à tous vos soucis !</p>
<h2>Include les fichiers avec votre code</h2>
<p>A la racine de votre projet, il faut un fichier <code>MANIFEST.IN</code> bien rempli et <code>include_package_data</code> sur <code>True</code> dans <code>setup.py</code>. <a href="http://sametmax.com/creer-un-setup-py-et-mettre-sa-bibliotheque-python-en-ligne-sur-pypi/">On a un article là dessus, ça tombe bien</a>.</p>
<p>N&#8217;utilisez pas <code>package_data</code>, <a href="http://blog.codekills.net/2011/07/15/lies,-more-lies-and-python-packaging-documentation-on--package_data-/">ça ne marche pas à tous les coups</a>.</p>
<h2>Charger les fichiers dans votre code</h2>
<p>Si vous êtes pressés, copiez ça dans un fichier utils.py:</p>

<div class="wp_syntax"><table><tr><td class="code"><pre class="python" style="font-family:monospace;"><span style="color: #ff7700;font-weight:bold;">import</span> <span style="color: #dc143c;">os</span>
<span style="color: #ff7700;font-weight:bold;">import</span> io
&nbsp;
<span style="color: #ff7700;font-weight:bold;">from</span> <span style="color: #dc143c;">types</span> <span style="color: #ff7700;font-weight:bold;">import</span> ModuleType
&nbsp;
&nbsp;
<span style="color: #ff7700;font-weight:bold;">class</span> RessourceError<span style="color: black;">&#40;</span><span style="color: #008000;">EnvironmentError</span><span style="color: black;">&#41;</span>:
&nbsp;
    <span style="color: #ff7700;font-weight:bold;">def</span> <span style="color: #0000cd;">__init__</span><span style="color: black;">&#40;</span><span style="color: #008000;">self</span><span style="color: #66cc66;">,</span> msg<span style="color: #66cc66;">,</span> errors<span style="color: black;">&#41;</span>:
        <span style="color: #008000;">super</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>.<span style="color: #0000cd;">__init__</span><span style="color: black;">&#40;</span>msg<span style="color: black;">&#41;</span>
        <span style="color: #008000;">self</span>.<span style="color: black;">errors</span> <span style="color: #66cc66;">=</span> errors
&nbsp;
&nbsp;
<span style="color: #ff7700;font-weight:bold;">def</span> binary_resource_stream<span style="color: black;">&#40;</span><span style="color: #dc143c;">resource</span><span style="color: #66cc66;">,</span> locations<span style="color: black;">&#41;</span>:
    <span style="color: #483d8b;">&quot;&quot;&quot; Return a resource from this path or package &quot;&quot;&quot;</span>
&nbsp;
    <span style="color: #808080; font-style: italic;"># convert</span>
    errors <span style="color: #66cc66;">=</span> <span style="color: black;">&#91;</span><span style="color: black;">&#93;</span>
&nbsp;
    <span style="color: #808080; font-style: italic;"># If location is a string or a module, we wrap it in a tuple</span>
    <span style="color: #ff7700;font-weight:bold;">if</span> <span style="color: #008000;">isinstance</span><span style="color: black;">&#40;</span>locations<span style="color: #66cc66;">,</span> <span style="color: black;">&#40;</span><span style="color: #008000;">str</span><span style="color: #66cc66;">,</span> ModuleType<span style="color: black;">&#41;</span><span style="color: black;">&#41;</span>:
        locations <span style="color: #66cc66;">=</span> <span style="color: black;">&#40;</span>locations<span style="color: #66cc66;">,</span><span style="color: black;">&#41;</span>
&nbsp;
    <span style="color: #ff7700;font-weight:bold;">for</span> location <span style="color: #ff7700;font-weight:bold;">in</span> locations:
&nbsp;
        <span style="color: #808080; font-style: italic;"># Assume location is a module and try to load it using pkg_resource</span>
        <span style="color: #ff7700;font-weight:bold;">try</span>:
            <span style="color: #ff7700;font-weight:bold;">import</span> pkg_resources
            module_name <span style="color: #66cc66;">=</span> <span style="color: #008000;">getattr</span><span style="color: black;">&#40;</span>location<span style="color: #66cc66;">,</span> <span style="color: #483d8b;">'__name__'</span><span style="color: #66cc66;">,</span> location<span style="color: black;">&#41;</span>
            <span style="color: #ff7700;font-weight:bold;">return</span> pkg_resources.<span style="color: black;">resource_stream</span><span style="color: black;">&#40;</span>module_name<span style="color: #66cc66;">,</span> <span style="color: #dc143c;">resource</span><span style="color: black;">&#41;</span>
        <span style="color: #ff7700;font-weight:bold;">except</span> <span style="color: black;">&#40;</span><span style="color: #008000;">ImportError</span><span style="color: #66cc66;">,</span> <span style="color: #008000;">EnvironmentError</span><span style="color: black;">&#41;</span> <span style="color: #ff7700;font-weight:bold;">as</span> e:
            errors.<span style="color: black;">append</span><span style="color: black;">&#40;</span>e<span style="color: black;">&#41;</span>
&nbsp;
            <span style="color: #808080; font-style: italic;"># Falling back to load it from path.</span>
            <span style="color: #ff7700;font-weight:bold;">try</span>:
                <span style="color: #808080; font-style: italic;"># location is a module</span>
                module_path <span style="color: #66cc66;">=</span> <span style="color: #008000;">__import__</span><span style="color: black;">&#40;</span>location<span style="color: black;">&#41;</span>.__file__
                parent_dir <span style="color: #66cc66;">=</span> <span style="color: #dc143c;">os</span>.<span style="color: black;">path_dirname</span><span style="color: black;">&#40;</span>module_path<span style="color: black;">&#41;</span>
            <span style="color: #ff7700;font-weight:bold;">except</span> <span style="color: black;">&#40;</span><span style="color: #008000;">AttributeError</span><span style="color: #66cc66;">,</span> <span style="color: #008000;">ImportError</span><span style="color: black;">&#41;</span>:
                <span style="color: #808080; font-style: italic;"># location is a dir path</span>
                parent_dir <span style="color: #66cc66;">=</span> <span style="color: #dc143c;">os</span>.<span style="color: black;">path</span>.<span style="color: black;">realpath</span><span style="color: black;">&#40;</span>location<span style="color: black;">&#41;</span>
&nbsp;
            <span style="color: #808080; font-style: italic;"># Now we got a resource full path. Just open it as a regular file.</span>
            canonical_path <span style="color: #66cc66;">=</span> <span style="color: #dc143c;">os</span>.<span style="color: black;">path</span>.<span style="color: black;">join</span><span style="color: black;">&#40;</span>parent_dir<span style="color: #66cc66;">,</span> <span style="color: #dc143c;">resource</span><span style="color: black;">&#41;</span>
            <span style="color: #ff7700;font-weight:bold;">try</span>:
                <span style="color: #ff7700;font-weight:bold;">return</span> <span style="color: #008000;">open</span><span style="color: black;">&#40;</span><span style="color: #dc143c;">os</span>.<span style="color: black;">path</span>.<span style="color: black;">join</span><span style="color: black;">&#40;</span>canonical_path<span style="color: black;">&#41;</span><span style="color: #66cc66;">,</span> mode<span style="color: #66cc66;">=</span><span style="color: #483d8b;">&quot;rb&quot;</span><span style="color: black;">&#41;</span>
            <span style="color: #ff7700;font-weight:bold;">except</span> <span style="color: #008000;">EnvironmentError</span> <span style="color: #ff7700;font-weight:bold;">as</span> e:
                errors.<span style="color: black;">append</span><span style="color: black;">&#40;</span>e<span style="color: black;">&#41;</span>
&nbsp;
    msg <span style="color: #66cc66;">=</span> <span style="color: black;">&#40;</span><span style="color: #483d8b;">'Unable to find resource &quot;%s&quot; in &quot;%s&quot;. '</span>
           <span style="color: #483d8b;">'Inspect RessourceError.errors for list of encountered erros.'</span><span style="color: black;">&#41;</span>
    <span style="color: #ff7700;font-weight:bold;">raise</span> RessourceError<span style="color: black;">&#40;</span>msg % <span style="color: black;">&#40;</span><span style="color: #dc143c;">resource</span><span style="color: #66cc66;">,</span> locations<span style="color: black;">&#41;</span><span style="color: #66cc66;">,</span> errors<span style="color: black;">&#41;</span>
&nbsp;
&nbsp;
<span style="color: #ff7700;font-weight:bold;">def</span> text_resource_stream<span style="color: black;">&#40;</span>path<span style="color: #66cc66;">,</span> locations<span style="color: #66cc66;">,</span> encoding<span style="color: #66cc66;">=</span><span style="color: #483d8b;">&quot;utf8&quot;</span><span style="color: #66cc66;">,</span> errors<span style="color: #66cc66;">=</span><span style="color: #008000;">None</span><span style="color: #66cc66;">,</span>
                    newline<span style="color: #66cc66;">=</span><span style="color: #008000;">None</span><span style="color: #66cc66;">,</span> line_buffering<span style="color: #66cc66;">=</span><span style="color: #008000;">False</span><span style="color: black;">&#41;</span>:
    <span style="color: #483d8b;">&quot;&quot;&quot; Return a resource from this path or package. Transparently decode the stream. &quot;&quot;&quot;</span>
    stream <span style="color: #66cc66;">=</span> binary_resource_stream<span style="color: black;">&#40;</span>path<span style="color: #66cc66;">,</span> locations<span style="color: black;">&#41;</span>
    <span style="color: #ff7700;font-weight:bold;">return</span> io.<span style="color: black;">TextIOWrapper</span><span style="color: black;">&#40;</span>stream<span style="color: #66cc66;">,</span> encoding<span style="color: #66cc66;">,</span> errors<span style="color: #66cc66;">,</span> newline<span style="color: #66cc66;">,</span> line_buffering<span style="color: black;">&#41;</span></pre></td></tr></table></div>

<p>Et faites:</p>

<div class="wp_syntax"><table><tr><td class="code"><pre class="python" style="font-family:monospace;"><span style="color: #ff7700;font-weight:bold;">from</span> utils <span style="color: #ff7700;font-weight:bold;">import</span> binary_resource_stream<span style="color: #66cc66;">,</span> text_resource_stream 
data_stream <span style="color: #66cc66;">=</span> binary_resource_stream<span style="color: black;">&#40;</span><span style="color: #483d8b;">'./chemin/relatif'</span><span style="color: #66cc66;">,</span> package<span style="color: black;">&#41;</span> <span style="color: #808080; font-style: italic;"># pour du binaire </span>
data <span style="color: #66cc66;">=</span> data_stream.<span style="color: black;">read</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span> 
txt_stream <span style="color: #66cc66;">=</span> text_resource_stream<span style="color: black;">&#40;</span><span style="color: #483d8b;">'./chemin/relatif'</span><span style="color: #66cc66;">,</span> package<span style="color: black;">&#41;</span> <span style="color: #808080; font-style: italic;"># pour du texte text = txt_stream.read()</span></pre></td></tr></table></div>

<p>Par exemple:</p>

<div class="wp_syntax"><table><tr><td class="code"><pre class="python" style="font-family:monospace;">image_data <span style="color: #66cc66;">=</span> binary_resource_stream<span style="color: black;">&#40;</span><span style="color: #483d8b;">'./static/img/logo.png'</span><span style="color: #66cc66;">,</span> <span style="color: #483d8b;">&quot;super_package&quot;</span><span style="color: black;">&#41;</span>.<span style="color: black;">read</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span> 
text <span style="color: #66cc66;">=</span> text_resource_stream<span style="color: black;">&#40;</span><span style="color: #483d8b;">'./config/default.ini'</span><span style="color: #66cc66;">,</span> <span style="color: #483d8b;">&quot;super_package&quot;</span><span style="color: #66cc66;">,</span> encoding<span style="color: #66cc66;">=</span><span style="color: #483d8b;">&quot;cp850&quot;</span><span style="color: black;">&#41;</span>.<span style="color: black;">read</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span></pre></td></tr></table></div>

<p>Si vous n&#8217;êtes pas pressés, voilà toute l&#8217;histoire&#8230;</p>
<p>A la base, ont fait généralement un truc du genre:</p>

<div class="wp_syntax"><table><tr><td class="code"><pre class="python" style="font-family:monospace;">PROJECT_DIR <span style="color: #66cc66;">=</span> <span style="color: #dc143c;">os</span>.<span style="color: black;">path</span>.<span style="color: black;">dirname</span><span style="color: black;">&#40;</span><span style="color: #dc143c;">os</span>.<span style="color: black;">path</span>.<span style="color: black;">realpath</span><span style="color: black;">&#40;</span>__file__<span style="color: black;">&#41;</span><span style="color: black;">&#41;</span> <span style="color: #808080; font-style: italic;"># ou pathlib/path.py </span>
data <span style="color: #66cc66;">=</span> <span style="color: #008000;">open</span><span style="color: black;">&#40;</span><span style="color: #dc143c;">os</span>.<span style="color: black;">path</span>.<span style="color: black;">join</span><span style="color: black;">&#40;</span>PROJECT_DIR<span style="color: #66cc66;">,</span> chemin_relatif<span style="color: black;">&#41;</span><span style="color: black;">&#41;</span>.<span style="color: black;">read</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span><span style="color: black;">&#41;</span></pre></td></tr></table></div>

<p>Mais ça ne marche pas dans le cas d&#8217;une installation binaire, zippée, trafiquée, en plein questionnement existentiel après avoir regardé un film des frères Cohen, etc.</p>
<p>La manière la plus sure de le faire est:</p>

<div class="wp_syntax"><table><tr><td class="code"><pre class="python" style="font-family:monospace;"><span style="color: #ff7700;font-weight:bold;">import</span> pkg_resources 
data <span style="color: #66cc66;">=</span> pkg_resources.<span style="color: black;">resource_stream</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'nom_de_votre_module'</span><span style="color: #66cc66;">,</span> chemin_relatif<span style="color: black;">&#41;</span>.<span style="color: black;">read</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span></pre></td></tr></table></div>

<p>Ça va marcher tant que votre package est importable. On se fout d’où il est, de sa forme, Python se démerde.</p>
<p>Mais ça pose plusieurs autres problèmes:</p>
<ul>
<li><code>pkg_resources</code> fait parti de setuptools, qui n&#8217;est pas forcément installé sur votre système. En effet, malgré l&#8217;existence de <code>ensure_pip</code> depuis la 3.4, beaucoup de distributions n&#8217;installent ni pip, ni setuptools par défaut et en font des paquets séparés.</li>
</p>
<li><code>data</code> sera forcément de type <code>bytes</code>. Il faut le décoder manuellement derrière.</li>
<li>si votre code doit fonctionner aussi en dehors du cadre d&#8217;un package importable (genre on unzip et ça juste marche), <code>pkg_resources</code> ne fonctionnera pas puisque par essence il utilise le nom du package pour trouver la ressource.</li>
<li>si vous voulez spécifier plusieurs endroits ou potentiellement trouver la ressource, ou passer l&#8217;objet module à la place du nom du module, ça ne marche pas.</li>
<li>tous ces cas, bien entendu, lèves des exceptions différentes histoire de faciliter votre <code>try</code>/<code>except</code>.</li>
<li>Il existe <code>pkgutil</code> qui est bien installé partout, mais ça load tout en mémoire d&#8217;un coup. Si vous avez un XML de 10Mo à charger ça fait mal au cul.</li>
<li>la doc de <code>pkg_resource</code> est aussi claire que l&#8217;anus de la mère du premier commentateur de cet article. On va voir qui lit l&#8217;article en entier là&#8230;</li>
</ul>
<p>Le snippet fourni corrige ces problèmes en gérant les cas tordus pour vous. Moi je l&#8217;utilise souvent en faisant:</p>

<div class="wp_syntax"><table><tr><td class="code"><pre class="python" style="font-family:monospace;"><span style="color: #ff7700;font-weight:bold;">with</span> text_resource_stream<span style="color: black;">&#40;</span><span style="color: #483d8b;">'./chemin/relatif'</span><span style="color: #66cc66;">,</span> <span style="color: black;">&#91;</span><span style="color: #483d8b;">'package_name'</span><span style="color: #66cc66;">,</span> <span style="color: #483d8b;">'chemin_vers_au_cas_ou_c_est_pas_un_package'</span><span style="color: black;">&#93;</span><span style="color: black;">&#41;</span> <span style="color: #ff7700;font-weight:bold;">as</span> f:
     <span style="color: #ff7700;font-weight:bold;">print</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'boom !'</span><span style="color: #66cc66;">,</span> f.<span style="color: black;">read</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span><span style="color: black;">&#41;</span></pre></td></tr></table></div>

<p>Je devrais peut-être rajouter ça dans batbelt et minibelt&#8230;</p>
<h2>Travailler avec des fichiers non Python</h2>
<p>Ça, c&#8217;est pour lire les ressources fournies. Mais si vous devez écrire des trucs, il vous faut un dossier de travail.</p>
<p>Si c&#8217;est pour un boulot jetable, faites-le dans un dossier temporaire. Le module <a href="https://docs.python.org/3/library/tempfile.html">tempfile</a> est votre ami.</p>
<p>Si c&#8217;est pour un boulot persistant, trouvez le dossier approprié à votre (fichier de config, fichier de log, etc) et travaillez dedans. <a href="https://github.com/jaraco/path.py">path.py</a> a des méthodes dédiées à cela (un des nombreuses raisons qui rendent ce module meilleur que <code>pathlib</code>).</p>


feed_id: http://sametmax.com/?p=18179
title: Signes qu&#8217;il faut extraire une fonction. 10 &#160; Recently updated !
uri: Signes-qu-il-faut-extraire-une-fonction-10-Recently-updated
url: http://sametmax.com/signes-quil-faut-extraire-une-fonction/
content:
<p>Organiser son code est tout un art. Ça se peaufine au fur et à mesure d’une carrière. J’ai découvert au fil du temps des règles qui formalisent quelque chose que je faisais sans vraiment m’en rendre compte. Vous savez comme ces règles de français à la noix que vous utilisez quotidiennement sans savoir qu’il y a règle. </p>
<h2>DRY au bout de 3</h2>
<p>Je commence par la plus facile.</p>
<p>Le célèbre Don&#8217;t Repeat Yourself n’a pas besoin d’être poussée à l’extrême. Copier/coller du code une fois est tout à fait acceptable, et souvent bien plus productif que de refactoriser son code. On commence donc à faire une fonction si on a 3 copies de son code.</p>
<p>Dans ce code par exemple, j’utilise 3 fois mon trigger, il est mur pour la cueillette :</p>

<div class="wp_syntax"><table><tr><td class="code"><pre class="python" style="font-family:monospace;">async <span style="color: #ff7700;font-weight:bold;">def</span> setup<span style="color: black;">&#40;</span><span style="color: #008000;">self</span><span style="color: #66cc66;">,</span> cwd<span style="color: #66cc66;">=</span><span style="color: #008000;">None</span><span style="color: black;">&#41;</span>:
&nbsp;
&nbsp;
    <span style="color: #ff7700;font-weight:bold;">if</span> cwd <span style="color: #ff7700;font-weight:bold;">is</span> <span style="color: #008000;">None</span>:
        cwd <span style="color: #66cc66;">=</span> get_project_dir<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
    <span style="color: #008000;">self</span>.<span style="color: black;">project_dir</span> <span style="color: #66cc66;">=</span> Path<span style="color: black;">&#40;</span>cwd<span style="color: black;">&#41;</span>
&nbsp;
    <span style="color: #808080; font-style: italic;"># Tell all component to hook them self to the events they want to react</span>
    <span style="color: #808080; font-style: italic;"># to</span>
    futures <span style="color: #66cc66;">=</span> <span style="color: black;">&#91;</span>ensure_awaitable<span style="color: black;">&#40;</span>c.<span style="color: black;">setup</span><span style="color: black;">&#41;</span> <span style="color: #ff7700;font-weight:bold;">for</span> c <span style="color: #ff7700;font-weight:bold;">in</span> <span style="color: #008000;">self</span>.<span style="color: black;">components</span>.<span style="color: black;">values</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span><span style="color: black;">&#93;</span>
    await asyncio.<span style="color: black;">gather</span><span style="color: black;">&#40;</span>*futures<span style="color: black;">&#41;</span>
&nbsp;
    <span style="color: #008000;">self</span>.<span style="color: black;">state</span> <span style="color: #66cc66;">=</span> value
    await <span style="color: #008000;">self</span>.<span style="color: black;">trigger</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'init'</span><span style="color: black;">&#41;</span>
&nbsp;
    <span style="color: #008000;">self</span>.<span style="color: black;">state</span> <span style="color: #66cc66;">=</span> value
    await <span style="color: #008000;">self</span>.<span style="color: black;">trigger</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'ready'</span><span style="color: black;">&#41;</span>
&nbsp;
    <span style="color: #008000;">self</span>.<span style="color: black;">state</span> <span style="color: #66cc66;">=</span> value
    <span style="color: #ff7700;font-weight:bold;">return</span> <span style="color: #008000;">self</span>.<span style="color: black;">trigger</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'running'</span><span style="color: black;">&#41;</span></pre></td></tr></table></div>

<p>Pouf:</p>

<div class="wp_syntax"><table><tr><td class="code"><pre class="python" style="font-family:monospace;"><span style="color: #ff7700;font-weight:bold;">def</span> change_state<span style="color: black;">&#40;</span><span style="color: #008000;">self</span><span style="color: #66cc66;">,</span> value<span style="color: black;">&#41;</span>:
    <span style="color: #483d8b;">&quot;&quot;&quot; Change the inner state of the app, and call all state observers. &quot;&quot;&quot;</span>
    <span style="color: #008000;">self</span>.<span style="color: black;">state</span> <span style="color: #66cc66;">=</span> value
    <span style="color: #ff7700;font-weight:bold;">return</span> <span style="color: #008000;">self</span>.<span style="color: black;">trigger</span><span style="color: black;">&#40;</span>value<span style="color: black;">&#41;</span>
&nbsp;
async <span style="color: #ff7700;font-weight:bold;">def</span> setup<span style="color: black;">&#40;</span><span style="color: #008000;">self</span><span style="color: #66cc66;">,</span> cwd<span style="color: #66cc66;">=</span><span style="color: #008000;">None</span><span style="color: black;">&#41;</span>:
&nbsp;
    <span style="color: #ff7700;font-weight:bold;">if</span> cwd <span style="color: #ff7700;font-weight:bold;">is</span> <span style="color: #008000;">None</span>:
        cwd <span style="color: #66cc66;">=</span> get_project_dir<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
    <span style="color: #008000;">self</span>.<span style="color: black;">project_dir</span> <span style="color: #66cc66;">=</span> Path<span style="color: black;">&#40;</span>cwd<span style="color: black;">&#41;</span>
&nbsp;
    <span style="color: #808080; font-style: italic;"># Tell all component to hook them self to the events they want to react</span>
    <span style="color: #808080; font-style: italic;"># to</span>
    futures <span style="color: #66cc66;">=</span> <span style="color: black;">&#91;</span>ensure_awaitable<span style="color: black;">&#40;</span>c.<span style="color: black;">setup</span><span style="color: black;">&#41;</span> <span style="color: #ff7700;font-weight:bold;">for</span> c <span style="color: #ff7700;font-weight:bold;">in</span> <span style="color: #008000;">self</span>.<span style="color: black;">components</span>.<span style="color: black;">values</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span><span style="color: black;">&#93;</span>
    await asyncio.<span style="color: black;">gather</span><span style="color: black;">&#40;</span>*futures<span style="color: black;">&#41;</span>
&nbsp;
    await <span style="color: #008000;">self</span>.<span style="color: black;">change_setate</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'init'</span><span style="color: black;">&#41;</span>
    await <span style="color: #008000;">self</span>.<span style="color: black;">change_setate</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'ready'</span><span style="color: black;">&#41;</span>
    <span style="color: #ff7700;font-weight:bold;">return</span> <span style="color: #008000;">self</span>.<span style="color: black;">change_setate</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'running'</span><span style="color: black;">&#41;</span></pre></td></tr></table></div>

<h2>Transformez vos commentaires en fonctions</h2>
<p>C’est un conseil qui a apparemment été rendu populaire pas le courant extrem programming. Leur idée c’est que les noms de fonctions sont une forme de documentation. Chaque fonction devant être testée et documentée, si vous avez un commentaire qui se balade tout seul au milieu du code, c’est le signe qu’il faut en faire une fonction.</p>
<p>Dans le code qu’on vient de voir, j’ai un gros commentaire en plein milieu expliquant un bout de code un peu compliqué. Hop, on coupe :</p>

<div class="wp_syntax"><table><tr><td class="code"><pre class="python" style="font-family:monospace;">async <span style="color: #ff7700;font-weight:bold;">def</span> setup<span style="color: black;">&#40;</span><span style="color: #008000;">self</span><span style="color: #66cc66;">,</span> cwd<span style="color: #66cc66;">=</span><span style="color: #008000;">None</span><span style="color: black;">&#41;</span>:
&nbsp;
    <span style="color: #ff7700;font-weight:bold;">if</span> cwd <span style="color: #ff7700;font-weight:bold;">is</span> <span style="color: #008000;">None</span>:
        cwd <span style="color: #66cc66;">=</span> get_project_dir<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
    <span style="color: #008000;">self</span>.<span style="color: black;">project_dir</span> <span style="color: #66cc66;">=</span> Path<span style="color: black;">&#40;</span>cwd<span style="color: black;">&#41;</span>
&nbsp;
    <span style="color: #808080; font-style: italic;"># Tell all component to hook them self to the events they want to react</span>
    <span style="color: #808080; font-style: italic;"># to</span>
    futures <span style="color: #66cc66;">=</span> <span style="color: black;">&#91;</span>ensure_awaitable<span style="color: black;">&#40;</span>c.<span style="color: black;">setup</span><span style="color: black;">&#41;</span> <span style="color: #ff7700;font-weight:bold;">for</span> c <span style="color: #ff7700;font-weight:bold;">in</span> <span style="color: #008000;">self</span>.<span style="color: black;">components</span>.<span style="color: black;">values</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span><span style="color: black;">&#93;</span>
    await asyncio.<span style="color: black;">gather</span><span style="color: black;">&#40;</span>*futures<span style="color: black;">&#41;</span>
&nbsp;
    await <span style="color: #008000;">self</span>.<span style="color: black;">change_setate</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'init'</span><span style="color: black;">&#41;</span>
    await <span style="color: #008000;">self</span>.<span style="color: black;">change_setate</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'ready'</span><span style="color: black;">&#41;</span>
    <span style="color: #ff7700;font-weight:bold;">return</span> <span style="color: #008000;">self</span>.<span style="color: black;">change_setate</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'running'</span><span style="color: black;">&#41;</span></pre></td></tr></table></div>

<p>Pouf:</p>

<div class="wp_syntax"><table><tr><td class="code"><pre class="python" style="font-family:monospace;">async <span style="color: #ff7700;font-weight:bold;">def</span> setup_components<span style="color: black;">&#40;</span><span style="color: #008000;">self</span><span style="color: black;">&#41;</span>:
    <span style="color: #483d8b;">&quot;&quot;&quot; Give all components a chance to hook to life cycle events &quot;&quot;&quot;</span>
    futures <span style="color: #66cc66;">=</span> <span style="color: black;">&#91;</span>ensure_awaitable<span style="color: black;">&#40;</span>c.<span style="color: black;">setup</span><span style="color: black;">&#41;</span> <span style="color: #ff7700;font-weight:bold;">for</span> c <span style="color: #ff7700;font-weight:bold;">in</span> <span style="color: #008000;">self</span>.<span style="color: black;">components</span>.<span style="color: black;">values</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span><span style="color: black;">&#93;</span>
    <span style="color: #ff7700;font-weight:bold;">return</span> await asyncio.<span style="color: black;">gather</span><span style="color: black;">&#40;</span>*futures<span style="color: black;">&#41;</span>
&nbsp;
&nbsp;
async <span style="color: #ff7700;font-weight:bold;">def</span> setup<span style="color: black;">&#40;</span><span style="color: #008000;">self</span><span style="color: #66cc66;">,</span> cwd<span style="color: #66cc66;">=</span><span style="color: #008000;">None</span><span style="color: black;">&#41;</span>:
&nbsp;
    <span style="color: #ff7700;font-weight:bold;">if</span> cwd <span style="color: #ff7700;font-weight:bold;">is</span> <span style="color: #008000;">None</span>:
        cwd <span style="color: #66cc66;">=</span> get_project_dir<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
    <span style="color: #008000;">self</span>.<span style="color: black;">project_dir</span> <span style="color: #66cc66;">=</span> Path<span style="color: black;">&#40;</span>cwd<span style="color: black;">&#41;</span>
&nbsp;
    await <span style="color: #008000;">self</span>.<span style="color: black;">setup_components</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
&nbsp;
    await <span style="color: #008000;">self</span>.<span style="color: black;">change_setate</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'init'</span><span style="color: black;">&#41;</span>
    await <span style="color: #008000;">self</span>.<span style="color: black;">change_setate</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'ready'</span><span style="color: black;">&#41;</span>
    <span style="color: #ff7700;font-weight:bold;">return</span> <span style="color: #008000;">self</span>.<span style="color: black;">change_setate</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'running'</span><span style="color: black;">&#41;</span></pre></td></tr></table></div>

<p>On voit ici que la présence du commentaire signalait que le code était assez complexe, et donc méritait d’être isolé. Cela permet de :</p>
<ul>
<li>Rendre la fonction <code>setup()</code> plus facile à comprendre.</li>
<li>Isoler la complexité en une unité simple, plus facile à lire et à expliquer donc les limites et dépendances sont claires.</li>
<li>Et donc permettre un beau test unittaire juste pour cette petite fonction.</li>
</ul>
<h2>Une partie qui n’a rien à voir avec la choucroute mérite sa propre choucroute</h2>
<p>Dans de grosses fonctions, on a souvent des bouts qui sont des étapes, mais qui ne sont pas liés sémantiquement aux autres étapes. Elles doivent juste arriver chronologiquement avant ou après celle-ci.</p>
<p>Toujours dans le même code, on a un bout qui initialise le project_dir de l’app. Ce snippet, c’est comme l’inspecteur Karamazov, fils unique, aucun lien. En tout cas aucun avec le setup de components ou le changement du state. Il est là car il doit arriver en premier. Aucune, aucune, aucune hésitation ! Amputation, SO-LU-TION !</p>

<div class="wp_syntax"><table><tr><td class="code"><pre class="python" style="font-family:monospace;">async <span style="color: #ff7700;font-weight:bold;">def</span> setup<span style="color: black;">&#40;</span><span style="color: #008000;">self</span><span style="color: #66cc66;">,</span> cwd<span style="color: #66cc66;">=</span><span style="color: #008000;">None</span><span style="color: black;">&#41;</span>:
&nbsp;
    <span style="color: #ff7700;font-weight:bold;">if</span> cwd <span style="color: #ff7700;font-weight:bold;">is</span> <span style="color: #008000;">None</span>:
        cwd <span style="color: #66cc66;">=</span> get_project_dir<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
    <span style="color: #008000;">self</span>.<span style="color: black;">project_dir</span> <span style="color: #66cc66;">=</span> Path<span style="color: black;">&#40;</span>cwd<span style="color: black;">&#41;</span>
&nbsp;
    await <span style="color: #008000;">self</span>.<span style="color: black;">setup_components</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
&nbsp;
    await <span style="color: #008000;">self</span>.<span style="color: black;">change_setate</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'init'</span><span style="color: black;">&#41;</span>
    await <span style="color: #008000;">self</span>.<span style="color: black;">change_setate</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'ready'</span><span style="color: black;">&#41;</span>
    <span style="color: #ff7700;font-weight:bold;">return</span> <span style="color: #008000;">self</span>.<span style="color: black;">change_setate</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'running'</span><span style="color: black;">&#41;</span></pre></td></tr></table></div>

<p>Pouf :</p>

<div class="wp_syntax"><table><tr><td class="code"><pre class="python" style="font-family:monospace;">async <span style="color: #ff7700;font-weight:bold;">def</span> setup_environnement<span style="color: black;">&#40;</span><span style="color: #008000;">self</span><span style="color: #66cc66;">,</span> cwd<span style="color: #66cc66;">=</span><span style="color: #008000;">None</span><span style="color: black;">&#41;</span>:
    <span style="color: #483d8b;">&quot;&quot;&quot; Set project dir for the current app &quot;&quot;&quot;</span>
    <span style="color: #ff7700;font-weight:bold;">if</span> cwd <span style="color: #ff7700;font-weight:bold;">is</span> <span style="color: #008000;">None</span>:
        cwd <span style="color: #66cc66;">=</span> get_project_dir<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
    <span style="color: #008000;">self</span>.<span style="color: black;">project_dir</span> <span style="color: #66cc66;">=</span> Path<span style="color: black;">&#40;</span>cwd<span style="color: black;">&#41;</span>
&nbsp;
async <span style="color: #ff7700;font-weight:bold;">def</span> setup<span style="color: black;">&#40;</span><span style="color: #008000;">self</span><span style="color: #66cc66;">,</span> cwd<span style="color: #66cc66;">=</span><span style="color: #008000;">None</span><span style="color: black;">&#41;</span>:
&nbsp;
    <span style="color: #008000;">self</span>.<span style="color: black;">setup_environnement</span><span style="color: black;">&#40;</span><span style="color: #dc143c;">cmd</span><span style="color: black;">&#41;</span>
&nbsp;
    await <span style="color: #008000;">self</span>.<span style="color: black;">setup_components</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
&nbsp;
    await <span style="color: #008000;">self</span>.<span style="color: black;">change_setate</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'init'</span><span style="color: black;">&#41;</span>
    await <span style="color: #008000;">self</span>.<span style="color: black;">change_setate</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'ready'</span><span style="color: black;">&#41;</span>
    <span style="color: #ff7700;font-weight:bold;">return</span> <span style="color: #008000;">self</span>.<span style="color: black;">change_setate</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'running'</span><span style="color: black;">&#41;</span></pre></td></tr></table></div>

<p>On gagne en testabilité.</p>
<p>Et maintenant notre méthode <code>setup()</code> est très, très facile à comprendre. Son rôle est d’organiser 5 étapes. Pas de faire les étapes elle-même, ça c’est délégué. Le cycle de vie de l’application saute maintenant aux yeux. S’il y a un bug, on cherchera uniquement dans la fonction qui est responsable.</p>
<p>Sans le savoir, et sans vraiment le chercher, nous avons mis en oeuvre le principe de séparation des responsabilités. Seulement au lieu d’avoir cette notion floue et abstraite de responsabilité, on a maintenant quelques règles qui peuvent être facilement appliquées.</p>
<p>Diantre, je faisais donc de la prose, depuis toujours dans le savoir&#8230;</p>
<h2>Pas de paramètre on/off</h2>
<p>Le code de <a href="http://sametmax.com/embarquer-un-fichier-non-python-proprement/">l’article d’hier</a> était un cas d’école</p>
<p>Ma première version ressemblait à :</p>

<div class="wp_syntax"><table><tr><td class="code"><pre class="python" style="font-family:monospace;"><span style="color: #ff7700;font-weight:bold;">def</span> resource_stream<span style="color: black;">&#40;</span><span style="color: #dc143c;">resource</span><span style="color: #66cc66;">,</span> locations<span style="color: #66cc66;">,</span> encoding<span style="color: #66cc66;">=</span><span style="color: #483d8b;">&quot;utf8&quot;</span><span style="color: #66cc66;">,</span> ...<span style="color: #66cc66;">,</span> binary<span style="color: #66cc66;">=</span><span style="color: #008000;">False</span><span style="color: black;">&#41;</span>:
    <span style="color: #483d8b;">&quot;&quot;&quot; Return a resource from this path or package &quot;&quot;&quot;</span>
    <span style="color: #808080; font-style: italic;"># du code, du code... puis:</span>
    <span style="color: #ff7700;font-weight:bold;">if</span> binary:
        <span style="color: #ff7700;font-weight:bold;">return</span> stream
    <span style="color: #ff7700;font-weight:bold;">return</span> io.<span style="color: black;">TextIOWrapper</span><span style="color: black;">&#40;</span>stream<span style="color: #66cc66;">,</span> encoding<span style="color: #66cc66;">,</span> errors<span style="color: #66cc66;">,</span> newline<span style="color: #66cc66;">,</span> line_buffering<span style="color: black;">&#41;</span></pre></td></tr></table></div>

<p>On voit clairement ici que <code>binary</code> est un paramètre on/off, un truc qui déclenche un mode auquel on va passer un booléen.</p>
<p>Ça va s’utiliser comme ça:</p>

<div class="wp_syntax"><table><tr><td class="code"><pre class="python" style="font-family:monospace;">txtstream <span style="color: #66cc66;">=</span>  resource_stream<span style="color: black;">&#40;</span>...<span style="color: black;">&#41;</span>
bstream <span style="color: #66cc66;">=</span>  resource_stream<span style="color: black;">&#40;</span>...<span style="color: #66cc66;">,</span> <span style="color: #008000;">True</span><span style="color: black;">&#41;</span></pre></td></tr></table></div>

<p>Quand on a des constantes magiques, il faut sortir la hache :</p>

<div class="wp_syntax"><table><tr><td class="code"><pre class="python" style="font-family:monospace;"><span style="color: #ff7700;font-weight:bold;">def</span> binary_resource_stream<span style="color: black;">&#40;</span><span style="color: #dc143c;">resource</span><span style="color: #66cc66;">,</span> locations<span style="color: black;">&#41;</span>:
    <span style="color: #483d8b;">&quot;&quot;&quot; Return a resource from this path or package &quot;&quot;&quot;</span>
    ...
    <span style="color: #ff7700;font-weight:bold;">return</span> stream
&nbsp;
&nbsp;
<span style="color: #ff7700;font-weight:bold;">def</span> text_resource_stream<span style="color: black;">&#40;</span>path<span style="color: #66cc66;">,</span> locations<span style="color: #66cc66;">,</span> encoding<span style="color: #66cc66;">=</span><span style="color: #483d8b;">&quot;utf8&quot;</span><span style="color: #66cc66;">,</span> ...<span style="color: black;">&#41;</span>:
    stream <span style="color: #66cc66;">=</span> binary_resource_stream<span style="color: black;">&#40;</span>path<span style="color: #66cc66;">,</span> locations<span style="color: black;">&#41;</span>
    <span style="color: #ff7700;font-weight:bold;">return</span> io.<span style="color: black;">TextIOWrapper</span><span style="color: black;">&#40;</span>stream<span style="color: #66cc66;">,</span> encoding<span style="color: #66cc66;">,</span> errors<span style="color: #66cc66;">,</span> newline<span style="color: #66cc66;">,</span> line_buffering<span style="color: black;">&#41;</span></pre></td></tr></table></div>

<p>La seconde fonction utilise la première. A l’usage le code est beaucoup plus clair :</p>

<div class="wp_syntax"><table><tr><td class="code"><pre class="python" style="font-family:monospace;">txtstream <span style="color: #66cc66;">=</span>  text_resource_stream<span style="color: black;">&#40;</span>...<span style="color: black;">&#41;</span>
bstream <span style="color: #66cc66;">=</span>  binary_resource_stream<span style="color: black;">&#40;</span>...<span style="color: black;">&#41;</span></pre></td></tr></table></div>

<p>Encore une fois, c’est plus facile à tester, plus facile à comprendre, et on diminue le nombre de branches dans le code.</p>
<p>Pour les gens particulièrement intéressés par la lisibilité du code, il existe l’indicateur de McCabe qui se base notamment sur le nombre de branches logiques. Le max recommandé est en général de 10, personnellement mes codes dépassent rarement 5. <a href="https://flake8.readthedocs.org/en/latest/">flake8 a l’option &#8211;max-complexity</a> pour vérifier que votre code s’y tient.</p>


feed_id: http://sametmax.com/?p=18191
title: Comment foirer sa communication avec son client en une leçon 10 &#160; Recently updated !
uri: Comment-foirer-sa-communication-avec-son-client-en-une-leçon-10-Re
url: http://sametmax.com/comment-foirer-sa-communication-avec-son-client-en-une-lecon/
content:
<p>S.Lott est un excellent informaticien, très connu dans le monde de Python. J’ai régulièrement eu affaire à lui sur stackoverflow, et il sait de quoi il parle.</p>
<p>Je lisais son (excellent) <a href="http://slott-softwarearchitect.blogspot.com/2016/02/sql-hegemony-and-document-databases.html">article</a> qui expliquait la difficulté à faire perdre aux gens leur habitude de penser en tables.</p>
<p>Malheureusement, si l’auteur a parfaitement réussi à faire passer son message aux lecteurs, il a complètement échoué à communiquer avec son client.</p>
<p>Voyez-vous, S.Lott fait preuve ici d’un péché auquel de nombreuses personnes intelligentes s’adonnent : il a raison.</p>
<p>Mais le client s’en branle.</p>
<p>Le client se fout complètement de savoir pourquoi on ne peut pas dumper ses données MongoDB telles-quel dans un tableau Excel. Il se fout complètement d’avoir tort.</p>
<p>Le client a un problème, et il souhaite une solution.</p>
<p>Notre taf n’est pas d’avoir raison. Notre taf est de résoudre des problèmes. On ne nous paie pas pour donner des leçons de justesse.</p>
<p>Donc, quand un client arrive avec une demande impossible à satisfaire, la bonne réaction à avoir n’est pas de lui expliquer que ce n’est pas possible. C’est inutile, improductif, et ça n’aide personne.</p>
<p>La bonne réaction à avoir, c’est de redéfinir le problème :</p>
<ul>
<li>Quel objectif souhaite-t-il atteindre ?</li>
<li>Pourquoi il a défini cette solution ?</li>
<li>Quelles sont ses contraintes ?</li>
</ul>
<p>Vous pouvez, bien entendu, dans le cadre de la démarche vers la mise en place de la bonne solution, lui expliquer pourquoi c’est la manière la plus appropriée. Pourquoi sa solution initiale ne va pas lui résoudre son problème.</p>
<p>Par exemple, dans le cadre de l’article, des gens demandent un dump de données hiérarchiques (document Mongo) vers un format tabulaire (tableau, Excel, csv&#8230;).</p>
<p>La mauvaise réponse est de dire que c’est impossible. Zéro intérêt pour les gens en face, et peut-être un léger sentiment d’humiliation. Ce n’est ni le lieu ni le moment pour l’enseignement, il y a d’autres opportunités bien plus appropriées pour ça.</p>
<p>Il y a pourtant une réponse très simple:</p>
<blockquote><p>
Je vois. Donnez-moi le schéma de vos données et à quoi doit ressembler la feuille Excel. </p>
<p>Il faut que vous sachiez qu’une base Mongo a un format de stockage très différent d’une feuille Excel, donc il est possible que ça demande des aménagements, où peut être choisir une solution différente. Il risque d’y avoir de la perte d’informations, ou pas mal de travail manuel pour dénormaliser les données. Il faut donc qu’on passe un peu de temps ensemble pour définir clairement le cadre de tout ça.  J’aurais besoin de faire un tête-à-tête avec une personne qui sait à quoi va servir la feuille Excel en détail, savoir qui va la lire, dans quel but, etc. On peut prendre rendez-vous ?</p></blockquote>
<p>Bien entendu, le client peut être votre patron. Ou votre collègue de bureau. Auquel cas le format de la conversation va changer, mais pas le sens général.</p>
<p>Le but est de comprendre ce que la personne veut faire.</p>
<p>Parfois (souvent même), en définissant clairement le problème, la personne voit que sa solution ne lui convient pas et vous demande par elle-même une alternative.</p>
<p>Une fois sur deux, vous n’aurez pas besoin de prendre rendez-vous, car la personne sent que quelque chose se passe et va se lancer dans la conversation : son problème simple à l’air plus compliqué que prévu. Elle va se rebeller : « c’est simple, tu me fais juste un dump de la base dans un bête fichier Excel, c’est tout ». </p>
<p>Les mots-clés sont « juste », « bête », « c’est tout ». On peut avoir aussi « simple », « évident », « c’est pas bien compliqué »&#8230;</p>
<p>Il suffit de rester calme, et avoir un comportement respectueux, mais d’expert. « J’ai déjà travaillé sur un problème similaire, je sais que je ne saurais pas résoudre ce problème correctement sans ces informations. Vous avez 2 minutes pour griffonner à quoi doit ressembler le fichier Excel sur un papier ? »</p>
<p>Vous la prenez à son propre jeu. Ça ne durera pas deux minutes. Car pour faire ça il faut avoir le schéma des données, les bonnes personnes en jeu, et une idée claire de l’usage final, ce qui rend de plus en plus clair, et sans argumenter, que le problème demande plus de travail que « juste », « bête » et « c’est tout ». C&#8217;est l&#8217;occasion de trouver la solution ensemble.</p>
<p>Bref, « laisse-moi te démontrer que c’est con ce que tu dis » n’est jamais une bonne phrase à dire, même enrobée dans des termes polis et une explication technique. Il faut rester factuel : voilà comment je comprends le problème, voilà ce qu’il me faut pour le résoudre.</p>
<p>En plus, le meeting de définition du problème, vous pouvez le facturer. Hey :)</p>


feed_id: http://sametmax.com/?p=18253
uri: L-internationalisation-c-est-long-et-dur-(et-ça-racle-le-fond)-7
url: http://sametmax.com/linternationalisation-cest-long-et-dur-et-ca-racle-le-fond/
title:
L&#8217;internationalisation, c&#8217;est long et dur (et ça racle le fond) 7 &#160; Recently updated !

content:
<p>L&#8217;internationalisation, abrégé i18n car il y a 18 lettres entre le i et le n, est le processus qui consiste à proposer différentes versions d&#8217;un contenu ou d&#8217;une interface afin de coller au plus proche des attentes culturelles de différents groupes d&#8217;humains.</p>
<p>Quand je discute avec un client et qu&#8217;il me dit :</p>
<blockquote><p>Je veux tradure mon site en Anglais, c&#8217;est combien ?</p></blockquote>
<p>Je tique toujours un peu.</p>
<p>C&#8217;est que l&#8217;i18n, ce n&#8217;est pas juste traduire des mots vers des autres. C&#8217;est beaucoup, beaucoup plus compliqué que ça. Et il faut avant tout identifier qui on vise, ce qu&#8217;on veut leur apporter, et ce qu&#8217;on est prêt à investir comme énergie pour leur offrir.</p>
<p>En générale, les populations anglophones sont assez proches culturellement de nous, donc c&#8217;est plus facile de faire la transition que vers des cultures arabes, russes ou chinoises. Et si c&#8217;est imparfait, ce n&#8217;est pas trop grave, car l&#8217;utilisateur s&#8217;adaptera assez bien.</p>
<p>Néanmoins, il ne faut pas croire que prendre le texte et le coller dans Google translate va suffire, loin de là.</p>
<p>Et si le travail vise de gens culturellement plus éloignés de vous, alors le travail peu vite de venir énorme.</p>
<h2>Le texte</h2>
<p>Bien entendu, on pense tout de suite à la traduction, et c&#8217;est déjà un gros boulot.</p>
<p>Mais déjà, il faut s&#8217;adapter au contexte culturel que l&#8217;on vise. En Japonais, les niveaux de respect ne seront pas les mêmes selon les clients. Vous traduisez vers de l&#8217;espagnol, mais visez-vous l&#8217;Amérique du Sud et ses ustedes partout, ou l&#8217;Espagne et son vosotros ? Vous voulez proposer de louer un cab aux Londoniens ou un taxi aux New-Yorkais ?</p>
<p>Au passage, prenez un bon traducteur, car en plus de devoir traduire le sens du texte (et pas juste les mots), il faut qu&#8217;il adapte la ponctuation. Ainsi, on colle &#8220;:&#8221;, &#8220;!&#8221; et &#8220;!&#8221; au mot précédent en Anglais, mais pas en français.</p>
<p>On a à peine commencé l&#8217;article et c&#8217;est déjà la merde, mais attendez !</p>
<p>Il y a le sens de lecture aussi !</p>
<p>De gauche à droite, de droite à gauche, de haut en bas. Et si vous croyez que ce n&#8217;est que le texte, lol&#8230; Car ça influence également toute la mise en page, avec notamment le placement des éléments importants : titres, boutons, etc. afin qu&#8217;ils soient mis en évidence.</p>
<p>Après, il y a l&#8217;espacement. Vous savez, on peut se permettre de se la jouer cool sur les espacements en français, parceque&#8217;on a plein de ponctuation. Donc un bloc de texte est clair, facile à lire en diagonale. En Thaïlandais par contre, il n&#8217;y a pas de virgule, de point, ni même d&#8217;espace entre les mots et les phrases. Donc si vous avez un truc à bien séparer du reste, il va falloir trouver une astuce visuelle.</p>
<p>Et puis il y a mon favoris, <a href="http://sametmax.com/lencoding-en-python-une-bonne-fois-pour-toute/">particulièrement parce que Python est très sévère (mais juste:)) avec</a> : l&#8217;encodage.</p>
<p>Car dans le meilleur des mondes, on a de l&#8217;UTF8 partout. Mais on est pas dans le meilleur des mondes, et si vous visez certains terminaux, il faudra encoder dans un truc couleur locale. Par exemple, si vous envoyez des SMS sur des vieux 3310 en Afrique.</p>
<h2>Les nombres</h2>
<p>Ah, les nombres. Qu&#8217;est-ce qu&#8217;il pourrait être plus simple ? C&#8217;est universel pas vrai ? </p>
<p>Pas vrai ?</p>
<p>Et bien disons que comme d&#8217;habitude, la donnée est universelle, et la représentation ne l&#8217;est pas.</p>
<p>Déjà, il a les bases. On <a href="https://randomascii.wordpress.com/2016/02/13/base-ten-for-almost-everything/">aime beaucoup la base 10</a>, et quand c&#8217;est du technique parfois un peu de binaire, d&#8217;octal ou d&#8217;hexa.</p>
<p>Mais certaines populations ont <a href="http://mentalfloss.com/article/31879/12-mind-blowing-number-systems-other-languages">des manières de compter</a> étonnantes. Par exemple, si vous faites un système à Bamako (ne rigolez pas, c&#8217;est un exemple de ma carrière), sachez qu&#8217;en Bambara 1000 se dit 2 fois 500.</p>
<p>À cela se rajoutent les séparateurs et devises. Mille dollars et quarante cents pour un américain, c&#8217;est $1,000.40 ou $1000.40, alors qu&#8217;en français se sera 1000,40 $ ou 1 000,40 $.</p>
<p>Et chaque format à ses règles de groupages, ses séparateurs, ses positions de préfixes&#8230; Les numéros de téléphone, les numéros de sécurité sociale, les codes postaux&#8230;</p>
<p>Attendez, ne fuyez pas, on n’en est pas encore à la moitié !</p>
<h2>Les unités</h2>
<p>Même la NASA <a href="http://edition.cnn.com/TECH/space/9909/30/mars.metric.02/">a niqué une mission</a> parce qu&#8217;ils se sont embrouillés entre les unités impériales et le système métrique.</p>
<p>Alors vous allez me dire, tant qu&#8217;on n’utilise pas <a href="media/82b1463f.19-unites_pifometriques.pdf" data-original-source="http://www.asqualab.com/documents/qualite/metrologie/19-unites_pifometriques.pdf">des unités pifométriques</a>, il suffit de convertir. Oui, mais encore faut-il savoir de quoi on parle. Il faut donc toujours demander à l&#8217;utilisateur en quoi il saisit ses données, et être très précis.</p>
<p>Très précis parce que figurez-vous que:</p>
<ul>
<li>Le Gallon est utilisé par aux UK, en Ireland, au Canada, aux Iles  caïman, A la Dominique et Myanmar.</li>
<li>Les Émirats Arabes Unis, Panama et la Guyane utilisaient le Gallon jusqu&#8217;en 2013.</li>
<li>Mais le Gallon américain et le Gallon britannique ne sont pas équivalent. L&#8217;un fait 3.785l, l&#8217;autre 4.54609l, ce qui a causé la mort de quelques pilotes en Mediterranée qui on fait un plein pas assez Charlie.</li>
</ul>
<h2>Les dates</h2>
<p>Je ne vous apprends rien en vous disant que gérer les fuseaux horaires c&#8217;est chiant. Mettez tout en UTC, et utiliser <a href="http://sametmax.com/les-time-zones-en-python/">une bonne lib</a> parce que le temps c&#8217;est relou : des zones géographiques alignées n&#8217;ont pas les mêmes heures (France et UK), des pays très grands ont plusieurs fuseaux (USA) ou un seul malgré leur gigantisme (Chine), et certains on les heures d&#8217;été/hiver, mais pas tous. Et il y a les années bissextiles, et la fameuse leap second. Et le fait que certains pays <a hre="https://www.timeanddate.com/news/time/samoa-dateline.html">décident de changer de fuseau horaire</a> comme ça, pouf, parce que ça les arrange.</p>
<p>Du coup il faut bien demander aux utilisateurs où ils sont pour récupérer la date dans le bon fuseau (l&#8217;ordinateur peut être réglé pour envoyer la mauvaise info, il peut sortir de l&#8217;avion, il peut ne pas vouloir utiliser la timezone actuelle, etc), convertir tout en UTC,  et l&#8217;afficher dans le bon.</p>
<p>Après, hop, on a le formage des dates. Mais si, vous savez, ce truc qui a 40 000 règles qui changent d&#8217;un pays à l&#8217;autre, d&#8217;un standard à l&#8217;autre. 02/03/2015: dd/mm/yyyy ou mm/dd/yyyy ? Timestamp en seconde ou en millisecondes ?</p>
<p>Allez, vous croyez que vous vous en sortirez comme ça ?</p>
<p>N&#8217;oubliez pas, tout le monde n&#8217;a pas le même calendrier&#8230;</p>
<p>Nous utilisons un calendrier basé sur le chrétien, mais vous avez des calendriers bouddhistes, musulmans, des trucs basés sur les phases de la lune, et même il y a quelque temps en France le très rigolo <a href="https://fr.wikipedia.org/wiki/Calendrier_r%C3%A9publicain">calendrier républicain</a>. Donc maintenant, il faut se soucier de pour où, de pour qui, mais aussi de pour quand on formate. Je plains les gens qui font des apps pour les musées.</p>
<p>Je plains aussi tous les gens qui font des plannings internationaux, qui doivent décider si lundi ou dimanche est le premier jour à afficher et traduire les notations naturelles comme &#8220;il y a 3 minutes(s)&#8221;.</p>
<h2>La loi</h2>
<p>Oh, si, oh si !</p>
<p>La loi change d&#8217;un pays à l&#8217;autre ! Nudité, le copyright, l&#8217;accessibilité&#8230;</p>
<p>Le DMCA aux USA, la censure en Chine, l&#8217;Hadopi en France. Nan pour le dernier je déconne hein, ils ont condamné 5 personnes depuis le début du programme </p>
<p>Et allez prendre en compte les lecteurs d&#8217;écran, mettre du ARIA comme il faut, tester le bouzin pour les personnes en déficience visuelle (problèmes de couleur, aveugles, malvoyants&#8230;) ou motrice. </p>
<p>Bon alors là je ne vais pas rentrer dans les détails, car c&#8217;est trop, trop compliqué pour ma petite tête.</p>
<h2>La culture</h2>
<p>Le symbolisme diffère d&#8217;un groupe culturel à l&#8217;autre.</p>
<p>Vous allez faire un site pour le mariage, en beau blanc ! </p>
<p>Mais en Chine, il faut du rouge.</p>
<p>Et dans certaines cultures, c&#8217;est la couleur du deuil.</p>
<p>Ce qui rend crédible commercialement aussi est différent. Un beau site bien épuré, avec plein d&#8217;espace négatif, c&#8217;est classe non ? Mais <a href="https://www.quora.com/Why-does-the-design-of-Japanese-websites-tend-to-differ-from-those-in-the-US">en Asie, avoir une page saturée est la norme.</a></p>
<p>En fait toute l&#8217;infrastructure, et même le système entier change la manière de voir les choses.</p>
<p>Quand on écrit du contenu pour un pays, il faut se rappeler des choses comme le climat, la monnaie différente, conduite à droite ou à gauche, voltage du courant, jours chômés, situation politique&#8230; </p>
<p>Si on fait une application avec beaucoup de carto, on ne mettra pas les mêmes choses en avant. Dans certains pays, les pistes sont importantes, dans d&#8217;autres les autoroutes le sont. Le truc le plus marquant qui me vient à l&#8217;esprit, c&#8217;est le fait que les cartes japonaises définissent un adresse en labélisant les blocs entre les rues, tandis que nous labélisons les rues entre les blocs.</p>
<h2>Et je ne parle même pas du markéting</h2>
<p>Qui est bien entendu radicalement différent selon la population visée, et non juste en termes d&#8217;i18n, mais aussi selon l&#8217;appartenance ethnique, sociale, culturelle, démographique, les handicape&#8230;</p>
<p>Il serait vraiment intéressant d&#8217;avoir un espèce de wiki open source qui, pour chaque groupe visé, liste les choses auxquelles il faut penser.</p>
<p>Finalement tout ça est trop chiant, z&#8217;ont cas se démerder ces estrangers de merde !</p>
<p>Arg, putain, on est 70 millions seulement, c&#8217;est nous les estrangers.</p>
<p>Après, ça ne veut pas dire qu&#8217;il faut tout faire, et trouver l&#8217;équilibre entre les ressources disponibles et le résultat qu&#8217;on vise. Mais il est bon de savoir ce qu&#8217;on décide de ne pas faire.</p>


feed_id: http://sametmax.com/?p=18388
title: Mais qui donc set ce signal handler ? 3
uri: Mais-qui-donc-set-ce-signal-handler-3
url: http://sametmax.com/mais-qui-donc-set-ce-signal-handler/
content:
<p>Le module <a href="https://docs.python.org/3/library/signal.html?highlight=signal#signal.signal">signal</a> permet de réagir aux <a href="http://en.cppreference.com/w/c/program/SIG_types">signaux envoyés l&#8217;OS</a>, par exemple celui qui vous annonce qu&#8217;il va vous défoncer la gueule (<code>SIGTERM</code>) ou que vous votre mémoire n&#8217;est plus ce qu&#8217;elle était (<code>SIGSEGV</code>).</p>
<p>Mais le problème, c&#8217;est que rien n&#8217;annonce qu&#8217;un signal est attrapé. Pire, quand on enregistre un handler pour un signal, ça écrase le précédent. Yep, c&#8217;est très con.</p>
<p>S&#8217;aurait été plus malin d&#8217;avoir un truc comme propose le DOM, genre <code>addEventListener</code>, qui accumule les handlers pour un event donné, mais non, on override.</p>
<p>Donc parfois, une lib tierce partie se permet un petit handling de signal sur le pouce, comme ça, sans demander la permission, provoquant chaos et moult jurons. Car des signaux, y en a pas mal:</p>

<div class="wp_syntax"><table><tr><td class="code"><pre class="python" style="font-family:monospace;"><span style="color: #66cc66;">&gt;&gt;&gt;</span> <span style="color: #ff7700;font-weight:bold;">import</span> <span style="color: #dc143c;">signal</span>
<span style="color: #66cc66;">&gt;&gt;&gt;</span> <span style="color: black;">&#91;</span>s <span style="color: #ff7700;font-weight:bold;">for</span> s <span style="color: #ff7700;font-weight:bold;">in</span> <span style="color: #008000;">dir</span><span style="color: black;">&#40;</span><span style="color: #dc143c;">signal</span><span style="color: black;">&#41;</span> <span style="color: #ff7700;font-weight:bold;">if</span> s.<span style="color: black;">isupper</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span><span style="color: black;">&#93;</span>
<span style="color: black;">&#91;</span><span style="color: #483d8b;">'ITIMER_PROF'</span><span style="color: #66cc66;">,</span> <span style="color: #483d8b;">'ITIMER_REAL'</span><span style="color: #66cc66;">,</span> <span style="color: #483d8b;">'ITIMER_VIRTUAL'</span><span style="color: #66cc66;">,</span> <span style="color: #483d8b;">'NSIG'</span><span style="color: #66cc66;">,</span> <span style="color: #483d8b;">'SIGABRT'</span><span style="color: #66cc66;">,</span> <span style="color: #483d8b;">'SIGALRM'</span><span style="color: #66cc66;">,</span> <span style="color: #483d8b;">'SIGBUS'</span><span style="color: #66cc66;">,</span> <span style="color: #483d8b;">'SIGCHLD'</span><span style="color: #66cc66;">,</span> <span style="color: #483d8b;">'SIGCLD'</span><span style="color: #66cc66;">,</span> <span style="color: #483d8b;">'SIGCONT'</span><span style="color: #66cc66;">,</span> <span style="color: #483d8b;">'SIGFPE'</span><span style="color: #66cc66;">,</span> <span style="color: #483d8b;">'SIGHUP'</span><span style="color: #66cc66;">,</span> <span style="color: #483d8b;">'SIGILL'</span><span style="color: #66cc66;">,</span> <span style="color: #483d8b;">'SIGINT'</span><span style="color: #66cc66;">,</span> <span style="color: #483d8b;">'SIGIO'</span><span style="color: #66cc66;">,</span> <span style="color: #483d8b;">'SIGIOT'</span><span style="color: #66cc66;">,</span> <span style="color: #483d8b;">'SIGKILL'</span><span style="color: #66cc66;">,</span> <span style="color: #483d8b;">'SIGPIPE'</span><span style="color: #66cc66;">,</span> <span style="color: #483d8b;">'SIGPOLL'</span><span style="color: #66cc66;">,</span> <span style="color: #483d8b;">'SIGPROF'</span><span style="color: #66cc66;">,</span> <span style="color: #483d8b;">'SIGPWR'</span><span style="color: #66cc66;">,</span> <span style="color: #483d8b;">'SIGQUIT'</span><span style="color: #66cc66;">,</span> <span style="color: #483d8b;">'SIGRTMAX'</span><span style="color: #66cc66;">,</span> <span style="color: #483d8b;">'SIGRTMIN'</span><span style="color: #66cc66;">,</span> <span style="color: #483d8b;">'SIGSEGV'</span><span style="color: #66cc66;">,</span> <span style="color: #483d8b;">'SIGSTOP'</span><span style="color: #66cc66;">,</span> <span style="color: #483d8b;">'SIGSYS'</span><span style="color: #66cc66;">,</span> <span style="color: #483d8b;">'SIGTERM'</span><span style="color: #66cc66;">,</span> <span style="color: #483d8b;">'SIGTRAP'</span><span style="color: #66cc66;">,</span> <span style="color: #483d8b;">'SIGTSTP'</span><span style="color: #66cc66;">,</span> <span style="color: #483d8b;">'SIGTTIN'</span><span style="color: #66cc66;">,</span> <span style="color: #483d8b;">'SIGTTOU'</span><span style="color: #66cc66;">,</span> <span style="color: #483d8b;">'SIGURG'</span><span style="color: #66cc66;">,</span> <span style="color: #483d8b;">'SIGUSR1'</span><span style="color: #66cc66;">,</span> <span style="color: #483d8b;">'SIGUSR2'</span><span style="color: #66cc66;">,</span> <span style="color: #483d8b;">'SIGVTALRM'</span><span style="color: #66cc66;">,</span> <span style="color: #483d8b;">'SIGWINCH'</span><span style="color: #66cc66;">,</span> <span style="color: #483d8b;">'SIGXCPU'</span><span style="color: #66cc66;">,</span> <span style="color: #483d8b;">'SIGXFSZ'</span><span style="color: #66cc66;">,</span> <span style="color: #483d8b;">'SIG_BLOCK'</span><span style="color: #66cc66;">,</span> <span style="color: #483d8b;">'SIG_DFL'</span><span style="color: #66cc66;">,</span> <span style="color: #483d8b;">'SIG_IGN'</span><span style="color: #66cc66;">,</span> <span style="color: #483d8b;">'SIG_SETMASK'</span><span style="color: #66cc66;">,</span> <span style="color: #483d8b;">'SIG_UNBLOCK'</span><span style="color: black;">&#93;</span></pre></td></tr></table></div>

<p>En plus, certains sont Windows only, d&#8217;autres Unix only&#8230;</p>
<p>Et pour faciliter les choses, les handlers peuvent être des entiers (bon, des enums dont <code>__eq__</code> accepte les entiers&#8230;):</p>

<div class="wp_syntax"><table><tr><td class="code"><pre class="python" style="font-family:monospace;"><span style="color: #66cc66;">&gt;&gt;&gt;</span> <span style="color: #008000;">type</span><span style="color: black;">&#40;</span><span style="color: #dc143c;">signal</span>.<span style="color: black;">getsignal</span><span style="color: black;">&#40;</span><span style="color: #dc143c;">signal</span>.<span style="color: black;">SIGKILL</span><span style="color: black;">&#41;</span><span style="color: black;">&#41;</span>
<span style="color: #66cc66;">&lt;</span>enum <span style="color: #483d8b;">'Handlers'</span><span style="color: #66cc66;">&gt;</span>
&nbsp;
<span style="color: #66cc66;">&gt;&gt;&gt;</span> <span style="color: #008000;">type</span><span style="color: black;">&#40;</span><span style="color: #dc143c;">signal</span>.<span style="color: black;">getsignal</span><span style="color: black;">&#40;</span><span style="color: #dc143c;">signal</span>.<span style="color: black;">SIGINT</span><span style="color: black;">&#41;</span> <span style="color: black;">&#41;</span>
<span style="color: #66cc66;">&lt;</span><span style="color: #ff7700;font-weight:bold;">class</span> <span style="color: #483d8b;">'builtin_function_or_method'</span><span style="color: #66cc66;">&gt;</span></pre></td></tr></table></div>

<p>Voici un petit snippet pour checker si un sacripant n&#8217;est pas en train de vous brouter le signal:</p>

<div class="wp_syntax"><table><tr><td class="code"><pre class="python" style="font-family:monospace;"><span style="color: #66cc66;">&gt;&gt;&gt;</span> <span style="color: #ff7700;font-weight:bold;">import</span> <span style="color: #dc143c;">signal</span>
<span style="color: #66cc66;">&gt;&gt;&gt;</span> handlers <span style="color: #66cc66;">=</span> <span style="color: black;">&#123;</span><span style="color: black;">&#125;</span>
<span style="color: #66cc66;">&gt;&gt;&gt;</span> <span style="color: #ff7700;font-weight:bold;">for</span> s <span style="color: #ff7700;font-weight:bold;">in</span> <span style="color: #008000;">dir</span><span style="color: black;">&#40;</span><span style="color: #dc143c;">signal</span><span style="color: black;">&#41;</span>:
...     <span style="color: #ff7700;font-weight:bold;">if</span> s.<span style="color: black;">isupper</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>: <span style="color: #808080; font-style: italic;"># on chope uniquement les constantes</span>
...         <span style="color: #ff7700;font-weight:bold;">try</span>:
...             <span style="color: black;">handler</span> <span style="color: #66cc66;">=</span> <span style="color: #008000;">getattr</span><span style="color: black;">&#40;</span><span style="color: #dc143c;">signal</span><span style="color: #66cc66;">,</span> s<span style="color: black;">&#41;</span> <span style="color: #808080; font-style: italic;"># quel handler pour ce signal ?</span>
...             <span style="color: #ff7700;font-weight:bold;">if</span> <span style="color: #008000;">callable</span><span style="color: black;">&#40;</span>handler<span style="color: black;">&#41;</span>: <span style="color: #808080; font-style: italic;"># on ignore SIG_DFL, SIG_IGN, etc.</span>
...                 <span style="color: black;">handlers</span><span style="color: black;">&#91;</span>s<span style="color: black;">&#93;</span> <span style="color: #66cc66;">=</span> handler <span style="color: #808080; font-style: italic;"># ok, on a un truc gégé !</span>
...         <span style="color: #ff7700;font-weight:bold;">except</span> <span style="color: #008000;">ValueError</span>:
...            <span style="color: #ff7700;font-weight:bold;">pass</span>
<span style="color: #66cc66;">&gt;&gt;&gt;</span> <span style="color: #ff7700;font-weight:bold;">print</span><span style="color: black;">&#40;</span>handlers<span style="color: black;">&#91;</span>s<span style="color: black;">&#93;</span><span style="color: black;">&#41;</span></pre></td></tr></table></div>

<p>On s&#8217;en sert pas souvent, mais je me suis dis que je ferai passer le mot.</p>


feed_id: http://sametmax.com/?p=18443
title: Open bar sur asyncio &#160; Recently updated !
uri: Open-bar-sur-asyncio-Recently-updated
url: http://sametmax.com/open-bar-sur-asyncio/
content:
<p>Plus je fais joujou avec asyncio, plus j&#8217;apprécie la lib. Mais je tombe aussi sur des tas de petits trucs qui me font dire qu&#8217;il va falloir créer quelques couches d&#8217;abstraction pour rendre tout ça plus miam.</p>
<p>Par exemple, lire de manière asynchrone les données pipées sur stdin:</p>

<div class="wp_syntax"><table><tr><td class="code"><pre class="python" style="font-family:monospace;">async <span style="color: #ff7700;font-weight:bold;">def</span> main<span style="color: black;">&#40;</span>loop<span style="color: black;">&#41;</span>:
&nbsp;
    reader <span style="color: #66cc66;">=</span> asyncio.<span style="color: black;">StreamReader</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
    <span style="color: #ff7700;font-weight:bold;">def</span> get_reader<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>:
        <span style="color: #ff7700;font-weight:bold;">return</span> asyncio.<span style="color: black;">StreamReaderProtocol</span><span style="color: black;">&#40;</span>reader<span style="color: black;">&#41;</span>
&nbsp;
    await loop.<span style="color: black;">connect_read_pipe</span><span style="color: black;">&#40;</span>get_reader<span style="color: #66cc66;">,</span> <span style="color: #dc143c;">sys</span>.<span style="color: black;">stdin</span><span style="color: black;">&#41;</span>
&nbsp;
    <span style="color: #ff7700;font-weight:bold;">while</span> <span style="color: #008000;">True</span>:
        line <span style="color: #66cc66;">=</span> await reader.<span style="color: #dc143c;">readline</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
        <span style="color: #ff7700;font-weight:bold;">if</span> <span style="color: #ff7700;font-weight:bold;">not</span> line:
            <span style="color: #ff7700;font-weight:bold;">break</span>
        <span style="color: #ff7700;font-weight:bold;">print</span><span style="color: black;">&#40;</span>line<span style="color: black;">&#41;</span>
&nbsp;
loop <span style="color: #66cc66;">=</span> asyncio.<span style="color: black;">get_event_loop</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
loop.<span style="color: black;">run_until_complete</span><span style="color: black;">&#40;</span>main<span style="color: black;">&#40;</span>loop<span style="color: black;">&#41;</span><span style="color: black;">&#41;</span></pre></td></tr></table></div>

<p>C&#8217;est assez verbeux, pas très Pythonique (une factory qui instancie un classe a qui on passe une autre instance, c&#8217;est très java), ça n’exploite pas toute la force de <code>async</code>/<code>await</code> (le <code>while</code> qui pourrait être un <code>async for</code>), et il faut se soucier de lancer la boucle. </p>
<p>En prime, c&#8217;est pas encore évident de trouver qu&#8217;il faut faire ça dans la doc.</p>
<p>Pareil, pour lire de manière asynchrone les données écrites par un utilisateur sur stdin:</p>

<div class="wp_syntax"><table><tr><td class="code"><pre class="python" style="font-family:monospace;"><span style="color: #ff7700;font-weight:bold;">def</span> on_stdin<span style="color: black;">&#40;</span>*args<span style="color: black;">&#41;</span>:
    <span style="color: #ff7700;font-weight:bold;">print</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">&quot;Somebody wrote:&quot;</span><span style="color: #66cc66;">,</span> <span style="color: #dc143c;">sys</span>.<span style="color: black;">stdin</span>.<span style="color: #dc143c;">readline</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span><span style="color: black;">&#41;</span>
&nbsp;
loop <span style="color: #66cc66;">=</span> asyncio.<span style="color: black;">get_event_loop</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
loop.<span style="color: black;">add_reader</span><span style="color: black;">&#40;</span><span style="color: #dc143c;">sys</span>.<span style="color: black;">stdin</span>.<span style="color: black;">fileno</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span><span style="color: #66cc66;">,</span> on_stdin<span style="color: black;">&#41;</span>
loop.<span style="color: black;">run_forever</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span></pre></td></tr></table></div>

<p>Bon, là on a du callback, clairement on peut faire mieux. </p>
<p>La bonne nouvelle, c&#8217;est que ça veut dire qu&#8217;on a un champ entier où on peut être le premier à écrire une lib, et donc devenir une implémentation de référence, un outil connu, etc. Si vous avez envie de faire votre trou, c&#8217;est une opportunité, et en plus, c&#8217;est rigolo :)</p>
<p>En effet, avec un peu d&#8217;enrobage, on peut rapidement faire des trucs choupinets:</p>

<div class="wp_syntax"><table><tr><td class="code"><pre class="python" style="font-family:monospace;"><span style="color: #ff7700;font-weight:bold;">class</span> AioStdinPipe:
&nbsp;
    <span style="color: #ff7700;font-weight:bold;">def</span> <span style="color: #0000cd;">__init__</span><span style="color: black;">&#40;</span><span style="color: #008000;">self</span><span style="color: #66cc66;">,</span> loop<span style="color: #66cc66;">=</span><span style="color: #008000;">None</span><span style="color: black;">&#41;</span>:
&nbsp;
        <span style="color: #008000;">self</span>.<span style="color: black;">loop</span> <span style="color: #66cc66;">=</span> loop <span style="color: #ff7700;font-weight:bold;">or</span> asyncio.<span style="color: black;">get_event_loop</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
        <span style="color: #008000;">self</span>.<span style="color: black;">reader</span> <span style="color: #66cc66;">=</span> asyncio.<span style="color: black;">StreamReader</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
&nbsp;
    <span style="color: #ff7700;font-weight:bold;">def</span> get_reader<span style="color: black;">&#40;</span><span style="color: #008000;">self</span><span style="color: black;">&#41;</span>:
        <span style="color: #ff7700;font-weight:bold;">return</span> asyncio.<span style="color: black;">StreamReaderProtocol</span><span style="color: black;">&#40;</span><span style="color: #008000;">self</span>.<span style="color: black;">reader</span><span style="color: black;">&#41;</span>
&nbsp;
    async <span style="color: #ff7700;font-weight:bold;">def</span> __aiter__<span style="color: black;">&#40;</span><span style="color: #008000;">self</span><span style="color: black;">&#41;</span>:
        await <span style="color: #008000;">self</span>.<span style="color: black;">loop</span>.<span style="color: black;">connect_read_pipe</span><span style="color: black;">&#40;</span><span style="color: #008000;">self</span>.<span style="color: black;">get_reader</span><span style="color: #66cc66;">,</span> <span style="color: #dc143c;">sys</span>.<span style="color: black;">stdin</span><span style="color: black;">&#41;</span>
        <span style="color: #ff7700;font-weight:bold;">return</span> <span style="color: #008000;">self</span>
&nbsp;
    async <span style="color: #ff7700;font-weight:bold;">def</span> __anext__<span style="color: black;">&#40;</span><span style="color: #008000;">self</span><span style="color: black;">&#41;</span>:
        <span style="color: #ff7700;font-weight:bold;">while</span> <span style="color: #008000;">True</span>:
            val <span style="color: #66cc66;">=</span> await <span style="color: #008000;">self</span>.<span style="color: black;">reader</span>.<span style="color: #dc143c;">readline</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
            <span style="color: #ff7700;font-weight:bold;">if</span> val <span style="color: #66cc66;">==</span> b<span style="color: #483d8b;">''</span>:
                <span style="color: #ff7700;font-weight:bold;">raise</span> StopAsyncIteration
            <span style="color: #ff7700;font-weight:bold;">return</span> val
&nbsp;
&nbsp;
<span style="color: #ff7700;font-weight:bold;">def</span> run_in_loop<span style="color: black;">&#40;</span>coro<span style="color: black;">&#41;</span>:
    loop <span style="color: #66cc66;">=</span> asyncio.<span style="color: black;">get_event_loop</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
    loop.<span style="color: black;">run_until_complete</span><span style="color: black;">&#40;</span>coro<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span><span style="color: black;">&#41;</span></pre></td></tr></table></div>

<p>Et ainsi réduire le premier code à un truc très simple, très clair, très Python:</p>

<div class="wp_syntax"><table><tr><td class="code"><pre class="python" style="font-family:monospace;"><span style="color: #66cc66;">@</span>run_in_loop
async <span style="color: #ff7700;font-weight:bold;">def</span> main<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>:
    async <span style="color: #ff7700;font-weight:bold;">for</span> line <span style="color: #ff7700;font-weight:bold;">in</span> AioStdinPipe<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>:
        <span style="color: #ff7700;font-weight:bold;">print</span><span style="color: black;">&#40;</span>line<span style="color: black;">&#41;</span></pre></td></tr></table></div>

<p>J&#8217;aime 2016, c&#8217;est une année pleine de possibilités.</p>


feed_id: http://sametmax.com/?p=18200
title: Il n&#8217;y a pas de mauvais script&#8230; 1 &#160; Recently updated !
uri: Il-n-y-a-pas-de-mauvais-script-1-Recently-updated
url: http://sametmax.com/il-ny-a-pas-de-mauvais-script/
content:
<blockquote><p>Ceci est un post invité de <a href="http://sametmax.com/author/atrament/">atrament </a>posté sous licence <a href="http://creativecommons.org/licenses/by/3.0/" target="_blank">creative common 3.0 unported</a>.</p></blockquote>
<p>&#8230; il n&#8217;y  a que des scripts en passe de devenir bons. Avis au débutants, croyez-y, vous ne le resterez pas.</p>
<p>C&#8217;est l&#8217;histoire d&#8217;un des premiers &#8220;vrais scripts&#8221; que j&#8217;ai écrits. Je venais de finir <a href="http://shop.oreilly.com/product/9781565924642.do">Learning Python</a> et <a href="http://shop.oreilly.com/product/9780596158118.do">Programming Python de Mark Lutz</a> (à l&#8217;époque la seconde édition <em>&#8220;couvre python 2 !&#8221;</em>).</p>
<p>J&#8217;étais même pas un script kiddie, et je ne programmais que pour m&#8217;amuser. J&#8217;étais fan de <a href="http://www.sinfest.net/">Sinfest</a>, un web comic gavé de gros blasphème qui tache.</p>
<p>Je me suis mis en tête de télécharger tout ces comics d&#8217;un coup. Voilà ce que ce script était, et ce qu&#8217;il est devenu.</p>
<p><a href="http://0bin.net/paste/X9fhVfT4zwHNwCGe#IksH-LwojHsPmoz6gtLFxeF2DU0u83J0eZCxY+P2N9O">Script originel avec formatage encore moins lisible, sur 0bin.</a></p>

<div class="wp_syntax"><table><tr><td class="code"><pre class="python" style="font-family:monospace;"><span style="color: #808080; font-style: italic;">#~ Script to DL all sinfest webcomics</span>
<span style="color: #808080; font-style: italic;">#~ one can edit the START and END parameters to DL a subset</span>
STARTDAY <span style="color: #66cc66;">=</span> <span style="color: #ff4500;">17</span>  <span style="color: #808080; font-style: italic;"># 17</span>
STARTMONTH <span style="color: #66cc66;">=</span> <span style="color: #ff4500;">01</span>  <span style="color: #808080; font-style: italic;"># 01</span>
STARTYEAR <span style="color: #66cc66;">=</span> <span style="color: #ff4500;">2011</span>  <span style="color: #808080; font-style: italic;"># 2000</span>
ENDDAY <span style="color: #66cc66;">=</span> <span style="color: #ff4500;">31</span>
ENDMONTH <span style="color: #66cc66;">=</span> <span style="color: #ff4500;">01</span>
ENDYEAR <span style="color: #66cc66;">=</span> <span style="color: #ff4500;">2012</span>
ROOTDIR <span style="color: #66cc66;">=</span> <span style="color: #483d8b;">'Sinfest'</span>
ROOTURL <span style="color: #66cc66;">=</span> <span style="color: #483d8b;">&quot;http://www.sinfest.net/comikaze/comics/&quot;</span>
IMGEXT <span style="color: #66cc66;">=</span> <span style="color: #483d8b;">&quot;.gif&quot;</span>
<span style="color: #808080; font-style: italic;">#~ Code start - no edition from here unless you know what you're doing</span>
<span style="color: #808080; font-style: italic;">#~ --------------------</span>
<span style="color: #ff7700;font-weight:bold;">from</span> <span style="color: #dc143c;">distutils</span>.<span style="color: black;">file_util</span> <span style="color: #ff7700;font-weight:bold;">import</span> copy_file
<span style="color: #ff7700;font-weight:bold;">from</span> <span style="color: #dc143c;">urllib</span> <span style="color: #ff7700;font-weight:bold;">import</span> urlopen
<span style="color: #ff7700;font-weight:bold;">import</span> <span style="color: #dc143c;">os</span>.<span style="color: black;">path</span>
<span style="color: #ff7700;font-weight:bold;">import</span> <span style="color: #dc143c;">os</span>
<span style="color: #808080; font-style: italic;">#~ ROOTDIR=os.getcwd()+ROOTDIR</span>
<span style="color: #808080; font-style: italic;">#~ Check if date is out of range</span>
&nbsp;
&nbsp;
<span style="color: #ff7700;font-weight:bold;">def</span> checkdate<span style="color: black;">&#40;</span>d<span style="color: #66cc66;">,</span> m<span style="color: #66cc66;">,</span> y<span style="color: #66cc66;">,</span> ed<span style="color: #66cc66;">,</span> em<span style="color: #66cc66;">,</span> ey<span style="color: #66cc66;">,</span> sd<span style="color: #66cc66;">,</span> sm<span style="color: #66cc66;">,</span> sy<span style="color: black;">&#41;</span>:  <span style="color: #808080; font-style: italic;"># end date is ed, em, ey</span>
    <span style="color: #ff7700;font-weight:bold;">if</span> y &amp;gt<span style="color: #66cc66;">;</span> ey <span style="color: #ff7700;font-weight:bold;">or</span> <span style="color: black;">&#40;</span>y <span style="color: #66cc66;">==</span> ey <span style="color: #ff7700;font-weight:bold;">and</span> m &amp;gt<span style="color: #66cc66;">;</span> em<span style="color: black;">&#41;</span> <span style="color: #ff7700;font-weight:bold;">or</span> <span style="color: black;">&#40;</span>y <span style="color: #66cc66;">==</span> ey <span style="color: #ff7700;font-weight:bold;">and</span> m <span style="color: #66cc66;">==</span> em <span style="color: #ff7700;font-weight:bold;">and</span> d &amp;gt<span style="color: #66cc66;">;</span> ed<span style="color: black;">&#41;</span>:
        <span style="color: #ff7700;font-weight:bold;">return</span> <span style="color: #ff4500;">0</span>  <span style="color: #808080; font-style: italic;"># date over date range</span>
    <span style="color: #ff7700;font-weight:bold;">elif</span> y &amp;lt<span style="color: #66cc66;">;</span> sy <span style="color: #ff7700;font-weight:bold;">or</span> <span style="color: black;">&#40;</span>y <span style="color: #66cc66;">==</span> sy <span style="color: #ff7700;font-weight:bold;">and</span> m &amp;lt<span style="color: #66cc66;">;</span> sm<span style="color: black;">&#41;</span> <span style="color: #ff7700;font-weight:bold;">or</span> <span style="color: black;">&#40;</span>y <span style="color: #66cc66;">==</span> sy <span style="color: #ff7700;font-weight:bold;">and</span> m <span style="color: #66cc66;">==</span> sm <span style="color: #ff7700;font-weight:bold;">and</span> d &amp;lt<span style="color: #66cc66;">;</span> sd<span style="color: black;">&#41;</span>:
        <span style="color: #ff7700;font-weight:bold;">return</span> <span style="color: #ff4500;">0</span>
    <span style="color: #ff7700;font-weight:bold;">else</span>:
        <span style="color: #ff7700;font-weight:bold;">return</span> <span style="color: #ff4500;">1</span>  <span style="color: #808080; font-style: italic;"># date below end date and above start date</span>
<span style="color: #808080; font-style: italic;">#~ make the file name teh sae format than sinfest server</span>
&nbsp;
&nbsp;
<span style="color: #ff7700;font-weight:bold;">def</span> getfilename<span style="color: black;">&#40;</span>y<span style="color: #66cc66;">,</span> m<span style="color: #66cc66;">,</span> d<span style="color: #66cc66;">,</span> IMGEXT<span style="color: black;">&#41;</span>:
    M <span style="color: #66cc66;">=</span>`m`
    D <span style="color: #66cc66;">=</span>`d`
    Y <span style="color: #66cc66;">=</span>`y`
    <span style="color: #ff7700;font-weight:bold;">if</span> <span style="color: #008000;">len</span><span style="color: black;">&#40;</span>M<span style="color: black;">&#41;</span> &amp;lt<span style="color: #66cc66;">;</span> <span style="color: #ff4500;">2</span>:
        M <span style="color: #66cc66;">=</span> <span style="color: #483d8b;">'0'</span> + M
    <span style="color: #ff7700;font-weight:bold;">if</span> <span style="color: #008000;">len</span><span style="color: black;">&#40;</span>D<span style="color: black;">&#41;</span> &amp;lt<span style="color: #66cc66;">;</span> <span style="color: #ff4500;">2</span>:
        D <span style="color: #66cc66;">=</span> <span style="color: #483d8b;">'0'</span> + D
    filename <span style="color: #66cc66;">=</span> Y + <span style="color: #483d8b;">'-'</span> + M + <span style="color: #483d8b;">'-'</span> + D + IMGEXT
    <span style="color: #ff7700;font-weight:bold;">return</span> filename
<span style="color: #808080; font-style: italic;">#~ run the loop</span>
<span style="color: #ff7700;font-weight:bold;">if</span> <span style="color: #ff7700;font-weight:bold;">not</span> <span style="color: #dc143c;">os</span>.<span style="color: black;">path</span>.<span style="color: black;">isdir</span><span style="color: black;">&#40;</span>ROOTDIR<span style="color: black;">&#41;</span>:
    <span style="color: #dc143c;">os</span>.<span style="color: black;">mkdir</span><span style="color: black;">&#40;</span>ROOTDIR<span style="color: black;">&#41;</span>
<span style="color: #ff7700;font-weight:bold;">for</span> y <span style="color: #ff7700;font-weight:bold;">in</span> <span style="color: #008000;">range</span><span style="color: black;">&#40;</span>STARTYEAR<span style="color: #66cc66;">,</span> ENDYEAR + <span style="color: #ff4500;">1</span><span style="color: black;">&#41;</span>:
    <span style="color: #ff7700;font-weight:bold;">for</span> m <span style="color: #ff7700;font-weight:bold;">in</span> <span style="color: #008000;">range</span><span style="color: black;">&#40;</span><span style="color: #ff4500;">1</span><span style="color: #66cc66;">,</span> <span style="color: #ff4500;">13</span><span style="color: black;">&#41;</span>:
        <span style="color: #ff7700;font-weight:bold;">for</span> d <span style="color: #ff7700;font-weight:bold;">in</span> <span style="color: #008000;">range</span><span style="color: black;">&#40;</span><span style="color: #ff4500;">1</span><span style="color: #66cc66;">,</span> <span style="color: #ff4500;">32</span><span style="color: black;">&#41;</span>:
            filename <span style="color: #66cc66;">=</span> getfilename<span style="color: black;">&#40;</span>y<span style="color: #66cc66;">,</span> m<span style="color: #66cc66;">,</span> d<span style="color: #66cc66;">,</span> IMGEXT<span style="color: black;">&#41;</span>
            <span style="color: #ff7700;font-weight:bold;">if</span> checkdate<span style="color: black;">&#40;</span>d<span style="color: #66cc66;">,</span> m<span style="color: #66cc66;">,</span> y<span style="color: #66cc66;">,</span> ENDDAY<span style="color: #66cc66;">,</span> ENDMONTH<span style="color: #66cc66;">,</span> ENDYEAR<span style="color: #66cc66;">,</span>
                         STARTDAY<span style="color: #66cc66;">,</span> STARTMONTH<span style="color: #66cc66;">,</span> STARTYEAR<span style="color: black;">&#41;</span>:
                <span style="color: #ff7700;font-weight:bold;">if</span> <span style="color: #dc143c;">os</span>.<span style="color: black;">path</span>.<span style="color: black;">isfile</span><span style="color: black;">&#40;</span>ROOTDIR + <span style="color: #483d8b;">'/'</span> + filename<span style="color: black;">&#41;</span>:
                    <span style="color: #ff7700;font-weight:bold;">print</span> <span style="color: #483d8b;">'['</span> + filename + <span style="color: #483d8b;">'] already exists'</span>
                <span style="color: #ff7700;font-weight:bold;">else</span>:
                    src <span style="color: #66cc66;">=</span> urlopen<span style="color: black;">&#40;</span>ROOTURL + filename<span style="color: black;">&#41;</span>
                    <span style="color: #ff7700;font-weight:bold;">if</span> src.<span style="color: black;">info</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>.<span style="color: black;">gettype</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span> <span style="color: #66cc66;">==</span> <span style="color: #483d8b;">'image/gif'</span>:
                        dst <span style="color: #66cc66;">=</span> <span style="color: #008000;">open</span><span style="color: black;">&#40;</span>ROOTDIR + <span style="color: #483d8b;">'/'</span> + filename<span style="color: #66cc66;">,</span> <span style="color: #483d8b;">'wb'</span><span style="color: black;">&#41;</span>
                        dst.<span style="color: black;">write</span><span style="color: black;">&#40;</span>src.<span style="color: black;">read</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span><span style="color: black;">&#41;</span>
                        dst.<span style="color: black;">close</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
                        <span style="color: #ff7700;font-weight:bold;">print</span> <span style="color: #483d8b;">'['</span> + filename + <span style="color: #483d8b;">']'</span> + <span style="color: #483d8b;">' copied'</span>
                    <span style="color: #ff7700;font-weight:bold;">else</span>:
                        <span style="color: #ff7700;font-weight:bold;">print</span> <span style="color: #483d8b;">&quot;MIMETYPE ERROR (probably ERROR 404) ignored&quot;</span>
                    src.<span style="color: black;">close</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span></pre></td></tr></table></div>

<p>On remarquera l&#8217;arrogance du jeune codeur fier de lui dans certains commentaires. Ils étaient à mon intention, en vrai, et montraient surtout que je ne savais pas trop ce que je faisais.</p>
<p>En vrac, parmi les choses qui me font sourire aujourd&#8217;hui :</p>
<ul>
<li>la fonction <code>checkdate</code> qui prend 9 paramètres pour vérifier si une date est entre deux autres,</li>
<li>les grosses constantes au début</li>
<li>la portabilité nulle du script qui doit être lancé au bon endroit</li>
<li>la date codée avec 3 boucles pour l&#8217;année, le mois, le jour.</li>
<li>Je ne parle même pas de la pep008, elle n&#8217;avait même pas un an, d&#8217;abord, et puis visiblement on était loin des questions de style&#8230;</li>
</ul>
<p>Bon, il y a prescription, et puis je suis pas mal autodidacte, c&#8217;était pas si mal : ça fonctionnait ! Ça m&#8217;a quand même servi, de temps en temps, jusque 2012, pour remettre à jour mes dossiers d&#8217;images.</p>
<hr />
<p>Et puis je l&#8217;ai relu et j&#8217;ai décidé de le reprendre avec ce que je savais de nouveau :</p>
<ul>
<li>le module <code>datetime</code>,</li>
<li>la construction <code>if __name__ == '__main__'</code>,</li>
<li>les exceptions,</li>
</ul>
<p>et ça a donné ça :</p>

<div class="wp_syntax"><table><tr><td class="code"><pre class="python" style="font-family:monospace;"><span style="color: #808080; font-style: italic;"># -*- coding: utf-8 -*-</span>
<span style="color: #808080; font-style: italic;">#! /usr/bin/python</span>
<span style="color: #808080; font-style: italic;"># Script to DL all sinfest webcomics</span>
<span style="color: #808080; font-style: italic;"># one can edit the START and END parameters to DL a subset</span>
<span style="color: #808080; font-style: italic;">#-------------------------------------------------------------------------------</span>
<span style="color: #808080; font-style: italic;"># Name:        getSinfest</span>
<span style="color: #808080; font-style: italic;"># Purpose:</span>
<span style="color: #808080; font-style: italic;">#</span>
<span style="color: #808080; font-style: italic;"># Author:      Atrament</span>
<span style="color: #808080; font-style: italic;">#</span>
<span style="color: #808080; font-style: italic;"># Created:     12/04/2013</span>
<span style="color: #808080; font-style: italic;"># Copyright:   (c) Atrament 2013</span>
<span style="color: #808080; font-style: italic;"># Licence:     </span>
<span style="color: #808080; font-style: italic;">#-------------------------------------------------------------------------------</span>
&nbsp;
<span style="color: #ff7700;font-weight:bold;">import</span> <span style="color: #dc143c;">datetime</span>
<span style="color: #ff7700;font-weight:bold;">from</span> <span style="color: #dc143c;">urllib</span>.<span style="color: black;">request</span> <span style="color: #ff7700;font-weight:bold;">import</span> urlopen
<span style="color: #ff7700;font-weight:bold;">import</span> <span style="color: #dc143c;">os</span>.<span style="color: black;">path</span>
<span style="color: #ff7700;font-weight:bold;">import</span> <span style="color: #dc143c;">os</span>
<span style="color: #ff7700;font-weight:bold;">from</span> <span style="color: #dc143c;">urllib</span>.<span style="color: black;">error</span> <span style="color: #ff7700;font-weight:bold;">import</span> HTTPError
&nbsp;
&nbsp;
<span style="color: #ff7700;font-weight:bold;">def</span> main<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>:
    debut<span style="color: #66cc66;">=</span><span style="color: #dc143c;">datetime</span>.<span style="color: black;">date</span><span style="color: black;">&#40;</span><span style="color: #ff4500;">2000</span><span style="color: #66cc66;">,</span><span style="color: #ff4500;">1</span><span style="color: #66cc66;">,</span><span style="color: #ff4500;">17</span><span style="color: black;">&#41;</span>
    curGif<span style="color: #66cc66;">=</span>debut
    <span style="color: #ff7700;font-weight:bold;">if</span> <span style="color: #ff7700;font-weight:bold;">not</span> <span style="color: #dc143c;">os</span>.<span style="color: black;">path</span>.<span style="color: black;">isdir</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">&quot;Sinfest&quot;</span><span style="color: black;">&#41;</span>:
        <span style="color: #dc143c;">os</span>.<span style="color: black;">mkdir</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">&quot;Sinfest&quot;</span><span style="color: black;">&#41;</span>
    <span style="color: #ff7700;font-weight:bold;">while</span> curGif&amp;lt<span style="color: #66cc66;">;=</span><span style="color: #dc143c;">datetime</span>.<span style="color: black;">date</span>.<span style="color: black;">today</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>:
        <span style="color: #ff7700;font-weight:bold;">if</span> <span style="color: #ff7700;font-weight:bold;">not</span> <span style="color: #dc143c;">os</span>.<span style="color: black;">path</span>.<span style="color: black;">isfile</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">&quot;Sinfest/&quot;</span>+curGif.<span style="color: black;">isoformat</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>+<span style="color: #483d8b;">&quot;.gif&quot;</span><span style="color: black;">&#41;</span>:
            <span style="color: #ff7700;font-weight:bold;">try</span>:
                src<span style="color: #66cc66;">=</span>urlopen<span style="color: black;">&#40;</span><span style="color: #483d8b;">&quot;http://www.sinfest.net/comikaze/comics/&quot;</span>+curGif.<span style="color: black;">isoformat</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>+<span style="color: #483d8b;">&quot;.gif&quot;</span><span style="color: black;">&#41;</span>
                dst<span style="color: #66cc66;">=</span><span style="color: #008000;">open</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">&quot;Sinfest/&quot;</span>+curGif.<span style="color: black;">isoformat</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>+<span style="color: #483d8b;">&quot;.gif&quot;</span><span style="color: #66cc66;">,</span><span style="color: #483d8b;">'wb'</span><span style="color: black;">&#41;</span>
                dst.<span style="color: black;">write</span><span style="color: black;">&#40;</span>src.<span style="color: black;">read</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span><span style="color: black;">&#41;</span>
                dst.<span style="color: black;">close</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
                <span style="color: #ff7700;font-weight:bold;">print</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">&quot;    &quot;</span>+curGif.<span style="color: black;">isoformat</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>+<span style="color: #483d8b;">&quot;.gif : fetched.&quot;</span><span style="color: black;">&#41;</span>
                src.<span style="color: black;">close</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
            <span style="color: #ff7700;font-weight:bold;">except</span> HTTPError <span style="color: #ff7700;font-weight:bold;">as</span> e:
                <span style="color: #ff7700;font-weight:bold;">print</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">&quot;on &quot;</span>+curGif.<span style="color: black;">isoformat</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>+<span style="color: #483d8b;">&quot; error happened. So is life.&quot;</span><span style="color: black;">&#41;</span>
        <span style="color: #ff7700;font-weight:bold;">else</span>:
            <span style="color: #ff7700;font-weight:bold;">print</span><span style="color: black;">&#40;</span>curGif.<span style="color: black;">isoformat</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>+<span style="color: #483d8b;">&quot; : ok&quot;</span><span style="color: black;">&#41;</span>
        curGif+<span style="color: #66cc66;">=</span><span style="color: #dc143c;">datetime</span>.<span style="color: black;">timedelta</span><span style="color: black;">&#40;</span>days<span style="color: #66cc66;">=</span><span style="color: #ff4500;">1</span><span style="color: black;">&#41;</span>
&nbsp;
<span style="color: #ff7700;font-weight:bold;">if</span> __name__ <span style="color: #66cc66;">==</span> <span style="color: #483d8b;">'__main__'</span>:
    main<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span></pre></td></tr></table></div>

<p>Et honnêtement ça s&#8217;améliore (un peu): un shebang (pas tout à fait en haut du fichier, mais j&#8217;apprendrai plus tard), bien que je code à l&#8217;époque uniquement sous windows avec pyscripter, qui me fournit les commentaires de début de fichier, qui ne servent à rien, mais à l&#8217;époque m&#8217;éclatent. Ben oui, à ce moment là, je suis à peine adulte, et avec les heures que j&#8217;ai passé sur IDLE à tâtonnner, c&#8217;est une victoire pour moi.</p>
<hr />
<p>C&#8217;est un peu plus tard encore que j&#8217;apprends que les requêtes internet qui prennent des heures, c&#8217;est pas obligatoire, parce qu&#8217;on peut faire du <em>multi thread</em>, et continuer de tourner pendant qu&#8217;on attend qu&#8217;une autre tâche s&#8217;accomplisse. Voilà de quoi faire rêver un jeune programmeur : la puissance du multi-coeurs à la portée de mon code ! (non, je ne savais pas que c&#8217;est faux). En conséquence, mon script évolue, pour devenir&#8230; ça. Attention, ça pique les yeux.</p>

<div class="wp_syntax"><table><tr><td class="code"><pre class="python" style="font-family:monospace;"><span style="color: #808080; font-style: italic;"># -*- coding: UTF-8 -*-</span>
<span style="color: #808080; font-style: italic;"># ---------------------------------------------------------------------</span>
<span style="color: #808080; font-style: italic;"># Author:      Atrament</span>
<span style="color: #808080; font-style: italic;"># Licence:     CC-BY-SA https://creativecommons.org/licenses/by-sa/4.0/</span>
<span style="color: #808080; font-style: italic;"># ---------------------------------------------------------------------</span>
<span style="color: #808080; font-style: italic;"># All libs are part of standard distribution for python 3.4</span>
<span style="color: #ff7700;font-weight:bold;">import</span> <span style="color: #dc143c;">imghdr</span>
<span style="color: #ff7700;font-weight:bold;">import</span> <span style="color: #dc143c;">os</span>
<span style="color: #ff7700;font-weight:bold;">import</span> queue
<span style="color: #ff7700;font-weight:bold;">from</span> <span style="color: #dc143c;">threading</span> <span style="color: #ff7700;font-weight:bold;">import</span> Thread
<span style="color: #ff7700;font-weight:bold;">import</span> <span style="color: #dc143c;">datetime</span>
<span style="color: #ff7700;font-weight:bold;">from</span> <span style="color: #dc143c;">urllib</span>.<span style="color: black;">error</span> <span style="color: #ff7700;font-weight:bold;">import</span> HTTPError<span style="color: #66cc66;">,</span> URLError
<span style="color: #ff7700;font-weight:bold;">from</span> <span style="color: #dc143c;">urllib</span>.<span style="color: black;">request</span> <span style="color: #ff7700;font-weight:bold;">import</span> urlopen
<span style="color: #ff7700;font-weight:bold;">import</span> <span style="color: #dc143c;">zipfile</span>
<span style="color: #ff7700;font-weight:bold;">import</span> <span style="color: #dc143c;">sys</span>
&nbsp;
&nbsp;
<span style="color: #808080; font-style: italic;"># Useful functions</span>
<span style="color: #ff7700;font-weight:bold;">def</span> make_cbz<span style="color: black;">&#40;</span>directory<span style="color: black;">&#41;</span>:
    <span style="color: #ff7700;font-weight:bold;">for</span> year <span style="color: #ff7700;font-weight:bold;">in</span> <span style="color: #008000;">range</span><span style="color: black;">&#40;</span><span style="color: #ff4500;">2000</span><span style="color: #66cc66;">,</span> <span style="color: #dc143c;">datetime</span>.<span style="color: black;">date</span>.<span style="color: black;">today</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>.<span style="color: black;">year</span> + <span style="color: #ff4500;">1</span><span style="color: black;">&#41;</span>:
        <span style="color: #ff7700;font-weight:bold;">with</span> <span style="color: #dc143c;">zipfile</span>.<span style="color: black;">ZipFile</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">&quot;Sinfest-{}.cbz&quot;</span>.<span style="color: black;">format</span><span style="color: black;">&#40;</span>year<span style="color: black;">&#41;</span><span style="color: #66cc66;">,</span> <span style="color: #483d8b;">&quot;w&quot;</span><span style="color: black;">&#41;</span> <span style="color: #ff7700;font-weight:bold;">as</span> archive:
            <span style="color: #ff7700;font-weight:bold;">for</span> gif_file <span style="color: #ff7700;font-weight:bold;">in</span> <span style="color: black;">&#91;</span>x <span style="color: #ff7700;font-weight:bold;">for</span> x <span style="color: #ff7700;font-weight:bold;">in</span> <span style="color: #dc143c;">os</span>.<span style="color: black;">listdir</span><span style="color: black;">&#40;</span>directory<span style="color: black;">&#41;</span> <span style="color: #ff7700;font-weight:bold;">if</span> x.<span style="color: black;">split</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">&quot;-&quot;</span><span style="color: black;">&#41;</span><span style="color: black;">&#91;</span><span style="color: #ff4500;">0</span><span style="color: black;">&#93;</span> <span style="color: #66cc66;">==</span> <span style="color: #008000;">str</span><span style="color: black;">&#40;</span>year<span style="color: black;">&#41;</span><span style="color: black;">&#93;</span>:
                archive.<span style="color: black;">write</span><span style="color: black;">&#40;</span>directory + <span style="color: #483d8b;">'/'</span> + gif_file<span style="color: #66cc66;">,</span> arcname<span style="color: #66cc66;">=</span>gif_file<span style="color: black;">&#41;</span>
        <span style="color: #ff7700;font-weight:bold;">print</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">&quot;Sinfest-{}.cbz has been generated&quot;</span>.<span style="color: black;">format</span><span style="color: black;">&#40;</span>year<span style="color: black;">&#41;</span><span style="color: black;">&#41;</span>
&nbsp;
&nbsp;
<span style="color: #ff7700;font-weight:bold;">def</span> confirm<span style="color: black;">&#40;</span>prompt<span style="color: black;">&#41;</span>:
    <span style="color: #ff7700;font-weight:bold;">if</span> <span style="color: #008000;">input</span><span style="color: black;">&#40;</span>prompt + <span style="color: #483d8b;">&quot; (y/n)&quot;</span><span style="color: black;">&#41;</span> <span style="color: #ff7700;font-weight:bold;">in</span> <span style="color: #483d8b;">&quot;yY&quot;</span>:
        <span style="color: #ff7700;font-weight:bold;">return</span> <span style="color: #008000;">True</span>
    <span style="color: #ff7700;font-weight:bold;">else</span>:
        <span style="color: #ff7700;font-weight:bold;">return</span> <span style="color: #008000;">False</span>
&nbsp;
&nbsp;
<span style="color: #ff7700;font-weight:bold;">def</span> file_needs_download<span style="color: black;">&#40;</span>filename<span style="color: black;">&#41;</span>:
    <span style="color: #483d8b;">&quot;&quot;&quot;Checks whether a file exists, is corrupt, so has to be downloaded again
    also cleans garbage if detected&quot;&quot;&quot;</span>
    <span style="color: #ff7700;font-weight:bold;">if</span> <span style="color: #ff7700;font-weight:bold;">not</span> <span style="color: #dc143c;">os</span>.<span style="color: black;">path</span>.<span style="color: black;">isfile</span><span style="color: black;">&#40;</span>filename<span style="color: black;">&#41;</span>:
        <span style="color: #808080; font-style: italic;"># many comics are *supposed* to be missing,</span>
        <span style="color: #808080; font-style: italic;"># no need to output for these (uncomment for debug)</span>
        <span style="color: #808080; font-style: italic;"># print(&quot;IS NOT FILE :&quot;, filename)</span>
        <span style="color: #ff7700;font-weight:bold;">return</span> <span style="color: #008000;">True</span>
    <span style="color: #ff7700;font-weight:bold;">elif</span> <span style="color: #dc143c;">os</span>.<span style="color: black;">path</span>.<span style="color: black;">getsize</span><span style="color: black;">&#40;</span>filename<span style="color: black;">&#41;</span> <span style="color: #66cc66;">==</span> <span style="color: #ff4500;">0</span>:
        <span style="color: #ff7700;font-weight:bold;">print</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">&quot;WRONG SIZE for&quot;</span><span style="color: #66cc66;">,</span> filename<span style="color: black;">&#41;</span>
        <span style="color: #ff7700;font-weight:bold;">return</span> <span style="color: #008000;">True</span>
    <span style="color: #ff7700;font-weight:bold;">elif</span> filename.<span style="color: black;">split</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">&quot;.&quot;</span><span style="color: black;">&#41;</span><span style="color: black;">&#91;</span>-<span style="color: #ff4500;">1</span><span style="color: black;">&#93;</span> <span style="color: #66cc66;">!=</span> <span style="color: #483d8b;">&quot;gif&quot;</span>:
        <span style="color: #ff7700;font-weight:bold;">print</span><span style="color: black;">&#40;</span>filename<span style="color: #66cc66;">,</span> <span style="color: #483d8b;">&quot;IS NOT GIF&quot;</span><span style="color: black;">&#41;</span>
        <span style="color: #ff7700;font-weight:bold;">return</span> <span style="color: #008000;">True</span>
    <span style="color: #ff7700;font-weight:bold;">elif</span> filename.<span style="color: black;">split</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">&quot;.&quot;</span><span style="color: black;">&#41;</span><span style="color: black;">&#91;</span>-<span style="color: #ff4500;">1</span><span style="color: black;">&#93;</span> <span style="color: #ff7700;font-weight:bold;">in</span> <span style="color: black;">&#123;</span><span style="color: #483d8b;">&quot;jpg&quot;</span><span style="color: #66cc66;">,</span> <span style="color: #483d8b;">&quot;gif&quot;</span><span style="color: #66cc66;">,</span> <span style="color: #483d8b;">&quot;png&quot;</span><span style="color: black;">&#125;</span> <span style="color: #ff7700;font-weight:bold;">and</span> <span style="color: #dc143c;">imghdr</span>.<span style="color: black;">what</span><span style="color: black;">&#40;</span>filename<span style="color: black;">&#41;</span> <span style="color: #ff7700;font-weight:bold;">is</span> <span style="color: #008000;">None</span>:
        <span style="color: #808080; font-style: italic;"># Encoding error...</span>
        <span style="color: #ff7700;font-weight:bold;">print</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">&quot;WRONG FILE STRUCTURE for&quot;</span><span style="color: #66cc66;">,</span> filename<span style="color: black;">&#41;</span>
        <span style="color: #ff7700;font-weight:bold;">return</span> <span style="color: #008000;">True</span>
    <span style="color: #ff7700;font-weight:bold;">else</span>:
        <span style="color: #ff7700;font-weight:bold;">return</span> <span style="color: #008000;">False</span>
&nbsp;
&nbsp;
<span style="color: #ff7700;font-weight:bold;">def</span> conditional_download<span style="color: black;">&#40;</span>filename<span style="color: #66cc66;">,</span> base_url<span style="color: black;">&#41;</span>:
    <span style="color: #ff7700;font-weight:bold;">if</span> file_needs_download<span style="color: black;">&#40;</span>filename<span style="color: black;">&#41;</span>:
        <span style="color: #ff7700;font-weight:bold;">try</span>:
            src <span style="color: #66cc66;">=</span> urlopen<span style="color: black;">&#40;</span>base_url + filename<span style="color: black;">&#41;</span>
            dst <span style="color: #66cc66;">=</span> <span style="color: #008000;">open</span><span style="color: black;">&#40;</span>filename<span style="color: #66cc66;">,</span> <span style="color: #483d8b;">'wb'</span><span style="color: black;">&#41;</span>
            dst.<span style="color: black;">write</span><span style="color: black;">&#40;</span>src.<span style="color: black;">read</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span><span style="color: black;">&#41;</span>
            <span style="color: #808080; font-style: italic;"># gracefully close theses accesses.</span>
            dst.<span style="color: black;">close</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
            src.<span style="color: black;">close</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
            <span style="color: #ff7700;font-weight:bold;">print</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">&quot;<span style="color: #000099; font-weight: bold;">\t</span>&quot;</span> + filename + <span style="color: #483d8b;">&quot; : fetched.&quot;</span><span style="color: black;">&#41;</span>
        <span style="color: #ff7700;font-weight:bold;">except</span> HTTPError:
            <span style="color: #808080; font-style: italic;"># many days do not have a comic published.</span>
            <span style="color: #808080; font-style: italic;"># no need to flood the console for this.</span>
            <span style="color: #ff7700;font-weight:bold;">pass</span>
        <span style="color: #ff7700;font-weight:bold;">except</span> URLError:
            <span style="color: #ff7700;font-weight:bold;">pass</span>
            <span style="color: #ff7700;font-weight:bold;">print</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">&quot;network error on &quot;</span> + filename<span style="color: black;">&#41;</span>
        <span style="color: #ff7700;font-weight:bold;">finally</span>:
            <span style="color: #808080; font-style: italic;"># clean garbage on disk, useful if failure occurred.</span>
            file_needs_download<span style="color: black;">&#40;</span>filename<span style="color: black;">&#41;</span>
&nbsp;
&nbsp;
<span style="color: #ff7700;font-weight:bold;">class</span> ThreadedWorker<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>:
    <span style="color: #ff7700;font-weight:bold;">def</span> <span style="color: #0000cd;">__init__</span><span style="color: black;">&#40;</span><span style="color: #008000;">self</span><span style="color: #66cc66;">,</span> function<span style="color: #66cc66;">=</span><span style="color: #008000;">None</span><span style="color: #66cc66;">,</span> number_of_threads<span style="color: #66cc66;">=</span><span style="color: #ff4500;">8</span><span style="color: black;">&#41;</span>:
        <span style="color: #008000;">self</span>.<span style="color: black;">queue</span> <span style="color: #66cc66;">=</span> queue.<span style="color: #dc143c;">Queue</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
&nbsp;
        <span style="color: #ff7700;font-weight:bold;">def</span> func<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>:
            <span style="color: #ff7700;font-weight:bold;">while</span> <span style="color: #008000;">True</span>:
                item <span style="color: #66cc66;">=</span> <span style="color: #008000;">self</span>.<span style="color: black;">queue</span>.<span style="color: black;">get</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
                <span style="color: #ff7700;font-weight:bold;">if</span> function:
                    function<span style="color: black;">&#40;</span>item<span style="color: black;">&#41;</span>
                <span style="color: #ff7700;font-weight:bold;">else</span>:
                    <span style="color: #ff7700;font-weight:bold;">print</span><span style="color: black;">&#40;</span>item<span style="color: #66cc66;">,</span> <span style="color: #483d8b;">&quot;is being processed.&quot;</span><span style="color: black;">&#41;</span>
                <span style="color: #008000;">self</span>.<span style="color: black;">queue</span>.<span style="color: black;">task_done</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
&nbsp;
        <span style="color: #008000;">self</span>.<span style="color: black;">function</span> <span style="color: #66cc66;">=</span> func
&nbsp;
        <span style="color: #ff7700;font-weight:bold;">for</span> i <span style="color: #ff7700;font-weight:bold;">in</span> <span style="color: #008000;">range</span><span style="color: black;">&#40;</span>number_of_threads<span style="color: black;">&#41;</span>:
            t <span style="color: #66cc66;">=</span> Thread<span style="color: black;">&#40;</span>target<span style="color: #66cc66;">=</span><span style="color: #008000;">self</span>.<span style="color: black;">function</span><span style="color: #66cc66;">,</span> name<span style="color: #66cc66;">=</span><span style="color: #483d8b;">&quot;Thread-{:03}&quot;</span>.<span style="color: black;">format</span><span style="color: black;">&#40;</span>i<span style="color: black;">&#41;</span><span style="color: black;">&#41;</span>
            t.<span style="color: black;">daemon</span> <span style="color: #66cc66;">=</span> <span style="color: #008000;">True</span>
            t.<span style="color: black;">start</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
&nbsp;
    <span style="color: #ff7700;font-weight:bold;">def</span> put<span style="color: black;">&#40;</span><span style="color: #008000;">self</span><span style="color: #66cc66;">,</span> object_to_queue<span style="color: black;">&#41;</span>:
        <span style="color: #008000;">self</span>.<span style="color: black;">queue</span>.<span style="color: black;">put</span><span style="color: black;">&#40;</span>object_to_queue<span style="color: black;">&#41;</span>
&nbsp;
    <span style="color: #ff7700;font-weight:bold;">def</span> join<span style="color: black;">&#40;</span><span style="color: #008000;">self</span><span style="color: black;">&#41;</span>:
        <span style="color: #008000;">self</span>.<span style="color: black;">queue</span>.<span style="color: black;">join</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
&nbsp;
    <span style="color: #ff7700;font-weight:bold;">def</span> feed<span style="color: black;">&#40;</span><span style="color: #008000;">self</span><span style="color: #66cc66;">,</span> iterator<span style="color: black;">&#41;</span>:
        <span style="color: #ff7700;font-weight:bold;">for</span> task <span style="color: #ff7700;font-weight:bold;">in</span> iterator:
            <span style="color: #008000;">self</span>.<span style="color: black;">queue</span>.<span style="color: black;">put</span><span style="color: black;">&#40;</span>task<span style="color: black;">&#41;</span>
&nbsp;
&nbsp;
<span style="color: #ff7700;font-weight:bold;">def</span> download_sinfest<span style="color: black;">&#40;</span>target_folder<span style="color: black;">&#41;</span>:
    <span style="color: #483d8b;">&quot;&quot;&quot;
    Creates a directory and fetches Sinfest comics to populate it in full.
    &quot;&quot;&quot;</span>
    <span style="color: #ff7700;font-weight:bold;">if</span> <span style="color: #ff7700;font-weight:bold;">not</span> <span style="color: #dc143c;">os</span>.<span style="color: black;">path</span>.<span style="color: black;">isdir</span><span style="color: black;">&#40;</span>target_folder<span style="color: black;">&#41;</span>:
        <span style="color: #dc143c;">os</span>.<span style="color: black;">makedirs</span><span style="color: black;">&#40;</span>target_folder<span style="color: black;">&#41;</span>
    <span style="color: #dc143c;">os</span>.<span style="color: black;">chdir</span><span style="color: black;">&#40;</span>target_folder<span style="color: black;">&#41;</span>
&nbsp;
    f <span style="color: #66cc66;">=</span> <span style="color: #ff7700;font-weight:bold;">lambda</span> filename: conditional_download<span style="color: black;">&#40;</span>filename<span style="color: #66cc66;">,</span> <span style="color: #483d8b;">&quot;http://www.sinfest.net/btphp/comics/&quot;</span><span style="color: black;">&#41;</span>
    <span style="color: #808080; font-style: italic;"># Make a worker with this function and run it</span>
    t <span style="color: #66cc66;">=</span> ThreadedWorker<span style="color: black;">&#40;</span>function<span style="color: #66cc66;">=</span>f<span style="color: #66cc66;">,</span> number_of_threads<span style="color: #66cc66;">=</span><span style="color: #ff4500;">20</span><span style="color: black;">&#41;</span>
    <span style="color: #808080; font-style: italic;"># structure of comprehended list is a bit complex to generate all file names</span>
    files <span style="color: #66cc66;">=</span> <span style="color: black;">&#91;</span><span style="color: black;">&#40;</span><span style="color: #dc143c;">datetime</span>.<span style="color: black;">date</span><span style="color: black;">&#40;</span><span style="color: #ff4500;">2000</span><span style="color: #66cc66;">,</span> <span style="color: #ff4500;">1</span><span style="color: #66cc66;">,</span> <span style="color: #ff4500;">17</span><span style="color: black;">&#41;</span> + <span style="color: #dc143c;">datetime</span>.<span style="color: black;">timedelta</span><span style="color: black;">&#40;</span>days<span style="color: #66cc66;">=</span>x<span style="color: black;">&#41;</span><span style="color: black;">&#41;</span>.<span style="color: black;">isoformat</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span> + <span style="color: #483d8b;">&quot;.gif&quot;</span>
             <span style="color: #ff7700;font-weight:bold;">for</span> x <span style="color: #ff7700;font-weight:bold;">in</span> <span style="color: #008000;">range</span><span style="color: black;">&#40;</span><span style="color: black;">&#40;</span><span style="color: #dc143c;">datetime</span>.<span style="color: black;">date</span>.<span style="color: black;">today</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span> - <span style="color: #dc143c;">datetime</span>.<span style="color: black;">date</span><span style="color: black;">&#40;</span><span style="color: #ff4500;">2000</span><span style="color: #66cc66;">,</span> <span style="color: #ff4500;">1</span><span style="color: #66cc66;">,</span> <span style="color: #ff4500;">17</span><span style="color: black;">&#41;</span><span style="color: black;">&#41;</span>.<span style="color: black;">days</span> + <span style="color: #ff4500;">1</span><span style="color: black;">&#41;</span><span style="color: black;">&#93;</span>
    t.<span style="color: black;">feed</span><span style="color: black;">&#40;</span>files<span style="color: black;">&#41;</span>
    t.<span style="color: black;">join</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
&nbsp;
&nbsp;
<span style="color: #ff7700;font-weight:bold;">if</span> __name__ <span style="color: #66cc66;">==</span> <span style="color: #483d8b;">&quot;__main__&quot;</span>:
    <span style="color: #ff7700;font-weight:bold;">if</span> <span style="color: #008000;">any</span><span style="color: black;">&#40;</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">&quot;y&quot;</span><span style="color: #66cc66;">,</span> <span style="color: #483d8b;">&quot;Y&quot;</span><span style="color: #66cc66;">,</span> <span style="color: #483d8b;">&quot;-y&quot;</span><span style="color: #66cc66;">,</span> <span style="color: #483d8b;">&quot;-Y&quot;</span> <span style="color: #ff7700;font-weight:bold;">in</span> <span style="color: #dc143c;">sys</span>.<span style="color: black;">argv</span><span style="color: black;">&#41;</span><span style="color: black;">&#41;</span>:
        folder <span style="color: #66cc66;">=</span> <span style="color: #dc143c;">os</span>.<span style="color: black;">path</span>.<span style="color: black;">expanduser</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">&quot;~/Pictures/Sinfest/&quot;</span><span style="color: black;">&#41;</span>.<span style="color: black;">replace</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">&quot;<span style="color: #000099; font-weight: bold;">\\</span>&quot;</span><span style="color: #66cc66;">,</span> <span style="color: #483d8b;">&quot;/&quot;</span><span style="color: black;">&#41;</span>
        <span style="color: #ff7700;font-weight:bold;">print</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">&quot;<span style="color: #000099; font-weight: bold;">\n</span>proceeding to download...&quot;</span><span style="color: black;">&#41;</span>
        download_sinfest<span style="color: black;">&#40;</span>folder<span style="color: black;">&#41;</span>
        <span style="color: #ff7700;font-weight:bold;">print</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">&quot;<span style="color: #000099; font-weight: bold;">\n</span>generating comic book files (.cbz)...&quot;</span><span style="color: black;">&#41;</span>
        <span style="color: #dc143c;">os</span>.<span style="color: black;">chdir</span><span style="color: black;">&#40;</span>folder<span style="color: black;">&#41;</span>
        <span style="color: #dc143c;">os</span>.<span style="color: black;">chdir</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">&quot;..&quot;</span><span style="color: black;">&#41;</span>
        make_cbz<span style="color: black;">&#40;</span>folder<span style="color: black;">&#41;</span>
    <span style="color: #ff7700;font-weight:bold;">else</span>:
        folder <span style="color: #66cc66;">=</span> <span style="color: #dc143c;">os</span>.<span style="color: black;">path</span>.<span style="color: black;">expanduser</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">&quot;~/Pictures/Sinfest/&quot;</span><span style="color: black;">&#41;</span>.<span style="color: black;">replace</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">&quot;<span style="color: #000099; font-weight: bold;">\\</span>&quot;</span><span style="color: #66cc66;">,</span> <span style="color: #483d8b;">&quot;/&quot;</span><span style="color: black;">&#41;</span>
        <span style="color: #ff7700;font-weight:bold;">while</span> <span style="color: #ff7700;font-weight:bold;">not</span> confirm<span style="color: black;">&#40;</span><span style="color: #483d8b;">&quot;Target to downloads is {} ?&quot;</span>.<span style="color: black;">format</span><span style="color: black;">&#40;</span>folder<span style="color: black;">&#41;</span><span style="color: black;">&#41;</span>:
            folder <span style="color: #66cc66;">=</span> <span style="color: #008000;">input</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">&quot;Please enter new folder (N to abort) :&quot;</span><span style="color: black;">&#41;</span>
            <span style="color: #ff7700;font-weight:bold;">if</span> folder <span style="color: #ff7700;font-weight:bold;">in</span> <span style="color: #483d8b;">&quot;nN&quot;</span>:
                exit<span style="color: black;">&#40;</span><span style="color: #ff4500;">0</span><span style="color: black;">&#41;</span>
        <span style="color: #ff7700;font-weight:bold;">if</span> confirm<span style="color: black;">&#40;</span><span style="color: #483d8b;">&quot;Proceed to download ?&quot;</span><span style="color: black;">&#41;</span>:
            download_sinfest<span style="color: black;">&#40;</span>folder<span style="color: black;">&#41;</span>
        <span style="color: #ff7700;font-weight:bold;">if</span> confirm<span style="color: black;">&#40;</span><span style="color: #483d8b;">&quot;Do you want to generate cbz (comic books) files ?&quot;</span><span style="color: black;">&#41;</span>:
            <span style="color: #dc143c;">os</span>.<span style="color: black;">chdir</span><span style="color: black;">&#40;</span>folder<span style="color: black;">&#41;</span>
            <span style="color: #dc143c;">os</span>.<span style="color: black;">chdir</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">&quot;..&quot;</span><span style="color: black;">&#41;</span>
            make_cbz<span style="color: black;">&#40;</span>folder<span style="color: black;">&#41;</span>
        <span style="color: #008000;">input</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">&quot;Finished. Please press Enter&quot;</span><span style="color: black;">&#41;</span></pre></td></tr></table></div>

<p>C&#8217;est une horreur. Si vous êtes de ces perfectionnistes qui lisent le code et font la code review par habitude, je suis désolé, vous avez du pleurer.  Le commentaire sur la lib standard &#8220;en intro&#8221; montre que à ce moment là, j&#8217;ai un peu conscience qu&#8217;on peut accéder à d&#8217;autres modules, mais on est loin de pip pour moi. Je tente maladroitement d&#8217;exploiter <code>sys.argv</code> (avec des erreurs qui pourraient être catastrophiques),  et l&#8217;input à défaut. Une fonctionnalité neuve est apparue : faire des archives comic book en zip. Je suis fier.</p>
<p>Mais le côté comique de ce code, c&#8217;est la classe <code>ThreadedWorker</code>. C&#8217;est ptêt bien ma première &#8216;vraie&#8217; classe, mais j&#8217;implémente moi-même un dispatcheur de jobs sur des threads. Il y a de quoi être fier, mais on n&#8217;est pas du tout dans du python propre, là. C&#8217;est ballot, la version précédente était pas si mal, niveau clarté.</p>
<p>et dire que la lib standard en fournit un, de dispacheur de jobs&#8230;</p>
<hr />
<p>Je passe quelques itérations sur des détails, aujourd&#8217;hui il en est là, ce script.</p>

<div class="wp_syntax"><table><tr><td class="code"><pre class="python" style="font-family:monospace;"><span style="color: #808080; font-style: italic;">#! /usr/bin/env python3.5</span>
<span style="color: #ff7700;font-weight:bold;">import</span> <span style="color: #dc143c;">imghdr</span>
<span style="color: #ff7700;font-weight:bold;">import</span> <span style="color: #dc143c;">os</span>
<span style="color: #ff7700;font-weight:bold;">import</span> <span style="color: #dc143c;">datetime</span>
<span style="color: #ff7700;font-weight:bold;">import</span> <span style="color: #dc143c;">zipfile</span>
<span style="color: #ff7700;font-weight:bold;">from</span> concurrent.<span style="color: black;">futures</span> <span style="color: #ff7700;font-weight:bold;">import</span> ThreadPoolExecutor
&nbsp;
<span style="color: #ff7700;font-weight:bold;">import</span> requests
<span style="color: #ff7700;font-weight:bold;">import</span> begin
&nbsp;
&nbsp;
<span style="color: #808080; font-style: italic;"># Useful functions</span>
<span style="color: #ff7700;font-weight:bold;">def</span> make_cbz<span style="color: black;">&#40;</span>dst_directory<span style="color: #66cc66;">,</span> src_directory<span style="color: black;">&#41;</span>:
    <span style="color: #ff7700;font-weight:bold;">for</span> year <span style="color: #ff7700;font-weight:bold;">in</span> <span style="color: #008000;">range</span><span style="color: black;">&#40;</span><span style="color: #ff4500;">2000</span><span style="color: #66cc66;">,</span> <span style="color: #dc143c;">datetime</span>.<span style="color: black;">date</span>.<span style="color: black;">today</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>.<span style="color: black;">year</span> + <span style="color: #ff4500;">1</span><span style="color: black;">&#41;</span>:
        <span style="color: #ff7700;font-weight:bold;">with</span> <span style="color: #dc143c;">zipfile</span>.<span style="color: black;">ZipFile</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">&quot;Sinfest-{}.cbz&quot;</span>.<span style="color: black;">format</span><span style="color: black;">&#40;</span>year<span style="color: black;">&#41;</span><span style="color: #66cc66;">,</span> <span style="color: #483d8b;">&quot;w&quot;</span><span style="color: black;">&#41;</span> <span style="color: #ff7700;font-weight:bold;">as</span> archive:
            <span style="color: #ff7700;font-weight:bold;">for</span> filename <span style="color: #ff7700;font-weight:bold;">in</span> <span style="color: #dc143c;">os</span>.<span style="color: black;">listdir</span><span style="color: black;">&#40;</span>src_directory<span style="color: black;">&#41;</span>:
                <span style="color: #ff7700;font-weight:bold;">if</span> filename.<span style="color: black;">startswith</span><span style="color: black;">&#40;</span><span style="color: #008000;">str</span><span style="color: black;">&#40;</span>year<span style="color: black;">&#41;</span><span style="color: black;">&#41;</span>:
                    archive.<span style="color: black;">write</span><span style="color: black;">&#40;</span>src_directory + <span style="color: #483d8b;">&quot;/&quot;</span> + filename<span style="color: #66cc66;">,</span> arcname<span style="color: #66cc66;">=</span>filename<span style="color: black;">&#41;</span>
        <span style="color: #ff7700;font-weight:bold;">print</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">&quot;Sinfest-{}.cbz has been generated&quot;</span>.<span style="color: black;">format</span><span style="color: black;">&#40;</span>year<span style="color: black;">&#41;</span><span style="color: black;">&#41;</span>
&nbsp;
&nbsp;
<span style="color: #ff7700;font-weight:bold;">def</span> file_needs_download<span style="color: black;">&#40;</span>filename<span style="color: black;">&#41;</span>:
    <span style="color: #483d8b;">&quot;&quot;&quot;Checks whether a file exists, is corrupt, so has to be downloaded again
    also cleans garbage if detected&quot;&quot;&quot;</span>
    <span style="color: #ff7700;font-weight:bold;">if</span> <span style="color: #ff7700;font-weight:bold;">not</span> <span style="color: #dc143c;">os</span>.<span style="color: black;">path</span>.<span style="color: black;">isfile</span><span style="color: black;">&#40;</span>filename<span style="color: black;">&#41;</span>:
        <span style="color: #808080; font-style: italic;"># many comics are *supposed* to be missing,</span>
        <span style="color: #808080; font-style: italic;"># no need to output for these (uncomment for debug)</span>
        <span style="color: #808080; font-style: italic;"># print(&quot;IS NOT FILE :&quot;, filename)</span>
        <span style="color: #ff7700;font-weight:bold;">return</span> <span style="color: #008000;">True</span>
    <span style="color: #ff7700;font-weight:bold;">elif</span> <span style="color: #dc143c;">os</span>.<span style="color: black;">path</span>.<span style="color: black;">getsize</span><span style="color: black;">&#40;</span>filename<span style="color: black;">&#41;</span> <span style="color: #66cc66;">==</span> <span style="color: #ff4500;">0</span>:
        <span style="color: #ff7700;font-weight:bold;">print</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">&quot;WRONG SIZE for&quot;</span><span style="color: #66cc66;">,</span> filename<span style="color: black;">&#41;</span>
        <span style="color: #ff7700;font-weight:bold;">return</span> <span style="color: #008000;">True</span>
    <span style="color: #ff7700;font-weight:bold;">elif</span> filename.<span style="color: black;">split</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">&quot;.&quot;</span><span style="color: black;">&#41;</span><span style="color: black;">&#91;</span>-<span style="color: #ff4500;">1</span><span style="color: black;">&#93;</span> <span style="color: #66cc66;">!=</span> <span style="color: #483d8b;">&quot;gif&quot;</span>:
        <span style="color: #ff7700;font-weight:bold;">print</span><span style="color: black;">&#40;</span>filename<span style="color: #66cc66;">,</span> <span style="color: #483d8b;">&quot;IS NOT GIF&quot;</span><span style="color: black;">&#41;</span>
        <span style="color: #ff7700;font-weight:bold;">return</span> <span style="color: #008000;">True</span>
    <span style="color: #ff7700;font-weight:bold;">elif</span> filename.<span style="color: black;">split</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">&quot;.&quot;</span><span style="color: black;">&#41;</span><span style="color: black;">&#91;</span>-<span style="color: #ff4500;">1</span><span style="color: black;">&#93;</span> <span style="color: #ff7700;font-weight:bold;">in</span> <span style="color: black;">&#123;</span><span style="color: #483d8b;">&quot;jpg&quot;</span><span style="color: #66cc66;">,</span> <span style="color: #483d8b;">&quot;gif&quot;</span><span style="color: #66cc66;">,</span> <span style="color: #483d8b;">&quot;png&quot;</span><span style="color: black;">&#125;</span> <span style="color: #ff7700;font-weight:bold;">and</span> <span style="color: #dc143c;">imghdr</span>.<span style="color: black;">what</span><span style="color: black;">&#40;</span>filename<span style="color: black;">&#41;</span> <span style="color: #ff7700;font-weight:bold;">is</span> <span style="color: #008000;">None</span>:
        <span style="color: #808080; font-style: italic;"># Encoding error...</span>
        <span style="color: #ff7700;font-weight:bold;">print</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">&quot;WRONG FILE STRUCTURE for&quot;</span><span style="color: #66cc66;">,</span> filename<span style="color: black;">&#41;</span>
        <span style="color: #ff7700;font-weight:bold;">return</span> <span style="color: #008000;">True</span>
    <span style="color: #ff7700;font-weight:bold;">else</span>:
        <span style="color: #ff7700;font-weight:bold;">return</span> <span style="color: #008000;">False</span>
&nbsp;
&nbsp;
<span style="color: #ff7700;font-weight:bold;">def</span> conditional_download<span style="color: black;">&#40;</span>filename<span style="color: #66cc66;">,</span> base_url<span style="color: #66cc66;">,</span> caller<span style="color: #66cc66;">=</span><span style="color: #008000;">None</span><span style="color: black;">&#41;</span>:
    <span style="color: #ff7700;font-weight:bold;">if</span> file_needs_download<span style="color: black;">&#40;</span>filename<span style="color: black;">&#41;</span>:
        src <span style="color: #66cc66;">=</span> requests.<span style="color: black;">get</span><span style="color: black;">&#40;</span>base_url + filename<span style="color: black;">&#41;</span>
        <span style="color: #808080; font-style: italic;"># manage failure to download</span>
        <span style="color: #ff7700;font-weight:bold;">if</span> src.<span style="color: black;">status_code</span> <span style="color: #66cc66;">==</span> <span style="color: #ff4500;">404</span>:
            src.<span style="color: black;">close</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
            <span style="color: #ff7700;font-weight:bold;">return</span>  <span style="color: #808080; font-style: italic;"># ignore it, that file is simply missing.</span>
        <span style="color: #ff7700;font-weight:bold;">if</span> src.<span style="color: black;">status_code</span> <span style="color: #66cc66;">!=</span> <span style="color: #ff4500;">200</span>:  <span style="color: #808080; font-style: italic;"># an error other than 404 occurred</span>
            <span style="color: #808080; font-style: italic;"># print(&quot;Error {} on {}&quot;.format(src.status_code, filename))</span>
            src.<span style="color: black;">close</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
            <span style="color: #ff7700;font-weight:bold;">if</span> caller:  <span style="color: #808080; font-style: italic;"># retry that file later</span>
                caller.<span style="color: black;">submit</span><span style="color: black;">&#40;</span>conditional_download<span style="color: #66cc66;">,</span> filename<span style="color: #66cc66;">,</span> base_url<span style="color: #66cc66;">,</span> caller<span style="color: black;">&#41;</span>
        <span style="color: #808080; font-style: italic;"># actually copy that file</span>
        dst <span style="color: #66cc66;">=</span> <span style="color: #008000;">open</span><span style="color: black;">&#40;</span>filename<span style="color: #66cc66;">,</span> <span style="color: #483d8b;">'wb'</span><span style="color: black;">&#41;</span>
        dst.<span style="color: black;">write</span><span style="color: black;">&#40;</span>src.<span style="color: black;">content</span><span style="color: black;">&#41;</span>
        <span style="color: #808080; font-style: italic;"># gracefully close theses accesses.</span>
        dst.<span style="color: black;">close</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
        src.<span style="color: black;">close</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
        <span style="color: #ff7700;font-weight:bold;">print</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">&quot;<span style="color: #000099; font-weight: bold;">\t</span>{} : fetched.&quot;</span>.<span style="color: black;">format</span><span style="color: black;">&#40;</span>filename<span style="color: black;">&#41;</span><span style="color: black;">&#41;</span>
&nbsp;
&nbsp;
<span style="color: #ff7700;font-weight:bold;">def</span> download_sinfest<span style="color: black;">&#40;</span>target_folder<span style="color: black;">&#41;</span>:
    <span style="color: #483d8b;">&quot;&quot;&quot;
    Source function for the process
    Creates a directory and fetches Sinfest comics to populate it in full.
    &quot;&quot;&quot;</span>
    <span style="color: #ff7700;font-weight:bold;">if</span> <span style="color: #ff7700;font-weight:bold;">not</span> <span style="color: #dc143c;">os</span>.<span style="color: black;">path</span>.<span style="color: black;">isdir</span><span style="color: black;">&#40;</span>target_folder<span style="color: black;">&#41;</span>:
        <span style="color: #dc143c;">os</span>.<span style="color: black;">makedirs</span><span style="color: black;">&#40;</span>target_folder<span style="color: black;">&#41;</span>
    <span style="color: #dc143c;">os</span>.<span style="color: black;">chdir</span><span style="color: black;">&#40;</span>target_folder<span style="color: black;">&#41;</span>
&nbsp;
    <span style="color: #ff7700;font-weight:bold;">with</span> ThreadPoolExecutor<span style="color: black;">&#40;</span>max_workers<span style="color: #66cc66;">=</span><span style="color: #ff4500;">64</span><span style="color: black;">&#41;</span> <span style="color: #ff7700;font-weight:bold;">as</span> executor:
        <span style="color: #ff7700;font-weight:bold;">for</span> <span style="color: #008000;">file</span> <span style="color: #ff7700;font-weight:bold;">in</span> <span style="color: black;">&#40;</span><span style="color: #483d8b;">&quot;&quot;</span>.<span style="color: black;">join</span><span style="color: black;">&#40;</span><span style="color: black;">&#91;</span><span style="color: black;">&#40;</span><span style="color: #dc143c;">datetime</span>.<span style="color: black;">date</span><span style="color: black;">&#40;</span><span style="color: #ff4500;">2000</span><span style="color: #66cc66;">,</span> <span style="color: #ff4500;">1</span><span style="color: #66cc66;">,</span> <span style="color: #ff4500;">17</span><span style="color: black;">&#41;</span> + <span style="color: #dc143c;">datetime</span>.<span style="color: black;">timedelta</span><span style="color: black;">&#40;</span>days<span style="color: #66cc66;">=</span>x<span style="color: black;">&#41;</span><span style="color: black;">&#41;</span>.<span style="color: black;">isoformat</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span><span style="color: #66cc66;">,</span> <span style="color: #483d8b;">&quot;.gif&quot;</span><span style="color: black;">&#93;</span><span style="color: black;">&#41;</span>
                     <span style="color: #ff7700;font-weight:bold;">for</span> x <span style="color: #ff7700;font-weight:bold;">in</span> <span style="color: #008000;">range</span><span style="color: black;">&#40;</span><span style="color: black;">&#40;</span><span style="color: #dc143c;">datetime</span>.<span style="color: black;">date</span>.<span style="color: black;">today</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span> - <span style="color: #dc143c;">datetime</span>.<span style="color: black;">date</span><span style="color: black;">&#40;</span><span style="color: #ff4500;">2000</span><span style="color: #66cc66;">,</span> <span style="color: #ff4500;">1</span><span style="color: #66cc66;">,</span> <span style="color: #ff4500;">17</span><span style="color: black;">&#41;</span><span style="color: black;">&#41;</span>.<span style="color: black;">days</span> + <span style="color: #ff4500;">1</span><span style="color: black;">&#41;</span><span style="color: black;">&#41;</span>:
            executor.<span style="color: black;">submit</span><span style="color: black;">&#40;</span>conditional_download<span style="color: #66cc66;">,</span> <span style="color: #008000;">file</span><span style="color: #66cc66;">,</span> <span style="color: #483d8b;">&quot;http://www.sinfest.net/btphp/comics/&quot;</span><span style="color: #66cc66;">,</span> executor<span style="color: black;">&#41;</span>
&nbsp;
&nbsp;
<span style="color: #66cc66;">@</span>begin.<span style="color: black;">start</span>
<span style="color: #ff7700;font-weight:bold;">def</span> run<span style="color: black;">&#40;</span>path: <span style="color: #483d8b;">&quot;folder in which the comics must be downloaded&quot;</span> <span style="color: #66cc66;">=</span> <span style="color: #dc143c;">os</span>.<span style="color: black;">path</span>.<span style="color: black;">expanduser</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">&quot;~/Sinfest/&quot;</span><span style="color: black;">&#41;</span><span style="color: #66cc66;">,</span>
        makecbz: <span style="color: #483d8b;">&quot;Compile CBZ comic book archives&quot;</span> <span style="color: #66cc66;">=</span> <span style="color: #008000;">False</span><span style="color: black;">&#41;</span>:
    <span style="color: #483d8b;">&quot;&quot;&quot;Download the Sinfest WebComics&quot;&quot;&quot;</span>
    download_sinfest<span style="color: black;">&#40;</span>path<span style="color: black;">&#41;</span>
    <span style="color: #ff7700;font-weight:bold;">if</span> makecbz:
        dst <span style="color: #66cc66;">=</span> path<span style="color: black;">&#91;</span>:-<span style="color: #ff4500;">1</span><span style="color: black;">&#93;</span> <span style="color: #ff7700;font-weight:bold;">if</span> path.<span style="color: black;">endswith</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'/'</span><span style="color: black;">&#41;</span> <span style="color: #ff7700;font-weight:bold;">else</span> path
        dst <span style="color: #66cc66;">=</span> <span style="color: #483d8b;">&quot;/&quot;</span>.<span style="color: black;">join</span><span style="color: black;">&#40;</span>dst.<span style="color: black;">split</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">&quot;/&quot;</span><span style="color: black;">&#41;</span><span style="color: black;">&#91;</span>:-<span style="color: #ff4500;">1</span><span style="color: black;">&#93;</span><span style="color: black;">&#41;</span>
        make_cbz<span style="color: black;">&#40;</span>dst<span style="color: #66cc66;">,</span> path<span style="color: black;">&#41;</span>
    <span style="color: #ff7700;font-weight:bold;">print</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">&quot;Finished. Goodbye.&quot;</span><span style="color: black;">&#41;</span>
    exit<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span></pre></td></tr></table></div>

<p>Enfin je m&#8217;autorise à utiliser des modules tierces. Alors, ça marche, même plutôt vite et bien. Mais c&#8217;est largement perfectible : si c&#8217;est mieux que par le passé, ça ne correspond pas à ce que je code aujourd&#8217;hui : il y a beaucoup de code hérité des anciennes versions et la prochaine évolution serait de le réécrire entièrement. Mais je ne lis plus vraiment Sinfest.</p>
<hr />
<p>&nbsp;</p>
<p>Mais à écrire ce texte, j&#8217;ai pris les nerfs, et j&#8217;ai refait tout ça. J&#8217;ai fait un effort, et <a href="https://github.com/AlbericC/comics-downloader">j&#8217;ai tout jeté à github</a>.</p>
<p>Encore un pas de mieux : les fichiers ont du sens, il y a une classe abstraite pour faire une pseudo-API, c&#8217;est devenu pratiquement évolutif.</p>
<p>&nbsp;</p>
<p>La conclusion à l&#8217;attention du débutant : chacun voit et juge son code et celui des autres selon son niveau e compétence à l&#8217;instant T. On a tous été fiers comme Artaban de bouts de code laids à nos yeux d&#8217;aujourd&#8217;hui. Débutant qui m&#8217;a lu (merci, t&#8217;as bien du courage), garde tes scripts à travers les années, reprends les, tu te rendras compte de tous les progrès que tu fais au fil du temps. Quand un type te dis sur internet que ce que tu fais n&#8217;est pas &#8220;pythonique&#8221;, il a sûrement raison, de son point de vue, et il y a sans doute un autre gars qui lui dira la même chose demain. La courbe d&#8217;apprentissage est longue comme la vie.</p>


feed_id: http://sametmax.com/?p=18144
title: Les interpréteurs alternatifs de python 1 &#160; Recently updated !
uri: Les-interpréteurs-alternatifs-de-python-1-Recently-updated
url: http://sametmax.com/les-interpreteurs-alternatifs-de-python/
content:
<blockquote><p>Ceci est un post invité de <a href="http://sametmax.com/author/deronnax/">deronnax </a>posté sous licence <a href="http://creativecommons.org/licenses/by/3.0/" target="_blank">creative common 3.0 unported</a>.</p></blockquote>
<p>Bonjour, tous.<br />
Vu qu&#8217;un des derniers posts de Sam&#038;Max parlait de vitesse et des implémentation alternatives de Python, je propose un article sur l&#8217;état de ces différentes implémentations justement, et leur rapport à la vitesse.</p>
<p>Je commence par un petit rappel, pour nos lecteurs les moins aguerris avec l&#8217;écosystème Python : Python est un langage et aussi un interpréteur de référence, dont le vrai nom est CPython, écrit en C, qui livre également la bibliothèque standard Python (les modules datetime, urllib, collections, etc). CPython a des performances typiques de langage de script comme PHP ou Ruby, c&#8217;est à dire pas terribles, et a une conception assez simple, qui utilise des solutions simples pour répondre aux problèmes de langages de script. Celles qui font le plus parler d&#8217;elles (en mal évidement) étant la gestion de la mémoire par comptage de références (references counting), considérée moins performant qu&#8217;un vrai ramasse-miette (garbage collector) et assez limitée, et un verrou global de l&#8217;interpréteur, dont vous avez déjà dû entendre parler, le très mal-aimé GIL (Global Interpreter Lock), qui empêche entre autre à CPython d&#8217;avoir de vrai threads, c&#8217;est à dire des portions de code s&#8217;exécutant en réellement en même temps, concurremment, sur des cœurs processeurs différents. Néanmoins, rappelons-le encore : ces solutions permettent d&#8217;avoir une interpréteur simple et clair, pour lequel écrire des modules d&#8217;extensions en C est simple, ce qui est considéré actuellement comme plus important. De plus, CPython lui-même n&#8217;ayant pas des performances significatives, du vrai multithread n&#8217;est pas un besoin pertinent. Si vous avez besoin de faire des traitements lourds, 9 fois sur 10 une bibliothèque spécialisée en C s&#8217;en occupe et fait elle même le traitement lourds en C dans ses threads, tel qu&#8217;OpenCV ou NumPy. En général vous avez très rarement besoin de vrai thread Python, et donc le GIL est relativement un faux problème.<br />
La communauté Python s&#8217;est adapté à cet absence de vrai concurrence, et différentes solutions sont apparus pour contourner le problème, je pense par exemple à l&#8217;excellent module multiprocessing, qui vous permet d&#8217;instancier différents interpréteurs et de les faire communiquer entre eux de manière transparente comme si c&#8217;était des threads.<br />
D&#8217;une manière générale, quand il sera question d&#8217;un interpréteur alternatif, quatre points intéresseront la communauté :</p>
<ul>
<li>a-t-il de meilleures performances que CPython ?</li>
<li>il y a-t-il un GIL/des vrais threads ?</li>
<li>il y a-t-il un vrai ramasse-miette ?</li>
<li>supporte-il les modules en C codés pour CPython ?</li>
</ul>
<p>Je ne parlerai ici que des interpréteurs se voulant concurrents de CPython et remplissant la même niche. Les interpréteurs remplissant une tache un peu différentes, et/ou nécessitant des modifications significative de la manière d&#8217;écrire du code Python, ou n&#8217;ayant pas un niveau d&#8217;aboutissement significatif ne seront pas abordés, comme Cython ou Stackless Python. Voici <a href="https://en.wikipedia.org/wiki/List_of_Python_software#Python_implementations">une liste complète des implémentations de python</a>.</p>
<p>Déjà, niveau implémentations alternatives, parlons des historiques Jython et IronPython ; ces deux projets sont des interpréteurs python tournant dans la JVM existante d’un autre langage, respectivement la JVM Java d&#8217;IBM et le CLR/.Net C# de Microsoft, apportant ainsi différents bénéfices, entre autre un vrai ramasse-miette et aussi des vrai threads qui carburent. Ils ont aussi en commun d’avoir été peu adoptés (très peu dans le cas d’IronPython) et qu’ils ne sont plus très vivants (très peu dans le cas d’IronPython, déjà lâché par Microsoft en 2010) : dernière release de Jython: mai 2015 ; IronPython: juin 2014. Ça fait mal. De plus, si à leur sortie ils donnaient par moment de meilleures performances que CPython, ce dernier s’est beaucoup amélioré et maintenant ils se valent au mieux. De plus, tous les deux sont très lourds, avec une empreinte mémoire beaucoup plus grosse et un temps de démarrage sans comparaison avec CPython. À déployer, c’est complètement la mort aussi, rien à voir avec apt-get install python/pypy. Très beaucoup la mort pour obtenir un IronPython qui tourne sous Mono sous Ubuntu. Ces projets sont bien partis pour disparaître s’ils ne changent pas. Et le support de python3 est inexistant pour tous les deux. Et vos modules codés en C, vous pouvez vous asseoir dessus.</p>
<p>Pour la petite histoire (anecdote à recaser en soirée pour briller), Jim Hugunin, le créateur de Jython, a commencé à écrire IronPython pour démontrer aux gens que la plateforme .Net était mauvaise et biens moins bonne que la JVM pour écrire une implémentation de langage. Jim a commencé à écrire une implémentation de Python en .Net, et à sa grande surprise a réussi à faire une implémentation très aboutie en peu de temps et a trouvé la plateforme .Net tellement géniale qu&#8217;il a quitté le projet Jython pour lancer IronPython.</p>
<p>Ensuite vient Pypy, l’interpréteur Python en python orienté vitesse. Pypy existe depuis une dizaine d&#8217;année et vise à être un drop-in remplacement de CPython, c’est à dire qu’ils suffit de l’installer et de changer les commandes “python truc” par “pypy truc” et hop ça marche. Ça fonctionne, à condition de ne pas utiliser de modules compilés en C. Si c’est néanmoins le cas, Pypy peut utiliser les modules en C de CPython moyennant recompilation, mais attention, les perfs sont moins bonne qu’avec CPython, dû au fait que l’API vient de CPython et impose à Pypy, un interfaçage/fonctionnement qui n’est pas le sien et ne supporte pas toute l&#8217;API. <a href="http://speed.pypy.org/">Pypy délivre actuellement des performances en moyenne sept fois supérieures à Cpython sur leur suite de tests</a>, ce qui est assez bluffant. Après, à chacun de considérer si la suite de test de Pypy est représentative d&#8217;une utilisation réelle, ce qui est le problème existentielle de chaque implémentation Python alternative qui vous met sous le nez un graphe montrant qu&#8217;elle est X fois plus rapide que CPython sur SA suite de tests. Pypy utilise également un GIL, donc ne fourni pas de vrais threads, mais a dans un coin <a href="http://doc.pypy.org/en/latest/faq.html#does-pypy-have-a-gil-why">un projet de passer à un modèle de concurrence qui permettrait de s&#8217;affranchir du GIL</a>, et dispose par contre d&#8217;un vrai ramasse-miette. Le support de Python 3 est minimum, <a href="http://doc.pypy.org/en/latest/release-2.4.0.html">en juin 2014 est sorti Pypy 2.4 supportant python version 3.2</a>,<a href="http://pypy.org/download.html"> l&#8217;équipe informant qu&#8217;il est néanmoins plus lent que Pypy visant python 2.7</a>, autant vous dire qu’on est pas en avance.</p>
<p>Vous avez pu tomber sur des noms genre Unladden Swallow et Stackless Python : Unladen Swallow était un Pypy-like antérieur par des ingénieurs de Google qui voulait générer du JIT avec LLVM en promettant des performances 5 fois supérieurs à CPython. Le projet a échoué à tenir ses promesses et a fini par mourir doucement lors que Google a retiré son financement. Ce qui a pu être sauvé a été mergé dans Pypy (des améliorations au module Pickle). Stackless  est un interpréteur modifié qui intègre les coroutines de base et d&#8217;autres trucs parallèles/asynchrone, il n&#8217;est d&#8217;un drop-in remplacement et demande d&#8217;écrire du python qui ne tournera que sur et pour lui.</p>
<p>Ensuite est venu il y a un an le “Pypy” de Dropbox, Pyston. Tout le monde a tapé sur Dropbox pour avoir réinventé sa roue carrée avec Pyston, Dropbox a répondu qu’ils utilisent une approche différente de Pypy, ils utilisent un method-at-a-time JIT au lieu d’un tracing JIT, méthode qui a donné de si bons résultats avec le moteur javascript de Google, V8, en s&#8217;appuyant sur la célèbre LLVM, la “machine virtuelle pour le bas-niveau” . LLVM étant par ailleurs soutenue par des grands noms genre Apple et Intel, si Dropbox ne merde pas, ça pourrait donner un truc intéressant. De plus, contrairement à Pypy qui a choisi de peu supportter l&#8217;API C de CPython pour les modules, Pyston vise une compatibilité absolue avec l&#8217;API C, c&#8217;est une de ses priorité fondamentale . Nous pouvons en déduire que Dropbox a beaucoup de modules d&#8217;extension en C auxquels ils tiennent beaucoup. Pypy utilise un ramasse miette et un GIL. Un point fait néanmoins tousser avec Pyston, c’est que les créateurs déclarent ne vouloir que viser Python 2.7 et ne pas envisager de supporter python3. On a du mal à croire que Guido van Rossum travaille pour cette boite. Toutes ces informations viennent de la <a href="https://github.com/dropbox/pyston/wiki/FAQ">FAQ de Pyston</a>. Le blog de Pyston, intéressant, explique leurs choix technique et rend compte de leur avancée : http://blog.pyston.org/.</p>
<p>Pyston a fait plusieurs chose intelligemment, notamment au lieu de réécrire un interpréteur, ils ont forké CPython et branché leurs systèmes de JIT dedans, ce qui leur a permis de sortir en moins d&#8217;un an une release de Pyston plutôt viable qui fait tourner beaucoup de choses et passe beaucoup de tests. <a href="http://blog.pyston.org/2015/11/03/102/">Leur interpréteur est déjà 25% plus rapide que CPython sur leur suite de test, face à un Pypy 50% plus performant</a>. C&#8217;est un petit tour de force.</p>
<p>Un énorme écueil d’écrire un interpréteur alternatif à CPython étaient les modules en C de la bibliothèque standard ; une partie significative des modules de la bibliothèque standard étaient écrits en C, donc si vous faisiez un interpréteur alternatif, il fallait aussi vous recoder dans le langage de votre interpréteur tous les modules de la bibliothèque standard que vous ne pouviez utiliser, ce qui implique de faire 10.000 tests pour s’assurer qu’ils ont exactement le même comportement que ceux de la bibliothèque standard, ce qui est extrêmement fastidieux et pénible. Depuis Python 3.3 en septembre 2012 , <a href="https://www.python.org/dev/peps/pep-0399/">CPython fourni un recode en Python de tous ses module en C qui passent exactement la même suite de test</a> et qui sont donc “garantis” (modulo erreur humaine, c’est une vaste et dure tâche) de se comporter exactement comme leurs congénères en C. C’est une énorme charge de travail en moins pour les développeurs d’interpréteurs alternatifs. Également, depuis Python 3.1 , l&#8217;import de modules, qui auparavant était une machinerie interne un peu obscure de l&#8217;interpréteur CPython, et donc qu&#8217;il fallait recoder en croisant les doigts pour que ça reproduise exactement le comportement de CPython sous peine de tout voir exploser, <a href="https://docs.python.org/3/library/importlib.html#module-importlib">a été recodé en Python et est livré avec CPython</a>, donc fini de revoir recoder un système d&#8217;import, vous pouvez juste le reprendre et l&#8217;utiliser, ouf.</p>
<p>De tous, Pypy est le seul qui s’approche actuellement vaguement d’un “concurrent” sérieux à CPython, talonné par Pyston pour un futur proche. Et encore, ce n’est pas encore le drop-in remplacement parfait. Et tous ont un support de Python3 inexistant, sauf Pypy qui en a un insuffisant :). Et tous, en tant que VM complète (donc lourde) ou interpréteur-optimiseur visant à optimiser le code (et donc analyser), ont des temps de démarrage et d’atteinte de l’efficacité sans commune mesure avec CPython. Ils ne sont intéressants qu’en cas de long-running (genre, vos sites Django). Et tous utilisent un ramasse-miette, ce qui donne un comportement différents sur les objets &#8220;finalisés&#8221;. Alors que CPython a un comportement déterministe et détruit immédiatement les objets qui ne sont plus utilisés, les ramasse-miettes reportent leur destruction à un moment où il sera plus opportun de les détruire, potentiellement très longtemps ou jamais au pire des cas. C&#8217;est pour ça que par hygiène, il faut toujours libérer explicitement les ressources des objets représentants des ressources tel que les files-like objets représentants fichiers ou socket en utilisant leur méthode &#8220;close()&#8221; ou le manager &#8220;with&#8221; même si ça fonctionne très bien sans sous CPython. Pour les objets représentant des ressources limitées comme les fichiers, les sockets ou n&#8217;importe quel lien vers une ressource limitée procurée par l&#8217;OS, ça signifie se prendre rapidement un stop de l&#8217;OS. D&#8217;une manière générale, CPython applique des solutions simples et déterministes qui offrent pas mal de garanties sympathiques (gestion des ressources, atomicité, alignement mémoire, adresse des objets en mémoire fixe, etc) qui sont maintenant considérées comme acquise et immuables par beaucoup de programmes python, malheureusement gênantes pour faire un interpréteur optimisant vraiment agressivement (là j&#8217;ai pas de sources mais c&#8217;est une complainte qui revient régulièrement dans les discussions des autres interpréteurs, surtout Pypy).</p>
<p>Parmi les news relatives à la vitesse de Python que l’article de S&amp;M ne mentionne pas, la nouvelle release de la JVM vient avec un meilleur découplage des éléments la composant, et du coup il devient facile de l’utiliser pour un autre langage. IBM a fait un proof-of-concept en faisant un interpréteur python, qu’ils vont open-sourcer (et faire pareil avec Ruby). Pas d’altruisme là-dedans, c’est pour montrer au monde que leur VM est bien fichue. Ça pourrait être le nouveau Jython : http://www.infoworld.com/article/3014128/open-source-tools/ibms-open-source-jvm-project-could-also-speed-ruby-python.html.</p>
<p>Aussi, Microsoft s’étant visiblement, dernièrement, un peu amouraché de Python, on peut s’attendre que leur coopération à Python aille un peu plus loin. Ou pas.</p>
<p>Après, la triste vérité est que les langages dynamiques sont très durs à optimiser, parce qu’ils sont justement dynamiques, surtout Python où tout est modifiable, et donc interpréteur/VM ne peut être sûr de rien et ne peut pas supprimer pleins de tests/résolutions/recherches, et que Python, malgré toutes ses implémentations pleines de bonne volonté, ne sera jamais aussi rapide que le C, ni même que Java. La dernière fois que j’ai regardé (~3 ans), le benchmark game donnait python 20x plus lent que le C, contre 2x pour le Java. Ça fait réfléchir. Tous les efforts poussés à fond, on tapera peut être le 5x plus lent que le C :)</p>
<p>Actuellement, un core-contributeur de CPython (français, cocorico !) travaille sur différents sujets visant à améliorer significativement les performances de CPython. C&#8217;est la première fois qu&#8217;un chantier aussi vaste est lancé visant à sérieusement améliorer les performances de CPython. Le site sur le sujet : <a href="http://faster-cpython.readthedocs.org/">http://faster-cpython.readthedocs.org/</a></p>
<p>Ces différents projets acceptent les dons. Je vous encourage évidemment vivement à donner. La vitesse de Python est l&#8217;un dernier écueil qui pourraient faire choisir à un décideur technique pressé un autre langage plutôt que Python. Des meilleurs interpréteurs Python, c&#8217;est plus de tâches accomplies en Python, donc plus de postes pour les développeurs Python, donc plus de demande, et donc de meilleurs salaires. Quand vous donnez à des projets Python, vous vous versez de l&#8217;argent à vous-même dans le futur. De plus, ça aide à faire reculer les parts de marché de cette horreur qu&#8217;est PHP et de la concurrence de Ruby et Go, et ça, c&#8217;est toujours ça de pris :)<br />
Même une petite somme est significative. Avec l&#8217;euro fort, vous avez un grand pouvoir. Et un grand pouvoir implique de grandes responsabilités.<br />
Donner à Cpython : <a href="https://www.python.org/psf/donations/">https://www.python.org/psf/donations/</a><br />
Donner à Pypy : <a href="http://pypy.org/">http://pypy.org/</a></p>
<p>Voila, vous savez tout sur l’histoire des différents interpréteurs Python. Avec ça, vous pourrez vous la pétez en soirée et niquer des tonnes de meufs. Ou pas.</p>


