# articles

feed_id: http://sametmax.com/?p=10007
title: Heartbleed, une semaine plus tard
uri: Heartbleed-une-semaine-plus-tard
url: http://sametmax.com/heartbleed-une-semaine-plus-tard/
content:
<p>Il y a eu pas mal d&#8217;infos sur le sujet, généralement assez technique et en anglais, aussi je vous fais un petit résumé de la crise.</p>
<p>Le 8 avril dernier, une faille dans le logiciel OpenSSL, surnommée <a href="http://heartbleed.com/">Heartbleed</a>, a été <a href="http://www.washingtonpost.com/business/technology/heartbleed-bug-puts-the-chaotic-nature-of-the-internet-under-the-magnifying-glass/2014/04/09/00f7064c-c00b-11e3-bcec-b71ee10e9bc3_story.html">rendue publique</a>. OpenSSL est le logiciel le plus utilisé au monde pour chiffrer les communications entre le navigateur et le site Web. Les banques, les e-commerce, les emails, les réseaux sociaux, tous utilisent SSL, pour éviter que l&#8217;on puisse intercepter vos communications. Et la plupart utilisent OpenSSL sur leurs serveurs pour permettre une connexion SSL.</p>
<p>Ce n&#8217;est pas le protocole SSL ou le chiffrement en général qui est mis en cause mais bien le logiciel OpenSSL.</p>
<p>Au début, on a cru à une faille critique qui permettait à l&#8217;attaquant de lire les communications. Toute personne exploitant cette faille aurait pu récupérer vos identifiants de banque au moment de votre login par exemple.</p>
<p>Il s&#8217;est avéré que c&#8217;était <a href="https://www.schneier.com/blog/archives/2014/04/heartbleed.html">bien pire encore</a>.</p>
<p>La faille permet de récupérer, à chaque heart beat (d&#8217;où le surnom) &#8211; c&#8217;est à dire à chaque fois qu&#8217;un client dit au serveur &#8220;je suis toujours connecté&#8221; &#8211; 64K de mémoire de l&#8217;ordinateur cible, au hasard. Parce que cette faille utilise le heart beat, elle n&#8217;implique pas d&#8217;authentification ou de log, elle est donc parfaitement silencieuse, ne laisse pas de trace, et n&#8217;importe qui sans autorisation peut se connecter et en tirer parti.</p>
<p><a href="http://www.scribd.com/doc/217805356/9/NCSC-FI-Advisory-on-OpenSSL">Tous les sites les plus importants sont concernés</a> incluant tous les réseaux sociaux, fournisseurs d&#8217;email, banque, etc. En fait, même les services en ligne qui ne sont pas des sites, comme Steam, sont affectés.</p>
<p>On ne peut pas choisir les 64K sur la machine, mais si on répète l&#8217;opération suffisamment longtemps, on peut récupérer la plupart des choses en mémoire. Comme un serveur a généralement énormément de cache en mémoire, il y a de tout dedans : les mots de passe, adresses emails, clés privées, certificats d&#8217;authentification, présents dans le programme, etc. En prime, en récupérant la clé privée, on peut écouter les connections au site ou carrément se faire passer pour le site, puis facilement récupérer numéros de carte de crédit, emails, messages privés, et tout ce qui transite.</p>
<href="https://filippo.io/Heartbleed/">La plupart des services</a> ont réagit très vite et ont corrigé le problème, seulement la faille a été dans la nature pendant 2 ans.</p>
<p>C&#8217;est une catastrophe.</p>
<h2>A qui profite le crime ?</h2>
<p>La question n&#8217;est pas qui peut en tirer parti. La réponse à cela est tous les attaquants : pirates, gouvernements, armées, services secrets et même script kiddies.</p>
<p>Non. Le débat est plutôt de savoir si la faille était intentionnelle ou pas.</p>
<p>D&#8217;un côté, j&#8217;aime beaucoup cette phrase attribuée à Napoléon :</p>
<blockquote><p>N&#8217;attribuez jamais à la malveillance ce qui s&#8217;explique très bien par l&#8217;incompétence.</p></blockquote>
<p>Et l&#8217;incompétence, il en a fallu, car il ne s&#8217;agit pas d&#8217;une faille de cryptographie complexe, mais d&#8217;<a href="http://blog.existentialize.com/diagnosis-of-the-openssl-heartbleed-bug.html">une erreur de débutant en programmation C</a>. Dans un logiciel qui a la portée de OpenSSL, ça amène sérieusement à se poser des questions, malgré <a href="http://www.smh.com.au/it-pro/security-it/man-who-introduced-serious-heartbleed-security-flaw-denies-he-inserted-it-deliberately-20140410-zqta1.html">le déni de l&#8217;auteur</a>.</p>
<p>Par dessus se rajoute <a href="http://igurublog.wordpress.com/2014/04/08/julian-assange-debian-is-owned-by-the-nsa/">une déclaration d&#8217;Assange</a>, qui enfonce un clou dans le cercueil, en expliquant que de nombreux logiciels Open Source sont complètement vérolés par la NSA, incluant des distributions Linux aussi réputées que Debian.</p>
<p>Comme souvent sur Internet, ce sont les commentaires de l&#8217;article qui sont les plus intéressants. On y note notamment plusieurs personnes, se présentant comme des professionnels de la cryptographies, débattant avec des argumentaires très bien construits de l&#8217;implication probable de la NSA et d&#8217;autres services secret dans la mise en œuvre de cette faille, et de son exploitation.</p>
<p>D&#8217;un côté, Bloomberg a annoncé que la NSA, a défaut d&#8217;avoir créé le bug, <a href="http://www.bloomberg.com/news/2014-04-11/nsa-said-to-have-used-heartbleed-bug-exposing-consumers.html">savait à son propos</a> depuis le début et l&#8217;a exploité. De l&#8217;autre, <a href="https://twitter.com/NSA_PAO/status/454720059156754434">la NSA nie</a> (putain la NSA a un compte Twitter ?).</p>
<p>En tout cas, avec la révélation de Prism, il ne tient plus de la théorie du complot mais de faits que ces entités cherchent par tous les moyens à obtenir la main mise sur les réseaux d&#8217;information. L&#8217;idée qu&#8217;ils aient créé cette back door parait donc plausible, bien qu&#8217;improuvable.</p>
<p>Et surtout, si c&#8217;est le cas, combien d&#8217;autres accès aussi radicaux ont-ils à leur disposition ? Mon frère me disait dernièrement :</p>
<blockquote><p>Je ne sais pas pourquoi ça te surprend encore. Tout en informatique a été craqué. Tout. Depuis toujours. On crack les playstations, Windows&#8230; Pourquoi tu crois que tes systèmes de sécurités ne sont pas craqués aux aussi ?</p></blockquote>
<p>Sous prétexte que tout le monde les utilise, qu&#8217;ils sont Open source et que des gens vachement plus intelligents que nous les ont vérifié, on fait confiance à ces systèmes. Mais si la porte blindée est attachée à un mur en BA13 et qu&#8217;on ne le sait pas ?</p>
<h2>Que faire pour l&#8217;utilisateur ?</h2>
<p>Comme l&#8217;a recommandé <a href="http://mathieu.agopian.info/blog/heartbleed-consequences-pour-les-utilisateurs.html">Mathieu Agopian</a>, d&#8217;abord changer tous ses mots de passe, pour tous ses sites. C&#8217;est là qu&#8217;on apprécie <a href="http://sametmax.com/les-10-meilleures-extensions-firefox-chrome/">lastpass</a>.</p>
<p>Je sais pertinemment que personne ne va le faire. Les gens ont une forte tendance à se plaindre que tout le monde est pourri et qu&#8217;on ne peut rien y changer, mais dès qu&#8217;il faut mettre la main à la pâte, y a plus personne. C&#8217;est pareil pour le recyclage, le boycott ou la lecture des programmes électoraux.</p>
<p>Et puis ils n&#8217;ont <a href="https://xkcd.com/1353/">rien à cacher</a>, pas vrai ?</p>
<p>De plus, si il existe d&#8217;autres failles de ce genre, ça ne résoudra pas le problème général, qui est politique et social. L&#8217;informatique n&#8217;est qu&#8217;un outil, on ne résout pas avec de la technique les problèmes d&#8217;un peuple. Même si ça aide.</p>
<p>De plus, le problème n&#8217;est pas complètement résolu côté fournisseur. Il faut que tout le monde mette à jour ses certificats, et les autorités de certifications croulent sous les demandes. Elles n&#8217;ont pas une infra prévue pour ça. Ensuite, certains outils ne pourront pas se changer. OpenSSL est présent dans de nombreux systèmes embarqués. Si vous mettre en sécurité implique jeter votre appareil et en racheter un neuf, vous pouvez être certain que que ça se fera encore moins que changer les mots de passe. Et je ne vous parle même pas de l&#8217;industrie. Je met ma bite à couper que dans le domaine des transports il y a des tas d&#8217;équipements bien trop chers à changer pour qu&#8217;ils soient sécurisés. Les professionnels vont donc jouer la carte de l&#8217;obscurité et serrer les fesses.</p>
<h2>La désillusion sur l&#8217;Open Source</h2>
<p>Ce qui fait mal au fion, c&#8217;est l&#8217;évidence de la réponse de <em>Wolf</em> dans un des commentaires de l&#8217;article sur Asssange, quand quelqu&#8217;un demande des solutions :</p>
<blockquote><p>I think the reason you’re not receiving much of an answer on this is the reason I gave above – there are none. We’re in a place of no good options.</p></blockquote>
<p>Car quand il s&#8217;agit du système général qui joue contre celui qu&#8217;il est censé servir, changer de logiciel ne sert à rien. On pensait que parce que les systèmes ouverts étaient transparents, ils n&#8217;étaient pas attaquables de cette manière. Des milliers d&#8217;yeux pour regarder le code source, qu&#8217;ils disaient.</p>
<p>Heartbleed nous montre une histoire bien différente.</p>
<p>OpenSSL s&#8217;est révélé être extrêmement difficile à auditer, car il est très complexe, sa documentation est réputée lacunaire sans compter qu&#8217;il faut de sacrées compétences pour ne serait-ce que commencer à proposer de participer.</p>
<p>En clair, la taille, la complexité et l&#8217;accessibilité des projets libres les plus importants font un sérieux contre poids à la fameuse garantie de la revue de pairs. C&#8217;est pour cette raison que des projets comme Android ne sont pas considérés comme libres par beaucoup : seul Google a les ressources pour étudier tout le code source. On manque de gens compétents, ainsi que de temps et d&#8217;argent. Il y a de plus en plus d&#8217;informaticiens, mais de moins en moins, proportionnellement, sont qualifiés. Beaucoup de techniciens, peu d&#8217;ingénieurs ou de hackers, malgré le titre sur le CV. Et encore moins qui sont motivés pour mettre les mains dans le cambouis du FOSS.</p>
<p>Cela veut-il dire qu&#8217;il faut jeter le bébé avec l&#8217;eau du bain ?</p>
<p><a href="http://sebsauvage.net/links/?CjQyCg">Certainement pas.</a></p>
<p>En fait, si cette faille avait été dans un logiciel propriétaire, nous n&#8217;aurions jamais été au courant. Elle a été découverte par hasard lors d&#8217;un audit d&#8217;un système qui n&#8217;a absolument rien à voir, par une équipe de sécurité Finlandaise. Les ingénieurs ont matraqué leur cible de tests aléatoires et sont tombés sur un problème. En cherchant l&#8217;origine du problème, ils se sont aperçus que le souci ne venait pas du produit testé, mais d&#8217;OpenSSL. <strong>Parce qu&#8217;ils avaient le code source</strong>, ils ont pu pointer du doigt la faille et prévenir les auteurs d&#8217;OpenSSL.</p>
<p>Entre temps, l&#8217;équipe de sécurité de Google (là encore, ça fait jaser car ils ont la réputation d&#8217;être proches de la NSA) a publié le bug et le correctif, et la couverture de presse vend le tout comme étant leur propre découverte.</p>
<h2>L&#8217;Emo de la fin</h2>
<p>Donc non, les logiciels Open Source ne sont pas vaccinés contre les failles majeures sous prétexte que le code est public.</p>
<p>Oui, il y a un soupçon grave de sabotage, à l&#8217;heure actuelle (et probablement pour longtemps) il n&#8217;y a aucune preuve. On peut s&#8217;attendre à une grosse vague de rejet de cette idée pour cause de &#8220;théorie du complot&#8221;. Ça ne change pas beaucoup des débats habituels dès qu&#8217;on parle de sécurité. C&#8217;est fou comme les gens sont prêt à jouer la carte de la présomption d&#8217;innocence pour défendre des grosses institutions boîtes noires au passé trouble et désignent coupable les accusés de pédophilie dès qu&#8217;ils voient leur tête au 20H&#8230;</p>
<p>La différence, c&#8217;est l&#8217;enjeu, et il est de taille. On parle pas d&#8217;une déchirure du slip, on parle d&#8217;être complètement à poil avec un bout du string cassé qui sort de l&#8217;anus.</p>
<p>Enfin oui, l&#8217;Open Source, offre toujours de meilleures garanties de sécurité en la matière que le logiciel fermé. Plus de transparence, plus de vitesse de réaction, disponibilité immédiate et pour toujours des correctifs, etc. Mais surtout, on vient d&#8217;en parler, si il s&#8217;agit de sabotage, le problème est politique et social, pas juste technique. Face à cela, un système démocratique, comme l&#8217;Open Source, a plus de chance de jouer en notre faveur qu&#8217;un système oligarchique de logiciel propriétaire. Reste à vérifier si l&#8217;élitisme du savoir ne risque pas de créer une oligarchie d&#8217;un autre genre côté FOSS. Sans compter les innombrables problèmes d&#8217;ego.</p>
<p>Mais ça ne suffit pas. Et à la lueur de ce genre d&#8217;affaire, il va falloir que la communauté du libre, principale gardienne des plus gros codes sources ouverts, se réorganise pour à la fois faire le ménage dans la base de code existante et filtrer à l&#8217;entrée les potentiels saboteurs. Qu&#8217;ils soient incompétents ou malveillants.</p>
<p>J&#8217;aimerais pas être dans les chaussettes de Linus en ce moment.</p>
<p>Malheureusement wikipedia est la preuve que des règles plus restrictives pour participer n&#8217;empêche pas les lobbyistes de pourrir le contenu mais apporte bel et bien un énorme frein à l&#8217;entrée de sang neuf. Ce qui n&#8217;arrange pas nos affaires, dans un monde où la complexité des sujets augmente plus vite que la compétence des gens qui pourraient participer. La revue communautaire reste le moins pire des systèmes, comme la démocratie pour Churchill, mais ça me colle un petit coup de blues tout ça quand même.</p>
 <p><a href="http://sametmax.com/?flattrss_redirect&amp;id=10007&amp;md5=5f1bc14d3b056b056723256ed775d6a0" title="Flattr" target="_blank"><img src="media/fc229d00.flattr-badge-large.png" data-original-source="http://sametmax.com/wp-content/plugins/flattr/img/flattr-badge-large.png" alt="flattr this!"/></a></p>


feed_id: http://sametmax.com/?p=9994
title: Ca va se prolonger
uri: Ca-va-se-prolonger
url: http://sametmax.com/ca-va-se-prolonger/
content:
<p>Situation imprévue et prolongation de l&#8217;absence. Retour sur le blog lundi ou mardi.</p>
 <p><a href="http://sametmax.com/?flattrss_redirect&amp;id=9994&amp;md5=9cb674ceaed6132bc644b788f17379d3" title="Flattr" target="_blank"><img src="media/fc229d00.flattr-badge-large.png" data-original-source="http://sametmax.com/wp-content/plugins/flattr/img/flattr-badge-large.png" alt="flattr this!"/></a></p>


feed_id: http://sametmax.com/?p=9962
title: Au revoir
uri: Au-revoir
url: http://sametmax.com/au-revoir/
content:
<p>Je me tire pour deux semaines.</p>
<p>Pas d&#8217;articles.</p>
<p>Pas de comments.</p>
<p>Bisous.</p>
 <p><a href="http://sametmax.com/?flattrss_redirect&amp;id=9962&amp;md5=ba9ad7df54835949dbee034c973d05bb" title="Flattr" target="_blank"><img src="media/fc229d00.flattr-badge-large.png" data-original-source="http://sametmax.com/wp-content/plugins/flattr/img/flattr-badge-large.png" alt="flattr this!"/></a></p>


feed_id: http://sametmax.com/?p=9944
title: Traquez les erreurs de vos apps Django avec Sentry
uri: Traquez-les-erreurs-de-vos-apps-Django-avec-Sentry
url: http://sametmax.com/traquez-les-erreurs-de-vos-apps-django-avec-sentry/
content:
<p>Tout foire tout le temps. Le site (ou app) sans erreur n&#8217;existe pas. Après chaque mise à jour on croise les doigts et on espère que tout va bien se passer.<br />
Les plus courageux auront fait des <a href="http://sametmax.com/un-gros-guide-bien-gras-sur-les-tests-unitaires-en-python-partie-1/">tests unitaires</a> en amont qu&#8217;ils lanceront avant l&#8217;update, les autres comme moi vont commiter leurs changements et redémarrer le serveur prod. On est boucher de père en fils dans la famille&#8230;</p>
<p>Avant toute chose Sentry qui avant était un projet Django serveur/client a depuis évolué et est devenu un serveur uniquement, laissant le choix du client suivant votre framework/language de travail. Sentry est disponible sur à peu près toutes les plateformes (Python, PHP, Ruby, JavaScript, Java, Node.js, iOS) et les frameworks Rails et Django. La <a href="http://sentry.readthedocs.org/en/latest/">documentation</a> est très fournie et le projet <a href="https://github.com/getsentry/sentry">très actif</a>.</p>
<p>Dans le cas d&#8217;un site web en général on a tellement de pages qu&#8217;on ne peut pas prédire à l&#8217;avance ce qui va foirer ou pas (mais ça foire toujours, tout foire, etc&#8230;). Même si vous avez fait les tests unitaires vous aurez forcément oublié quelque chose.</p>
<p>Cette page spéciale sur les nains obèses unijambistes scatophiles pour lesquels vous avez créé un tag unique et qui ne supporte pas que vous ayez changé votre mode de cache va lamentablement planter et vous priver de la fidélité de DSK sur votre site pour longtemps.</p>
<p>Heureusement nous allons pouvoir logger toutes ces erreurs et avoir des jolis graphiques post-mortems. Bien évidement ça va vous faire du taf en plus bande de feignasses.</p>
<p>Sentry vient en <a href="http://sentry.readthedocs.org/en/latest/quickstart/index.html#install-sentry">standalone</a> ou en version <a href="https://www.getsentry.com/pricing/">gratuite limitée</a>, hebergée par les créateurs.<br />
J&#8217;ai voulu installer la standalone parceque je revenais de l&#8217;apéro et qu&#8217;au 8ème Ricard on veut tout faire soit-même mais la raison et 14heures de sommeil l&#8217;ont emporté, c&#8217;est à mon réveil que je me suis alors décidé à installer la version trial limitée à 7 jours de log et 2800 evenements loggés (qui au passage suffit sur un de nos plus gros site).</p>
<p><strong>la mise en place est vraiment des plus simple:<br />
</strong></p>
<p><a href="https://www.getsentry.com/register/">Créez un compte</a> et ensuite récupérez la clef API dans les préférences sur votre compte en ligne Sentry.</p>
<p><strong>Du côté de votre projet Django:</strong></p>
<p>Installez <a href="http://raven.readthedocs.org/en/latest/index.html">Raven</a>, qui est un client python pour Sentry. Il va se charger collecter vos erreurs et les transmètre à Sentry.</p>
<p>Comme d&#8217;habitude une bonne PIP:</p>

<div class="wp_syntax"><div class="code"><pre class="bash" style="font-family:monospace;">  pip <span style="color: #c20cb9; font-weight: bold;">install</span> raven</pre></div></div>

<p>Dans le fichier <em>settings.py</em> de votre projet Django:</p>
<p>Utilisez la clef Api fournie par Sentry (voir plus haut)</p>

<div class="wp_syntax"><div class="code"><pre class="python" style="font-family:monospace;"><span style="color: #808080; font-style: italic;"># used for sentry login</span>
RAVEN_CONFIG = <span style="color: black;">&#123;</span>
    <span style="color: #483d8b;">'dsn'</span>: <span style="color: #483d8b;">'https://aboudouboudousplish:aboudouboudousplash@app.getsentry.com/1984'</span>,
<span style="color: black;">&#125;</span></pre></div></div>

<p>Vous pouvez faire un test pour voir si tout est bien configuré:</p>
<p>Dans votre projet Django tapez</p>

<div class="wp_syntax"><div class="code"><pre class="python" style="font-family:monospace;">python manage.<span style="color: black;">py</span> raven <span style="color: #dc143c;">test</span></pre></div></div>

<p>Vous devriez recevoir un email de Sentry avec le récapitulatif du test raven.</p>
<p><strong><br />
L&#8217;interface Sentry:</strong></p>
<div id="attachment_9947" class="wp-caption aligncenter" style="width: 610px"><img src="media/6e39849a.group_list.png" data-original-source="http://sametmax.com/wp-content/uploads/2014/03/group_list.png" alt="" title="La liste des évènements avec possibilité de filtres." width="600" class="wp-image-9947" /><p class="wp-caption-text">La liste des évènements avec possibilité de filtres.</p></div>
<p>Sur le flux vous allez avoir la liste de tous les évènements loggés, vous pouvez les trier en choisissant parmi une plétore d&#8217;options. Vous pouvez nettoyer ce flux en un seul clic (lors d&#8217;un redemarrage de serveur vous risquez d&#8217;avoir beaucoup d&#8217;erreurs qui n&#8217;en sont en fait pas, donc inutile de les traiter).</p>
<div id="attachment_9949" class="wp-caption aligncenter" style="width: 610px"><img src="media/3cbc4d2c.event.png" data-original-source="http://sametmax.com/wp-content/uploads/2014/03/event.png" alt="" title="L&#039;évènement en détail, une clarté qui donne envie d&#039;avoir des bugs ^^" width="600"  class="wp-image-9949" /><p class="wp-caption-text">L&#039;évènement en détail, une clarté qui donne envie d&#039;avoir des bugs ^^</p></div>
<p>Chaque évènement du flux a une vue détail où vous allez pouvoir tracer le bug, c&#8217;est clair et plein d&#8217;informations (un peu comme les pages de debug Django). Il y a la possibilité d&#8217;ajouter des notes, de relancer la requête afin de vérifier la correction effectuée.</p>
<p><strong><br />
Le mode Team:</strong><br />
Sentry vous permet de bosser en équipe, vous créez un projet, des utilisateurs et chacun peut monitorer, corriger des bugs etc (pas dispo dans la version trial). Vous pouvez aussi créer des groupes d&#8217;utilisateurs qui n&#8217;auront accès qu&#8217;à certains projet. Le côté équipe a bien été intégré. Vous saurez sur qui gueuler lorsque le bug aura été mal corrigé.</p>
<p><strong>Conclusion:</strong><br />
Monitorer ses apps est un vrai calvaire quand on n&#8217;a pas les bons outils ou que ces derniers sont chiants à installer/maintenir.<br />
Sentry offre deux possibilités d&#8217;installation et leur mode trial vaut vraiment le coup (gratuit je le répète), les plus courageux se farciront l&#8217;install.<br />
Pour la version standalone j&#8217;ai lu un peu partout qu&#8217;il était préférable de l&#8217;installer sur un serveur à part, à méditer avant de se lancer. Les logs sont en temps réél, vous n&#8217;avez pas à attendre le lendemain pour savoir ce qui a planté la veille, vous pouvez donc corriger vos bugs rapidement.</p>
<p>Le plus gros problème de Sentry je pense c&#8217;est qu&#8217;on tombe accroc, je m&#8217;y rend désormais plusieurs fois par jour pour voir si il n&#8217;y a pas un petit bug à corriger :)</p>
<p>PS: je cherche l&#8217;équivalent pour monitorer les serveurs, Munin est trop vieux et tous les autres outils que j&#8217;ai pu tester demande à installer (entendez passer des heures à compiler) toute une machinerie. L&#8217;idéal serait des paquets à installer sur les serveurs à monitorer + service en ligne à la Sentry.</p>
 <p><a href="http://sametmax.com/?flattrss_redirect&amp;id=9944&amp;md5=f02b1ed9da7baac448319c5bf8e672f2" title="Flattr" target="_blank"><img src="media/fc229d00.flattr-badge-large.png" data-original-source="http://sametmax.com/wp-content/plugins/flattr/img/flattr-badge-large.png" alt="flattr this!"/></a></p>


feed_id: http://sametmax.com/?p=9930
title: Pour 2014, prenez une bonne résolution : baisez de la grosse !
uri: Pour-2014-prenez-une-bonne-résolution-baisez-de-la-grosse
url: http://sametmax.com/pour-2014-prenez-une-bonne-resolution-baisez-de-la-grosse/
content:
<p>Il n&#8217;est jamais trop tard pour se reprendre en main et aller dans la bonne direction.</p>
<p>J&#8217;étais en train de me faire draguer par une copine de mon coloc plutôt rondoudou que pikachu quand je me suis fait la réflexion que toutes les rondes avec qui j&#8217;avais couché avait été de super bon coups.</p>
<p>Ça va faire raler Max, parce que lui il n&#8217;aime que les anorexiques. Il a bon goût en la matière ceci dit, et on se rejoint tous les deux sur les africaines, alors ça compense.</p>
<p>Par contre ça va plaire à Réchèr qui est très BBW.</p>
<p>Bref, 5 raisons de passer le permis poids lourd&#8230;</p>
<h2>Elles se donnent au max</h2>
<p>Et oui, elles font des efforts. Comme dit le dicton : &#8220;De mémoire de marin, a jamais vu une baleine faire l&#8217;étoile de mer.&#8221;</p>
<p>Ce dicton n&#8217;existe absolument pas et je l&#8217;ai complètement inventé.</p>

<!-- iframe plugin v:2.3 - wordpress.org/extend/plugins/iframe/ -->
<iframe width="590" height="480" src="http://www.sextvx.com/en/embed/447546" frameborder="0" scrolling="no" class="iframe-class"></iframe>
<h2>On a pas peur de taper dans le fond</h2>
<p>Il y a des filles, on a peur de les casser en deux. Et d&#8217;autres, on peut partir en rodéo en levrette. Or, comme l&#8217;énergie cinétique c&#8217;est 1/2 de mv carré, avoir un corps résistant pour absorber le choc est un avantage non négligeable.</p>

<!-- iframe plugin v:2.3 - wordpress.org/extend/plugins/iframe/ -->
<iframe width="510" height="400" src="http://xhamster.com/xembed.php?video=1788995" frameborder="0" scrolling="no" class="iframe-class"></iframe>
<h2>Elles ont la langue super entrainée.</h2>
<p>Les grosses sucent super bien. C&#8217;est un cliché. Je ne suis pas un échantillon représentatif.</p>
<p>Mais bon, je défend l&#8217;idée. On est sur internet, si je dis une connerie se sera diluée dans la masse de toute façon.</p>

<!-- iframe plugin v:2.3 - wordpress.org/extend/plugins/iframe/ -->
<iframe src="http://www.pornhub.com/embed/1373876859" frameborder="0" width="608" height="468" scrolling="no" class="iframe-class"></iframe>
<h2>Elles ont des seins idéaux pour la branlette espagnole</h2>
<p>Le bonnet F a plein d&#8217;avantages. On peut y plonger la tête dedans. On peut y insérer divers choses. On peut faire sa muscu avec.</p>

<!-- iframe plugin v:2.3 - wordpress.org/extend/plugins/iframe/ -->
<iframe src="http://www.youjizz.com/videos/embed/2187784" frameborder="0" width="704" height="550" scrolling="no" allowtransparency="true" class="iframe-class"></iframe>
<h2>Elles cuisinent bien. Et moi après, j&#8217;ai souvent faim.</h2>
<p>Parce que le cul et la bouffe, c&#8217;est indissociable.</p>
<p>Et je ne dis absolument pas ça parce que j&#8217;ai pris du poids dernièrement.</p>

<!-- iframe plugin v:2.3 - wordpress.org/extend/plugins/iframe/ -->
<iframe width="760" height="524" type="text/html" src="http://embed.porntube.com/1342274" frameborder="0" id="f1342274" 1="class="porntube-player"></iframe" scrolling="no" class="iframe-class"></iframe>
<p>La semaine prochaine on vous expliquera pourquoi brouter de la rousse est bon pour la peau.</p>
 <p><a href="http://sametmax.com/?flattrss_redirect&amp;id=9930&amp;md5=bfaacdd070e224e47cf2014ccfb85c37" title="Flattr" target="_blank"><img src="media/fc229d00.flattr-badge-large.png" data-original-source="http://sametmax.com/wp-content/plugins/flattr/img/flattr-badge-large.png" alt="flattr this!"/></a></p>


feed_id: http://sametmax.com/?p=9873
title: Mobile First
uri: Mobile-First
url: http://sametmax.com/mobile-first/
content:
<p>Ca fait un bout de temps que les amateurs des claviers virtuels sautillent en criant que le mobile va remplacer le PC.</p>
<p>Ce mois ci la version mobile d&#8217;un site de cul de Max vient de rapporter plus d&#8217;argent en pub que la version Desktop.</p>
<p>C&#8217;est arrivé.</p>
<p>Donc c&#8217;est parti pour une énième reconversion de vos skills.</p>
<p>Aiguisez vos talents de responsive designer. Developpez le site pour mobile en premier, puis étendez le aux tablettes et enfin aux écranx 1024 pouces et plus. Servez-vous un red bull, ça fera passer le goût du guronsan.</p>
<p>Je sais vous venez tout juste de finir d&#8217;apprendre les animations CSS3, le localStorage, les nouveaux tags HTML 5 et les websockets. Vous vous disiez que vous pouviez encore attendre un peu. Faire une petite pause quoi, merde. Peut être le temps d&#8217;essayer un autre langage pour le fun ?</p>
<p>Nope.</p>
<p>Au boulot !</p>
<p>Et n&#8217;oubliez pas, la prog asynchrone, le WebRTC, les nouveaux frameworks comme angularjs, bref, le real time Web, est en train de monter aussi. Et demain, sûrement, les clients ne voudront plus que ça.</p>
<p>Au passage, vous vous souvenez comme dans les années 2000 tout le monde se touchait sur le code xHTML valide, accessible pour les handicapés ? Tout le monde s&#8217;en tape maintenant. Il n&#8217;y a pas une app new gen qui soit blind-friendly.</p>
<p>Bref, comme toujours, entre bonne conscience et pédance, il n&#8217;y a qu&#8217;un pas.</p>
 <p><a href="http://sametmax.com/?flattrss_redirect&amp;id=9873&amp;md5=7f0a9bd85e05c809fbe87f85082566e4" title="Flattr" target="_blank"><img src="media/fc229d00.flattr-badge-large.png" data-original-source="http://sametmax.com/wp-content/plugins/flattr/img/flattr-badge-large.png" alt="flattr this!"/></a></p>


feed_id: http://sametmax.com/?p=9844
title: NoSQL : arrêtons de dire n’importe quoi
uri: NoSQL-arrêtons-de-dire-n-importe-quoi
url: http://sametmax.com/nosql-arretons-de-dire-nimporte-quoi/
content:
<p>J&#8217;ai regardé le mouvement NoSQL évoluer au fil des années. On y retrouve à peu prêt tout ce qui fait l&#8217;informatique depuis que le monde IT est monde : brillance et troll, hype et génie, utile et gadget, buzz et fact, sam et max, etc.</p>
<p>De plus on peut mettre à n&#8217;importe quoi sous le label NoSQL, et du coup ça a été fait. En fait un fichier est déjà une base de données NoSQL :)</p>
<p>Mais rant mise à part, des projets comme redis, riak, elastic search ou mongodb changent vraiment la donne.</p>
<p>Malheureusement, tout comme d&#8217;autres technos du moment (prog asychrone, tout-http, pre-processeurs, generateurs&#8230;), les gens ont tendance à l&#8217;utiliser comme la barre de fer, la silver bullet, le passe partout, le tournenis sonique, bref, le truc à tout faire.</p>
<p>L&#8217;adage populaire dit &#8220;quand on a un bon marteau, tous les problèmes ressemblent à des clous&#8221;. Or, je constate qu&#8217;au dessus de ça, les dev appliquent aussi souvent le dicton préféré d&#8217;un de mes colocs : &#8220;arrête de taper si fort, prend un plus gros marteau&#8221;. </p>
<p>Ca donne du NoSQL utilisé partout, pour tout, brandi comme LA solution, vendu à des débutants comme une panacées de traitement d&#8217;informations. Zob, vous vous doutez bien que ça pose problème, non ?</p>
<h2>Anti-fact 1 : NoSql, c&#8217;est plus facile pour démarrer</h2>
<p>Il n&#8217;existe pas à l&#8217;heure actuelle de base NoSQL embarquée qui arrive à la cheville de SQLite : ça marche partout, dans tous les langages, sans rien à avoir installer ou configurer pour la plupart des langages.</p>
<p>Dès que vous demandez à un débutant d&#8217;installer un truc, vous rajoutez une barrière d&#8217;entrée énorme.</p>
<p>De plus, il y a beaucoup, beaucoup, beaucoup plus d&#8217;hébergeurs qui fournissent du SQL que du NoSQL en solution par défaut. Et comme toute les technos legacy, il y a 100 fois plus de doc.</p>
<p>Enfin, il y a la fameuse question du &#8220;quoi&#8221; ? Quel système allez-vous installer ? Couch ? Casssandra ? Mongo ? On parle de NoSQL, ou de schemaless ? C&#8217;est pas la même chose ? Memcache et Redis, c&#8217;est que pour le cache ? Elastic Search, c&#8217;est que pour la recherche de texte ? Les données géographiques, je les mets dans quoi, mongo ou un GIS spécialisé ? Attend, j&#8217;ai entendu parlé d&#8217;une super bdd de graph&#8230;</p>
<p>L&#8217;abondance de solutions, le manque de recul et les informations contradictoires disponibles rendent non seulement le choix difficile, mais en plus hasardeux. Car contrairement au monde du SQL, se gourrer en NoSQL peut vous pourir toute votre archi.</p>
<p>Souvenez-vous qu&#8217;il est beaucoup plus difficile de migrer son système de base de données NoSQL d&#8217;une solution à une autre car il n&#8217;y a pas ce petit détail en commun entre les produits : le SQL justement.</p>
<h2>Anti-fact 2 : Avec NoSql, pas besoin de réfléchir à son modèle de données</h2>
<p>Je crois que c&#8217;est ce qui me fait le plus grincer des dents. Les gens qui disent qu&#8217;on peut tout mettre dedans, hop, et on verra plus tard. J&#8217;ai vu les pires modèles de données possibles stockés en MongoDb ou Redis, parceque les gars qui avaient travaillé dessus avait juste dumpé leurs données sans réfléchir.</p>
<p>Une base NoSQL ne vous oblige pas à formaliser votre schéma, mais ça ne veut CERTAINEMENT PAS dire qu&#8217;il ne faut pas le faire. L&#8217;auteur de Redis a très bien <a rhef="http://oldblog.antirez.com/post/reply-open-minded-reader.html">expliqué le problème</a> (je graisse pour donner une effet dramatique et puissant au message) :</p>
<blockquote><p>Redis is not the kind of system where you can insert data and then argue about how to fetch those data in creative ways. Not at all, the whole idea of its data model, and part of the fact that it will be so fast to retrieve your data, is that <strong>you need to think in terms of organising your data for fetching</strong>. You need to design with the query patterns in mind.</p></blockquote>
<p>C&#8217;est vrai pour tout système dans lequel on met ses données, SQL, NoSQL, fichier, mémoire, le tirroir de votre bureau&#8230;</p>
<p>Il faut penser au type des données, leurs formats, les relations entre les éléments, comment et à quelle fréquence vous allez les écrire, les lire, garantir la consistence de leurs relations et leur fraicheur (ou pas d&#8217;ailleur, mais il faut en faire le choix). Les bases SQL sont contraignantes parce qu&#8217;elles vous obligent à penser, dès le début, en ces termes.</p>
<p>C&#8217;est vrai, vous êtes de grandes personnes, a priori vous savez ce que vous faites, vous n&#8217;avez pas besoin qu&#8217;on vous FORCE à le faire. C&#8217;est pour ça que j&#8217;aime le typage dynamique. Je ne veux pas que tu me demandes mes papiers pour déclarer une variable, je sais ce que je fais.</p>
<p>Seulement <strong>il faut le faire</strong>, et ce n&#8217;est souvent pas fait. Pire, le modèle n&#8217;est généralement jamais formalisé NUL PART. Un schéma, c&#8217;est une doc. Sans doc, le coût d&#8217;entrée dans votre projet est élevé, sa maintenance est galère, le potentiel de bug lors de l&#8217;évolution est plus grand. Mais une doc c&#8217;est chiant à écrire et à tenir à jour.</p>
<p>C&#8217;est un des interessants effets secondaires des ORMs : les classes de définition sont le modèle documenté dans sa structures, ses relations, ses limites, ses contraintes, ses tests et vérifications, etc. La doc par le code, j&#8217;adore.</p>
<h2>Anti-fact 3 : NoSql, c&#8217;est plus performant</h2>
<p>A chaque fois qu&#8217;on lit &#8220;x est plus performant que z&#8221;, il faut faire une pause et réfléchir deux minutes. Généralement il y a un piège.</p>
<p>Les performances, ça dépend toujours du contexte. Par exemple, Redis est plus performant à la lecture et l&#8217;écriture, mais les données doivent tenir en RAM, sinon ça bouffe sur la mémoire virtuelle. Autre chose, Redis est très lent à démarrer sur des gros jeux de données (ça peut aller à plusieurs minutes si vous avez des Go). MongoDB doit normalement pouvoir tenir une augmentation de charge de manière prédictive en rajoutant des noeuds. Mais sur un seul noeud, c&#8217;est toujours moins performant qu&#8217;un PostGres. Et 0.01 % des sites ont besoin de plus d&#8217;un serveur.</p>
<p>Par ailleurs, les performances sont très dépendantes de l&#8217;anti-fact 2. Il faut créer les bons index, avoir un cache correctement ajusté, faire des requêtes intelligentes. Pour tous les systèmes.</p>
<p>Bref, encore une fois, NoSQL n&#8217;est pas une techno magique. Il est contre-productif, et j&#8217;ai envie de dire même irrespectueux envers ses collègues, de la vendre comme telle.</p>
<h2>Anti-fact 4 : NoSQL remplace le SQL</h2>
<p>Tweeter tourne sur MySQL ET Memcache.</p>
<p>Stackoverflow utiliser SQL Server 2008 ET Redis.</p>
<p>Il y a carrément des sites qui <a href="http://www.plotprojects.com/why-we-use-postgresql-and-slick/">utilisent PostGres et MongoDb en parallèle</a>. En fait, il y a <a href="http://www.infoq.com/news/2013/02/MoSQL">des outils</a> pour les faire collaborer. </p>
<p>Nous sur notre plus gros site on utilise Redis pour les sessions, les compteurs, les crawlers, les queues et passer des données entre process. <strong>Pas juste pour le cache.</strong> On utilise PostGres pour les données complexes avec des queries lourdes. Et on utilise Solr pour le moteur de recherche. </p>
<p>Les bases NoSQL sont des nouveaux outils, qui sont mieux adaptés à CERTAINS usages ou à CERTAINS contexte. Pas tout, tout le temps, partout. C&#8217;est un outil en plus, pas un obligatoire remplaçant.</p>
<p>Par ailleurs, on peut utiliser PostGres comme une base de données <a href="media/90a46c98.pg-as-nosql-pgday-fosdem-2013.pdf" data-original-source="http://thebuild.com/presentations/pg-as-nosql-pgday-fosdem-2013.pdf">clé-valeur</a> ou <a href="http://www.reddit.com/comments/1q3skb">JSON</a>, on peut mettre SQLite complètement en mémoire vive, on peut utiliser MySQL comme un moteur de recherche de texte&#8230; Multiplier les points of faillures dans une archi n&#8217;est pas toujours une bonne idée. Ces outils qu&#8217;on considère comme de l&#8217;histoire ancienne ont beaucoup plus de ressource que vous ne l&#8217;immaginez. Ils sont ultra performant. Des années et des années d&#8217;optimisation.</p>
<p>Le monde de la tech n&#8217;est jamais lisse. Jamais.</p>
 <p><a href="http://sametmax.com/?flattrss_redirect&amp;id=9844&amp;md5=527c76003751e7e5a48c768d10446683" title="Flattr" target="_blank"><img src="media/fc229d00.flattr-badge-large.png" data-original-source="http://sametmax.com/wp-content/plugins/flattr/img/flattr-badge-large.png" alt="flattr this!"/></a></p>


feed_id: http://sametmax.com/?p=9833
title: Solution de l’exo la modif de flash
uri: Solution-de-l-exo-la-modif-de-flash
url: http://sametmax.com/solution-de-lexo-la-modif-de-flash/
content:
<p><a href="http://sametmax.com/exercice-python-round-3/">L&#8217;exercice d&#8217;hier</a> était plus un prétexte pour faire un peu de Python 3 avec manipulation de bytes qu&#8217;un vrai défi, mais c&#8217;est toujours marrant.</p>

<div class="wp_syntax"><div class="code"><pre class="python" style="font-family:monospace;"><span style="color: #483d8b;">&quot;&quot;&quot;
    Kick flash bin ass so it stop giving us trouble will full screen
    vids on a dual screen.
&quot;&quot;&quot;</span>
&nbsp;
<span style="color: #ff7700;font-weight:bold;">import</span> <span style="color: #dc143c;">sys</span>
<span style="color: #ff7700;font-weight:bold;">import</span> argparse
&nbsp;
<span style="color: #808080; font-style: italic;"># On déclare un argument positionnel obligatoire.</span>
<span style="color: #808080; font-style: italic;"># Ce doit être un fichier que l'on veut</span>
<span style="color: #808080; font-style: italic;"># ouvrir en lecture et mode binaire.</span>
<span style="color: #dc143c;">parser</span> = argparse.<span style="color: black;">ArgumentParser</span><span style="color: black;">&#40;</span>description=__doc__<span style="color: black;">&#41;</span>
<span style="color: #dc143c;">parser</span>.<span style="color: black;">add_argument</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'bin'</span>, <span style="color: #008000;">type</span>=argparse.<span style="color: black;">FileType</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'rb'</span><span style="color: black;">&#41;</span>, <span style="color: #008000;">help</span>=<span style="color: #483d8b;">&quot;Path to flash bin, man&quot;</span><span style="color: black;">&#41;</span>
args = <span style="color: #dc143c;">parser</span>.<span style="color: black;">parse_args</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
&nbsp;
<span style="color: #808080; font-style: italic;"># On charge le contenu du fichier en mémoire.</span>
<span style="color: #808080; font-style: italic;"># Un peu bourrin, mais tellement plus simple</span>
<span style="color: #808080; font-style: italic;"># que de chercher directement dans le fichier.</span>
flash_oh_oooooooh = <span style="color: #dc143c;">bytearray</span><span style="color: black;">&#40;</span>args.<span style="color: black;">bin</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>
<span style="color: #808080; font-style: italic;"># On chercher la chaîne binaire qui correspond à la constante</span>
<span style="color: #808080; font-style: italic;"># qu'on veut écorcher.</span>
<span style="color: #ff7700;font-weight:bold;">if</span> b<span style="color: #483d8b;">&quot;_NET_ACTIVE_WINDOW&quot;</span> <span style="color: #ff7700;font-weight:bold;">not</span> <span style="color: #ff7700;font-weight:bold;">in</span> flash_oh_oooooooh:
    <span style="color: #ff7700;font-weight:bold;">print</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">&quot;Flash est déjà défoncé&quot;</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><span style="color: #ff4500;">0</span><span style="color: black;">&#41;</span>
<span style="color: #808080; font-style: italic;"># On remplace juste un bit </span>
flash_oh_oooooooh<span style="color: black;">&#91;</span>flash_oh_oooooooh.<span style="color: black;">find</span><span style="color: black;">&#40;</span>b<span style="color: #483d8b;">&quot;_NET_ACTIVE_WINDOW&quot;</span><span style="color: black;">&#41;</span> + <span style="color: #ff4500;">1</span><span style="color: black;">&#93;</span> = <span style="color: #ff4500;">42</span>
&nbsp;
<span style="color: #808080; font-style: italic;"># On écrit le résultat dans dans le fichier final.</span>
<span style="color: #ff7700;font-weight:bold;">try</span>:
    <span style="color: #ff7700;font-weight:bold;">with</span> <span style="color: #008000;">open</span><span style="color: black;">&#40;</span>args.<span style="color: black;">bin</span>.<span style="color: black;">name</span>, <span style="color: #483d8b;">'wb'</span><span style="color: black;">&#41;</span> <span style="color: #ff7700;font-weight:bold;">as</span> news_flash:
        news_flash.<span style="color: black;">write</span><span style="color: black;">&#40;</span>flash_oh_oooooooh<span style="color: black;">&#41;</span>
<span style="color: #ff7700;font-weight:bold;">except</span> PermissionError:
    <span style="color: #dc143c;">sys</span>.<span style="color: black;">exit</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">&quot;Fozzy says you don't have right to write in '%s', man...&quot;</span> <span style="color: #66cc66;">%</span> args.<span style="color: black;">bin</span>.<span style="color: black;">name</span><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;Rosebud&quot;</span><span style="color: black;">&#41;</span></pre></div></div>

<p>Voilà, rien d&#8217;incroyable, mais on trouve deux choses intéressantes : l&#8217;utilisation de <code>FileType</code> pour gérer un paramètre de type chemin de fichier sans se prendre la tête, et celle de <code>bytesarray</code> qui nous permet d&#8217;avoir un array mutable et donc de tout charger en mémoire comme un porc sans avoir à dupliquer tout le contenu lors de la modification.</p>
<p>J&#8217;ai vu certains ouvrir le fichier sans le flag binaire (&#8216;b&#8217;), attention Python peut vous bousiller tout le contenu du fichier puisqu&#8217;il va interpréter les caractères spéciaux et faire du décodage sur un contenu sur lequel ça n&#8217;a pas de sens.</p>
<p>La vraie question, c&#8217;est : combien y a-t-il de références pop culturelles dans ce code ? </p>
<hr/>
<p><a href="https://github.com/sametmax/codes-des-articles/blob/master/2014/mars/kick_flash_ass.py">Télécharger le code de l&#8217;article.</a></p>
 <p><a href="http://sametmax.com/?flattrss_redirect&amp;id=9833&amp;md5=9ef73747ad55ebd53e35a7ea692258ea" title="Flattr" target="_blank"><img src="media/fc229d00.flattr-badge-large.png" data-original-source="http://sametmax.com/wp-content/plugins/flattr/img/flattr-badge-large.png" alt="flattr this!"/></a></p>


feed_id: http://sametmax.com/?p=9823
title: Exercice Python, round 3
uri: Exercice-Python-round-3
url: http://sametmax.com/exercice-python-round-3/
content:
<p>Continuons cette <a href="http://sametmax.com/tag/exercice/">petite série</a> fort sympathique, avec aujourd&#8217;hui un peu de manipulation de bytes, de gestion d&#8217;erreur et de parsing d&#8217;arguments de script.</p>
<p>Souvenez vous de <a href="http://sametmax.com/garder-une-video-flash-en-full-screen-et-travailler-sur-un-autre-ecran/">ce hack</a> qui permet d&#8217;avoir sa video Flash en plein écran sur un écran, et travailler sur l&#8217;autre, quand on est en dual screen. Le problème c&#8217;est qu&#8217;il faut le faire à la main. De plus, il faut recommencer à chaque mise à jour de flash. On va automatiser tout ça.</p>
<p>Faites un script (en Python 3 \o/) qui va parser les arguments de la ligne de commande avec argparse. Il doit déclarer :</p>
<ul>
<li>Une docstring.</li>
<li>Un message d&#8217;help à partir de cette docstring.</li>
<li>Un argument positionnel obligatoire devant être le chemin du binaire flash.</li>
</ul>
<p>Ensuite on ouvre le fichier, on cherche le fameux <code>_NET_ACTIVE_WINDOW</code>, on le deface, et on sauvegarde le tout dans le fichier final. Ce sera l&#8217;occasion de jouer un peu avec la gestion plus propre du binaire en Python 3.</p>
<p>Contrairement aux exercices précédent, on met un minimum de check d&#8217;erreur car c&#8217;est un petit script (35 lignes copieusement commentées chez moi): donc au moins avertir proprement l&#8217;utilisateur si on ne peut pas lire ou écrire dans le fichier et pourquoi.</p>
<p><a href=" Solution de l’exo la modif de flash">La solution demain.</a></p>
 <p><a href="http://sametmax.com/?flattrss_redirect&amp;id=9823&amp;md5=71f29586bc5f5e5d1863bb3d93718ba3" title="Flattr" target="_blank"><img src="media/fc229d00.flattr-badge-large.png" data-original-source="http://sametmax.com/wp-content/plugins/flattr/img/flattr-badge-large.png" alt="flattr this!"/></a></p>


feed_id: http://sametmax.com/?p=9812
title: Droit de réponse au troll JS
uri: Droit-de-réponse-au-troll-JS
url: http://sametmax.com/droit-de-reponse-au-troll-js/
content:
<p><em>Comme tous les trolls, on a eu le droit à <a href="http://sametmax.com/un-gros-troll-de-plus-sur-javacscript/">la cohue dans les comments</a>, mais une réponse a eu l&#8217;élégance de faire ça <a href="https://gist.github.com/DavidBruant/9549803#file-gistfile1-md">sur un pastebin</a> à part et de structurer l&#8217;argumentation.</p>
<p>Et en plus de faire un bisou.</p>
<p>J&#8217;aime le droit de réponse, et puisque cette personne n&#8217;a visiblement pas de blog (et que ça risque de se perdre dans le fin fond du web), je le publie ici, avec son autorisation.</em></p>
<blockquote><p>C’est quand Google a enfin pu donner des perfs décentes – c’est à dire celles qu’ont d’autres langage depuis une décennie – à Javascript que les gens ont envisagé de l’utiliser sur le serveur.</p>
</blockquote>
<p>Nan, c&#8217;est parce que Ryan Dahl a compris l&#8217;importance de la programmation asynchrone et que les gens qui font du JS sont déjà dans le moule de l&#8217;asynchrone et que c&#8217;était donc une communauté facile à convaincre.<br />
C&#8217;est juste une bonne coïncidence qu&#8217;au moins une VM très performante est dispo.</p>
<p>Node.js massacre beaucoup d&#8217;autres langages en performance grâce à l&#8217;asynchrone, pas grand chose d&#8217;autre, sûrement pas la &#8220;vitesse du langage&#8221;.</p>
<blockquote>
<p>Javascript (&#8230;) ne sert absolument à rien sans un framework côté serveur</p>
</blockquote>
<p>On compare des choux et des carottes. Un langage (syntaxe, sémantique) ne sert à rien. Il faut toujours des trucs en plus. Dans un contexte web, à quoi sert Ruby sans Rails ?</p>
<blockquote>
<p>côté client il est parfaitement improductif sans une lib pour gommer les incompatibilités entre les implémentations.</p>
</blockquote>
<p>Le web est un pari ambitieux qui a des coûts. Mais il faut aussi comprendre les enjeux du web. Aucun téléphone ne sort sans navigateur web aujourd&#8217;hui. Ca n&#8217;est même pas une discussion. Ca a des coûts qui vont avec. On peut <a href="https://www.youtube.com/watch?v=0l-OhSpKLdQ" rel="noreferrer">rêver d&#8217;un autre monde</a>, mais essayons d&#8217;avancer dans celui qu&#8217;on a entre temps ;-)</p>
<blockquote>
<p>Au cas où vous ne l’aurez pas encore compris, je vomis sur Javascript. Mais je code avec, et j’offre même des formations dessus, car je suis pragmatique. Les besoins de l’industrie, l’inertie technologie et le contexte social / technique / économique dictent bien plus souvent les standards que nous utilisons que leurs qualités intrasèques. Sinon nous n’aurions pas utilisé le format .doc ou les VHS.<br />
En fait, je ne détesterais pas autant Javascript si je n’en avais pas besoin quotidiennement. Je le hais du plus profond de mon âme justement parce que c’est non seulement une contrainte inamovible de mon métier, mais en plus une tumeur que les chercheurs annoncent voir grossir de jour en jour. Avec le sourire, les connards !</p>
</blockquote>
<p>Bienvenue dans l&#8217;humanité ;-)</p>
<blockquote>
<p>Les experts recommandent d’éviter la moitié du langage</p>
</blockquote>
<p>Du fat d&#8217;être langage du web et de la contrainte de backward-compat du web, JS ne peut pas se débarrasser de ses <em>bad parts</em>. <a href="https://www.youtube.com/watch?v=7eNFQqMSxtU" rel="noreferrer">J&#8217;ai donné un talk sur le sujet</a>. Être efficace en JS nécessite de comprendre un peu son histoire et les pièges à éviter. C&#8217;est le coût d&#8217;être le langage du web. Mais c&#8217;est bien le web, non ? Ca vaut le coup, non ?</p>
<blockquote>
<p>En fait activer <code>use strict</code> dès que possible pour éviter l’insertion de ; automatiques.</p>
</blockquote>
<p>wtf? N&#8217;importe quoi ! La spec ECMAScript 5 est la référence pour les différences entre strict et &#8220;sloppy&#8221; mode. Pour des ressources un peu plus digestes :<br />
<a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions_and_function_scope/Strict_mode" rel="noreferrer">https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions_and_function_scope/Strict_mode</a><br />
<a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions_and_function_scope/Strict_mode/Transitioning_to_strict_mode" rel="noreferrer">https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions_and_function_scope/Strict_mode/Transitioning_to_strict_mode</a></p>
<blockquote>
<p>Cependant, faites gaffe quand vous convertissez du code parce que :</p>
<div class="highlight highlight-js">
<pre><span class="o">=&gt;</span> <span class="nx">a</span> <span class="o">=</span> <span class="mi">1</span>
<span class="mi">1</span>
<span class="o">=&gt;</span> <span class="kd">var</span> <span class="nx">a</span> <span class="o">=</span> <span class="mi">1</span>
<span class="kc">undefined</span>
</pre>
</div>
</blockquote>
<p>Mais c&#8217;est quoi ce business de la désinformation !!<br />
<code>a = 1</code> est une expression qui permet de faire <code>a = b = 1</code>, donc sa valeur est la valeur de la sous-expression de droite. <code>var a = 1</code> n&#8217;est pas une expression (<code>if(var a = 1){}</code> est une SyntaxError), donc le REPL se contente de retourner undefined, mais ça n&#8217;a aucune importance en pratique.</p>
<blockquote>
<p>Ah oui, et ne pas utiliser les mots clés new ou with pour vos propres libs. Si, si, on bannit carrément des mots clés, c’est écrit dans le livre.</p>
</blockquote>
<p>Douglas Crockford est une seule personne. On a le droit aussi de ne pas être d&#8217;accord. Utiliser <code>with</code>, c&#8217;est souvent se tirer une balle dans le pied, d&#8217;où le bannissement dans le strict mode. Mais <code>new</code>, c&#8217;est plus une question de style. Je suis assez expert JS, j&#8217;utilise <code>new</code> et je le vis bien.</p>
<blockquote>
<p>Faire du JS propre présuppose l’utilisation de design patterns<br />
En l’état, on ne peut pas écrire du JS propre.</p>
</blockquote>
<p>En français aussi, il y a des figures de style. En l&#8217;état, on peut écrire du JS propre. Je veux bien admettre que ça exige beaucoup plus de disciplines que d&#8217;autres langages, mais ça s&#8217;arrête là. Encore une fois, JS est obligé de porter le poids de son histoire parce que c&#8217;est le langage du web.</p>
<blockquote>
<p>Au fait, vous avez déjà essayé d’expliquer le prototypage à quelqu’un ? Bonne chance !</p>
</blockquote>
<p><a href="http://davidbruant.github.io/ObjectViz/" rel="noreferrer">http://davidbruant.github.io/ObjectViz/</a></p>
<blockquote>
<p>J’ouvre Firefox, je tape <code>[] + {}</code></p>
</blockquote>
<p>J&#8217;écris ça quotidiennement aussi, ça m&#8217;aide beaucoup pour écrire des interfaces facilement utilisables par les utilisateurs. <code>[] + {}</code> créé aussi des méta-promesses qui permettent de faire de l&#8217;asynchrone un peu plus rapide que la vitesse de la lumière.</p>
<p>Si tu additionnes des choux et des carottes, faut pas s&#8217;étonner. Si des fois, tu es fatigué, utilise TypeScript pour aider avec la discipline.</p>
<blockquote>
<p>chaines multilignes</p>
</blockquote>
<p>Ca arrive dans ES6 <a href="https://gist.github.com/lukehoban/9303054#template-strings" rel="noreferrer">https://gist.github.com/lukehoban/9303054#template-strings</a></p>
<blockquote>
<p>list compréhension à la Python</p>
</blockquote>
<p>ES6 : <a href="https://gist.github.com/lukehoban/9303054#comprehensions" rel="noreferrer">https://gist.github.com/lukehoban/9303054#comprehensions</a><br />
Implémenté dans Firefox et dans la Nightly depuis la semaine dernière.</p>
<blockquote>
<p>Et encore, je suis cool, je mets un code JS qui utilise l.length directement dans la boucle et pas de variable pour l[i].</p>
</blockquote>
<p>Nan, mais c&#8217;est bon, on est en 2014, plus besoin de ces conneries :-) Y&#8217;en a jamais eu besoin, c&#8217;était juste de la micro-optimisations de gens qui pensaient que ça avait un impact significatif après qu&#8217;ils aient fait un micro-benchmark sur le sujet.</p>
<blockquote>
<p>Array.map arrive avec JS 1.6 et les arrays comprehensions avec la 1.7&#8230;</p>
</blockquote>
<p>Cute :-)<br />
Array.prototype.map, on peut le <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map?redirectlocale=en-US&amp;redirectslug=JavaScript%2FReference%2FGlobal_Objects%2FArray%2Fmap#Polyfill" rel="noreferrer">polyfiller</a></p>
<p>JavaScript 1.6, 1.7, ça n&#8217;existe pas. C&#8217;est des gens chez Mozilla, ils étaient bourrés (Brendan Eich a continué à boire même après avoir créé et shippé JS en 10 jours), ils ont donné des numéros de version, après le mot &#8220;JavaScript&#8221;, mais c&#8217;était pour rire (c&#8217;était lié au numéro de version de SpiderMonkey, moteur JS de Firefox). Seuls les numéros de spec d&#8217;ECMAScript ont un sens un peu sérieux.</p>
<blockquote>
<p>que vous pourrez utiliser sur tous les navigateurs d’ici 2056.</p>
</blockquote>
<p><code>.map</code> tu peux l&#8217;utiliser depuis des années. Les array compréhension, ça marche sur <a href="http://google.github.io/traceur-compiler/demo/repl.html" rel="noreferrer">Traceur</a></p>
<blockquote>
<p>PASramètre<br />
Comment on fait ça en Javascript ?</p>
</blockquote>
<p>Tiens <a href="https://gist.github.com/lukehoban/9303054#default--rest--spread" rel="noreferrer">https://gist.github.com/lukehoban/9303054#default&#8211;rest&#8211;spread</a><br />
J&#8217;ai utilisé ça en TypeScript (mais ceux qui préfèrent Traceur peuvent aussi choisir ça) sur un vrai projet qui tourne sur de vrais mobiles il y a presque un an.</p>
<blockquote>
<p>Ah mais il faut utiliser coffeescript !<br />
Oui donc pas Javascript. Point made.</p>
</blockquote>
<p>Jeremy Ashkenas, inventeur de CoffeeScript le décrit en disant &#8220;it&#8217;s just JavaScript&#8221;. Je dis ça&#8230;</p>
<blockquote>
<p>Vous trouvez ça normal ?<br />
Vous trouvez que c’est le signe d’un BON langage ?</p>
</blockquote>
<p>Aucune importance. C&#8217;est ce qu&#8217;on a, il faut vivre avec, bon gré mal gré.</p>
<p>Gros bisous,</p>
<p>David</p>
 <p><a href="http://sametmax.com/?flattrss_redirect&amp;id=9812&amp;md5=25f71b5e4166d9f4d4b5e223641e04d4" title="Flattr" target="_blank"><img src="media/fc229d00.flattr-badge-large.png" data-original-source="http://sametmax.com/wp-content/plugins/flattr/img/flattr-badge-large.png" alt="flattr this!"/></a></p>


feed_id: http://sametmax.com/?p=10038
title: Le manifeste du cache du mode hors ligne pour HTML5
uri: Le-manifeste-du-cache-du-mode-hors-ligne-pour-HTML5
url: http://sametmax.com/mode-le-manifeste-du-cache-du-mode-hors-ligne-pour-html5/
content:
<p>La bataille app native VS site responsive va faire rage pendant pas mal de temps, et pour le moment les apps gagnent : performances plus élevées, meilleures intégration visuelle dans l&#8217;OS, accès à une API plus riche&#8230; Les utilisateurs les préfèrent, et du coup les pros sont obligés de se les coltiner. C&#8217;est chiant, mais c&#8217;est la réalité du terrain pour les dev sur mobile.</p>
<p>Mais pour les sites Web ou les apps simples, il est super intéressant d&#8217;exploiter les capacités HTLM5 au max pour une obtenir une expérience plus &#8220;app&#8221; et moins &#8220;site web&#8221;.</p>
<p>Parmi ces possibilités : le mode hors ligne. D&#8217;un côté, il y a le stockage des données dans le navigateur, mais <a href="http://sametmax.com/stockez-vos-donnees-dans-le-navigateur-de-lutilisateur-avec-localstorage-html-5/">on vous en a déjà parlé</a>.</p>
<p>De l&#8217;autre, il y a le cache des ressources. Cela consiste à déclarer quels fichiers (html, css, js, images, fonts, n&#8217;importe quoi&#8230;) garder en mémoire afin de les charger directement depuis le disque dur au lieu de le faire en ligne.</p>
<p>Pour cela, il faut déclarer un manifeste dans son HTML :</p>

<div class="wp_syntax"><div class="code"><pre class="html" style="font-family:monospace;">&lt;html manifest=&quot;cache.manifest&quot;&gt;</pre></div></div>

<p>Ensuite, on créer le fichier <code>cache.manifest</code> dans son projet, qui est un fichier de texte simple.</p>
<p>Il faut le faire servir avec le mime-type
<pre lang="python bash">text/cache-manifest</pre>
<p> sinon ça ne marche pas. Si vous le nommez
<pre lang="python bash">*.manifest</pre>
<p> et que vous le servez avec un serveur de dev, ça marchera tout seul. Pour la prod, il faut le spécifier à votre serveur. Par exemple avec nginx, il faut éditer le fichier <code>/etc/nginx/mime.types</code> et y ajouter :</p>
<pre>text/cache-manifest                   manifest;</pre>
<p>Pour apache, c&#8217;est un truc du genre dans le <code>.htaccess</code>:</p>
<pre>AddType text/cache-manifest .manifest</pre>
<p>Ensuite, le manifeste ressemble à ça :</p>
<pre>CACHE MANIFEST
# 2014-04-20 13:25:00

# Toutes les ressources à sauvegarder en local. Le navigateur
# va toujours chercher ces ressources sur le disque.
# Si on est déconnecter, la page index.html, ses styles et javascript
# s'afficheront donc quand même.
CACHE:
index.html
/favicon.ico
stylesheet.css
images/logo.png
scripts/main.js
fonts/font.woff

# Ressources qui ne sont chargées que si on est en ligne.
# Par exemple, on charge le module de stats de visiteurs que si on est
# en ligne car impossible de compter les vues sans le serveur.
NETWORK:
js/visits.js

# Ressources alternatives si les précédentes sont innacessibles.
# Par exemple, pour afficher un point rouge si on est hors ligne
# et un point vert si on est en ligne :
# images/offline.png sera servi si images/online.png est inaccessible
FALLBACK:
images/online.png images/offline.png</pre>
<p>Notez le commentaire <code># 2014-04-20 13:25:00</code> tout en haut. C&#8217;est<br />
une convention qu&#8217;on utilise pour donner la dernière date de modification des<br />
fichiers cachés. En effet, <strong>les fichiers de la section <code>CACHE</code> ne seront<br />
pas rechargés tant que le manifeste n&#8217;a pas été modifié. </strong></p>
<p>Cela veut dire que si vous modifiez <code>index.html</code>, l&#8217;utilisateur<br />
ne verra pas la modification. Mais si vous changez la date du fichier manifeste,<br />
le fichier est modifié, et le navigateur rechargera donc toutes les ressources<br />
qu&#8217;il a mis en cache. Ainsi, vous permettez aux utilisateurs de voir les<br />
ressources cachées qui ont été modifiées.</p>
 <p><a href="http://sametmax.com/?flattrss_redirect&amp;id=10038&amp;md5=f5fda6374fb3bbe75d28d576c909b894" title="Flattr" target="_blank"><img src="media/fc229d00.flattr-badge-large.png" data-original-source="http://sametmax.com/wp-content/plugins/flattr/img/flattr-badge-large.png" alt="flattr this!"/></a></p>


feed_id: http://sametmax.com/?p=10029
title: Générer des données factices avec faker
uri: Générer-des-données-factices-avec-faker
url: http://sametmax.com/generer-des-donnees-factices-avec-faker/
content:
<p><a href="https://github.com/joke2k/faker">faker</a> fait partie de ses libs que j&#8217;ai toujours voulu écrire sans jamais prendre le temps de le faire. Comme <a href="http://sametmax.com/les-time-zones-en-python/">arrow</a> par exemple. Et puis un jour quelqu&#8217;un le fait, et je suis à la fois soulagé de ne pas avoir tenté de le faire (au risque de ne pas réussir aussi bien) et un peu déçu d&#8217;être passé à côté de la bonne idée.</p>
<p>Le principe de la lib est très simple : générer des données bidons. Noms, numéros de téléphone, adresses physiques ou email&#8230; C&#8217;est utile pour tout un tas de choses :</p>
<ul>
<li>Faire des tests, évidement.</li>
<li>Créer des bots, des crawlers et tout autre programme qui doit se faire passer pour un utilisateur.</li>
<li>Remplir une base de données vide en attendant que les utilisateurs réels remplissent le site. Cela évite le sentiment d&#8217;arriver sur un service désert. Tous les sites de rencontre font ça.</li>
<li>Générer un contexte artificiel, par exemple pour un jeu vidéo.</li>
</ul>
<p>Faker est déjà très simple à utiliser :</p>

<div class="wp_syntax"><div class="code"><pre class="python" style="font-family:monospace;"><span style="color: #66cc66;">&gt;&gt;&gt;</span> <span style="color: #ff7700;font-weight:bold;">from</span> faker <span style="color: #ff7700;font-weight:bold;">import</span> Faker
<span style="color: #66cc66;">&gt;&gt;&gt;</span> f = Faker<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
<span style="color: #66cc66;">&gt;&gt;&gt;</span> f.<span style="color: black;">phone_number</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
u<span style="color: #483d8b;">'+69(4)8833689405'</span>
<span style="color: #66cc66;">&gt;&gt;&gt;</span> f.<span style="color: black;">phone_number</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
u<span style="color: #483d8b;">'1-201-240-9452'</span>
<span style="color: #66cc66;">&gt;&gt;&gt;</span> f.<span style="color: black;">phone_number</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
u<span style="color: #483d8b;">'+95(8)7680219065'</span>
<span style="color: #66cc66;">&gt;&gt;&gt;</span> f.<span style="color: black;">phone_number</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
u<span style="color: #483d8b;">'754-833-9664x654'</span>
<span style="color: #66cc66;">&gt;&gt;&gt;</span> f.<span style="color: #dc143c;">email</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
u<span style="color: #483d8b;">'bentley.gaylord@batz.org'</span>
<span style="color: #66cc66;">&gt;&gt;&gt;</span> f.<span style="color: #dc143c;">email</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
u<span style="color: #483d8b;">'ludwig.rohan@adamskoch.info'</span></pre></div></div>

<p>Et surtout déjà très riche. Il y a <strong>plus d&#8217;une centaine</strong> de choses que vous pouvez générer. Quelques exemples :</p>
<ul>
<li><code>f.city</code></li>
<li><code>f.company</code></li>
<li><code>f.credit_card_number</code></li>
<li><code>f.date_time_this_decade</code></li>
<li><code>f.geo_coordinate</code></li>
<li><code>f.hex_color</code></li>
<li><code>f.ipv4</code></li>
<li><code>f.language_code</code></li>
<li><code>f.md5</code></li>
<li><code>f.mime_type</code></li>
<li><code>f.month_name</code></li>
<li><code>f.paragraph</code></li>
<li><code>f.password</code></li>
<li><code>f.postcode</code></li>
<li><code>f.ssn</code></li>
<li><code>f.street_address</code></li>
<li><code>f.url</code></li>
<li><code>f.user_agent</code></li>
<li><code>f.word</code></li>
</ul>
<p>faker est très flexible. En effet, beaucoup d&#8217;éléments existent en plusieurs versions ou de manière composée et utilisables séparément. Ainsi, on peut prendre l&#8217;IPV4 ou V6, choisir l&#8217;adresse entière ou le code postal seul, récupérer le nom complet, ou juste le prénom :</p>

<div class="wp_syntax"><div class="code"><pre class="python" style="font-family:monospace;"><span style="color: #66cc66;">&gt;&gt;&gt;</span> f.<span style="color: black;">name</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
u<span style="color: #483d8b;">'Olin McCullough'</span>
<span style="color: #66cc66;">&gt;&gt;&gt;</span> f.<span style="color: black;">first_name</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
u<span style="color: #483d8b;">'Brandie'</span></pre></div></div>

<p>Mais en plus, faker est localisé :</p>

<div class="wp_syntax"><div class="code"><pre class="python" style="font-family:monospace;"><span style="color: #66cc66;">&gt;&gt;&gt;</span> f = Faker<span style="color: black;">&#40;</span><span style="color: #dc143c;">locale</span>=<span style="color: #483d8b;">&quot;fr_FR&quot;</span><span style="color: black;">&#41;</span>
<span style="color: #66cc66;">&gt;&gt;&gt;</span> f.<span style="color: black;">name</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
u<span style="color: #483d8b;">'Martine de la Petitjean'</span></pre></div></div>

<p>Enfin, on peut créer ses propres générateurs et fournisseurs de données au besoin.</p>
<p>Comme dirait Max, c&#8217;est d&#8217;la balle baby !</p>
 <p><a href="http://sametmax.com/?flattrss_redirect&amp;id=10029&amp;md5=6c79ec4bd68cf9513e85161d9af76a36" title="Flattr" target="_blank"><img src="media/fc229d00.flattr-badge-large.png" data-original-source="http://sametmax.com/wp-content/plugins/flattr/img/flattr-badge-large.png" alt="flattr this!"/></a></p>


feed_id: http://sametmax.com/?p=10380
title: Le potentiel de WAMP, autobahn et crossbar.io
uri: Le-potentiel-de-WAMP-autobahn-et-crossbar-io
url: http://sametmax.com/le-potentiel-de-wamp-autobahn-et-crossbar-io/
content:
<p>Je sais, je sais, je vous fais chier avec <a href="http://sametmax.com/crossbar-le-futur-des-applications-web-python/">crossbar</a> et <a href="http://sametmax.com/un-petit-gout-de-meteor-js-en-python/">autobahn</a>.</p>
<p>Mais ça me tue de ne pas voir plus de monde exploiter cette techno.</p>
<p>Pendant que Max fait la sieste, j&#8217;ai pris mon stylo et j&#8217;ai fait la liste des besoins d&#8217;une app Web actuelle. Quels sont les composants qu&#8217;on utilise presque systématiquement, mais en agrégeant divers bouts de trucs à droite et à gauche ?</p>
<p>Ensuite j&#8217;ai regardé les possibilités des outils WAMP :</p>
<ul>
<li>PUB/SUB et RPC.</li>
<li>Asynchrone.</li>
<li>Gestionnaire de process intégré.</li>
<li>Serveur stand alone qui n&#8217;a pas besoin d&#8217;un proxy pour être en prod.</li>
</ul>
<p>M&#8217;inspirant de cela, et du travail que je suis en train de faire avec l&#8217;équipe de Tavendo pour faire une API flaskesque pour autobahn, j&#8217;ai prototypé une API d&#8217;un framework Web qu&#8217;on pourrait coder au dessus de cette techno.</p>
<p>Voilà ce que ça donne&#8230;</p>
<p>Une API qui mélange flask et nodejs pour le Web</p>

<div class="wp_syntax"><div class="code"><pre class="python" style="font-family:monospace;">app = Application<span style="color: black;">&#40;</span><span style="color: #483d8b;">'YourProjectName'</span><span style="color: black;">&#41;</span>
&nbsp;
<span style="color: #808080; font-style: italic;"># Envoyer et recevoir des requêtes HTTP</span>
@app.<span style="color: black;">http</span>.<span style="color: black;">post</span><span style="color: black;">&#40;</span>r<span style="color: #483d8b;">'/form'</span><span style="color: black;">&#41;</span>
<span style="color: #ff7700;font-weight:bold;">def</span> _<span style="color: black;">&#40;</span>req, res<span style="color: black;">&#41;</span>:
    res.<span style="color: black;">json</span><span style="color: black;">&#40;</span><span style="color: black;">&#123;</span><span style="color: #483d8b;">'data'</span>: <span style="color: #483d8b;">'pouet'</span><span style="color: black;">&#125;</span><span style="color: black;">&#41;</span>
&nbsp;
@app.<span style="color: black;">http</span>.<span style="color: black;">get</span><span style="color: black;">&#40;</span>r<span style="color: #483d8b;">'/user/:id/'</span><span style="color: black;">&#41;</span>
<span style="color: #ff7700;font-weight:bold;">def</span> _<span style="color: black;">&#40;</span>req, res<span style="color: black;">&#41;</span>:
    res.<span style="color: black;">render</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'index.html'</span>, <span style="color: black;">&#123;</span><span style="color: #483d8b;">'data'</span>: <span style="color: #483d8b;">'pouet'</span><span style="color: black;">&#125;</span><span style="color: black;">&#41;</span>
&nbsp;
<span style="color: #808080; font-style: italic;"># Servir des fichiers statiques</span>
@app.<span style="color: black;">http</span>.<span style="color: black;">serve</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'uri'</span>, <span style="color: #483d8b;">'/path/to/dir'</span>, <span style="color: black;">&#91;</span>allow_index<span style="color: black;">&#93;</span><span style="color: black;">&#41;</span>
&nbsp;
app.<span style="color: black;">run</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span></pre></div></div>

<p>Comme c&#8217;est asynchrone, on a de très bonnes perfs. Comme c&#8217;est basé sur Twisted, on a pas besoin d&#8217;un serveur wsgi (gunicorn, uwsgi, etc) ni d&#8217;un proxy (nginx) devant. On peut le mettre en prod tel quel.</p>
<p>Parti de ce principe, on peut ajouter la gestion du PUB/SUB et du RPC pour WAMP :</p>

<div class="wp_syntax"><div class="code"><pre class="python" style="font-family:monospace;"><span style="color: #808080; font-style: italic;"># Callback attendant l'événement</span>
@app.<span style="color: black;">wamp</span>.<span style="color: black;">event</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'auth.signedin'</span><span style="color: black;">&#41;</span>
<span style="color: #ff7700;font-weight:bold;">def</span> _<span style="color: black;">&#40;</span>ctx, a, b, c<span style="color: black;">&#41;</span>:
    <span style="color: #ff7700;font-weight:bold;">pass</span>
&nbsp;
<span style="color: #808080; font-style: italic;"># déclenchement de l'événément</span>
app.<span style="color: black;">wamp</span>.<span style="color: black;">pub</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'auth.signedin'</span><span style="color: black;">&#41;</span>
&nbsp;
<span style="color: #808080; font-style: italic;"># Déclaration du fonnction appelable à distance</span>
@app.<span style="color: black;">wamp</span>.<span style="color: black;">remote</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'auth.signin'</span><span style="color: black;">&#41;</span>
<span style="color: #ff7700;font-weight:bold;">def</span> _<span style="color: black;">&#40;</span>ctx, a, b, c<span style="color: black;">&#41;</span>:
    <span style="color: #ff7700;font-weight:bold;">pass</span>
&nbsp;
<span style="color: #808080; font-style: italic;"># appel de la fonnction</span>
app.<span style="color: black;">wamp</span>.<span style="color: black;">call</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'auth.signin'</span><span style="color: black;">&#41;</span></pre></div></div>

<p>On est souvent perdu quand on fait de l&#8217;asynchrone pour la première fois avec Python car on ne sait pas comment lancer du code après <code>.run()</code>. On peut régler la question proposant des hooks pour les instants clés de l&#8217;app.</p>

<div class="wp_syntax"><div class="code"><pre class="python" style="font-family:monospace;"><span style="color: #808080; font-style: italic;"># Callback à lancer quand l'app est prête</span>
@app.<span style="color: black;">on</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'app.ready'</span><span style="color: black;">&#41;</span>
<span style="color: #ff7700;font-weight:bold;">def</span> _<span style="color: black;">&#40;</span>ctx, args<span style="color: black;">&#41;</span>:
    <span style="color: #ff7700;font-weight:bold;">pass</span>
&nbsp;
<span style="color: #808080; font-style: italic;"># Signalement que l'app est prête (fait automatiquement en interne</span>
<span style="color: #808080; font-style: italic;"># pour les moments les plus importants)</span>
app.<span style="color: black;">emit</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'app.ready'</span><span style="color: black;">&#41;</span></pre></div></div>

<p>Et tant qu&#8217;on y est, puisqu&#8217;on a une event loop, profitons en pour proposer du CRON intégré à l&#8217;app. C&#8217;est moins chiant à déployer qu&#8217;un script CRON, c&#8217;est cross plateforme, et on a accès facilement à toute sa stack.</p>

<div class="wp_syntax"><div class="code"><pre class="python" style="font-family:monospace;"><span style="color: #808080; font-style: italic;"># Lancer du code tous les x temps ou a une date précise</span>
@app.<span style="color: black;">cron</span><span style="color: black;">&#40;</span>every=seconds<span style="color: black;">&#41;</span>
@app.<span style="color: black;">cron</span><span style="color: black;">&#40;</span>every=timedelta, overlap=<span style="color: #008000;">False</span><span style="color: black;">&#41;</span>
@app.<span style="color: black;">cron</span><span style="color: black;">&#40;</span>hour=<span style="color: #ff4500;">7</span>, minute=<span style="color: #ff4500;">30</span>, day_of_week=<span style="color: #ff4500;">1</span><span style="color: black;">&#41;</span>
@app.<span style="color: black;">cron</span><span style="color: black;">&#40;</span>when=<span style="color: #dc143c;">datetime</span><span style="color: black;">&#41;</span>
<span style="color: #ff7700;font-weight:bold;">def</span> _<span style="color: black;">&#40;</span>ctx, args<span style="color: black;">&#41;</span>:
    <span style="color: #ff7700;font-weight:bold;">pass</span></pre></div></div>

<p>Pourquoi s&#8217;arrêter là ? Event loop + message passing + safe queues + workers = tasks queues !</p>

<div class="wp_syntax"><div class="code"><pre class="python" style="font-family:monospace;"><span style="color: #808080; font-style: italic;"># Créer une file d'attente</span>
queue = @app.<span style="color: black;">queue</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'name'</span>, <span style="color: black;">&#91;</span>workers<span style="color: black;">&#93;</span>, <span style="color: black;">&#91;</span>result_backend<span style="color: black;">&#93;</span><span style="color: black;">&#41;</span>
&nbsp;
<span style="color: #808080; font-style: italic;"># Callback appelé par un worker quand il depop ce </span>
<span style="color: #808080; font-style: italic;"># message dans la file</span>
@queue.<span style="color: black;">task</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'encode.video'</span><span style="color: black;">&#41;</span>
<span style="color: #ff7700;font-weight:bold;">def</span> _<span style="color: black;">&#40;</span>ctx, data<span style="color: black;">&#41;</span>:
    <span style="color: #ff7700;font-weight:bold;">pass</span>
&nbsp;
<span style="color: #808080; font-style: italic;"># Envoie d'une tache dans la queu</span>
queue.<span style="color: black;">append</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'encode.video'</span>, data<span style="color: black;">&#41;</span></pre></div></div>

<p>Comme on utilise Twisted, on a accès à une chiée de protocoles, et on peut aussi créer les siens. On peut donc imaginer un système de plugins qui rajoute des protocoles supportés :</p>

<div class="wp_syntax"><div class="code"><pre class="python" style="font-family:monospace;">app = Application<span style="color: black;">&#40;</span><span style="color: #483d8b;">'YourProjectName'</span><span style="color: black;">&#41;</span>
app.<span style="color: black;">plug</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'lib.ajoutant.sms'</span>, <span style="color: black;">&#91;</span>namespace<span style="color: black;">&#93;</span><span style="color: black;">&#41;</span></pre></div></div>

<p>Si on en a beaucoup et que le namespace nous convient :</p>

<div class="wp_syntax"><div class="code"><pre class="python" style="font-family:monospace;">app = Application<span style="color: black;">&#40;</span><span style="color: #483d8b;">'YourProjectName'</span>, plugins=<span style="color: black;">&#40;</span><span style="color: #483d8b;">'lib1'</span>, <span style="color: #483d8b;">'lib2'</span>, <span style="color: #483d8b;">'etc'</span><span style="color: black;">&#41;</span><span style="color: black;">&#41;</span></pre></div></div>

<p>Exemples de plugins possibles :</p>

<div class="wp_syntax"><div class="code"><pre class="python" style="font-family:monospace;"><span style="color: #808080; font-style: italic;"># Recevoir et envoyer des SMS (via un service type twilio, une gateway kannel ou</span>
<span style="color: #808080; font-style: italic;"># un modem physique)</span>
@app.<span style="color: black;">sms</span>.<span style="color: black;">receive</span><span style="color: black;">&#40;</span>r<span style="color: #483d8b;">'LOVE <span style="color: #000099; font-weight: bold;">\w</span>+ <span style="color: #000099; font-weight: bold;">\w</span>+'</span><span style="color: black;">&#41;</span>
<span style="color: #ff7700;font-weight:bold;">def</span> _<span style="color: black;">&#40;</span>ctx, args<span style="color: black;">&#41;</span>:
    <span style="color: #ff7700;font-weight:bold;">pass</span>
app.<span style="color: black;">sms</span>.<span style="color: black;">send</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'test'</span>, <span style="color: black;">&#91;</span>contact<span style="color: black;">&#93;</span><span style="color: black;">&#41;</span>
&nbsp;
&nbsp;
<span style="color: #808080; font-style: italic;"># Envoyer et recevoir des emails (via un server SMTP ou IMAP)</span>
@app.<span style="color: #dc143c;">email</span>.<span style="color: black;">receive</span><span style="color: black;">&#40;</span>src=r<span style="color: #483d8b;">'.*@sametmax.com'</span>, dest=r<span style="color: #483d8b;">'spam.*@*.'</span><span style="color: black;">&#41;</span>
<span style="color: #ff7700;font-weight:bold;">def</span> _<span style="color: black;">&#40;</span>ctx, args<span style="color: black;">&#41;</span>:
    <span style="color: #ff7700;font-weight:bold;">pass</span>
app.<span style="color: #dc143c;">email</span>.<span style="color: black;">send</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'test'</span>, <span style="color: black;">&#91;</span>contact, title, attachments<span style="color: black;">&#93;</span><span style="color: black;">&#41;</span>
&nbsp;
&nbsp;
<span style="color: #808080; font-style: italic;"># techniquement n'importe quel service de message pour lequel on peut écrire</span>
<span style="color: #808080; font-style: italic;"># un backend</span>
@app.<span style="color: black;">tweet</span>.<span style="color: black;">receive</span><span style="color: black;">&#40;</span>r<span style="color: #483d8b;">'Chat'</span><span style="color: black;">&#41;</span>
@app.<span style="color: black;">fb</span>.<span style="color: black;">receive</span><span style="color: black;">&#40;</span>r<span style="color: #483d8b;">'Like'</span><span style="color: black;">&#41;</span>
@app.<span style="color: black;">instagram</span>.<span style="color: black;">receive</span><span style="color: black;">&#40;</span>r<span style="color: #483d8b;">'Bouffe'</span><span style="color: black;">&#41;</span>
@app.<span style="color: black;">irc</span>.<span style="color: black;">message</span><span style="color: black;">&#40;</span>r<span style="color: #483d8b;">'dtc'</span><span style="color: black;">&#41;</span>
<span style="color: #ff7700;font-weight:bold;">def</span> _<span style="color: black;">&#40;</span>ctx, args<span style="color: black;">&#41;</span>:
    <span style="color: #ff7700;font-weight:bold;">pass</span></pre></div></div>

<p>Le problème des apps centrées sur un objet, c&#8217;est qu&#8217;elles ont souvent un design monolithique. Ce n&#8217;est pas un problème du concept d&#8217;app, c&#8217;est juste que les auteurs ont pensé &#8220;point d&#8217;entrée&#8221;, et pas &#8220;élément composable&#8221;.</p>
<p>Si besoin, on doit pouvoir composer une app via plusieurs sous-app :</p>

<div class="wp_syntax"><div class="code"><pre class="python" style="font-family:monospace;">app = Application<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
app.<span style="color: black;">embed</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'autre.app'</span><span style="color: black;">&#41;</span></pre></div></div>

<p>ou</p>

<div class="wp_syntax"><div class="code"><pre class="python" style="font-family:monospace;">app = Application<span style="color: black;">&#40;</span>embed=<span style="color: black;">&#91;</span><span style="color: #483d8b;">'app1'</span>, <span style="color: #483d8b;">'app2'</span>, <span style="color: #483d8b;">'app3'</span><span style="color: black;">&#93;</span><span style="color: black;">&#41;</span></pre></div></div>

<p>Il faut des hooks pour overrider la configuration, mais vous avez compris le principe.</p>
<p>Un autre problème avec les plateformes comme NodeJS, c&#8217;est qu&#8217;il est difficile d&#8217;utiliser plusieurs coeurs. C&#8217;est une des raisons du succès de Go.</p>
<p>Or, Crossbar encourage la division en plusieurs process qui communiquent entre eux (un peu comme les channels). Créons aussi une API pour ça :</p>

<div class="wp_syntax"><div class="code"><pre class="python" style="font-family:monospace;">p1 = app.<span style="color: black;">process</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
p2 = app.<span style="color: black;">process</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
&nbsp;
<span style="color: #808080; font-style: italic;"># Déclarer et appeler une procédure dans process 1</span>
@p1.<span style="color: black;">wamp</span>.<span style="color: black;">remote</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'auth.signin'</span><span style="color: black;">&#41;</span>
<span style="color: #ff7700;font-weight:bold;">def</span> _<span style="color: black;">&#40;</span>ctx, args<span style="color: black;">&#41;</span>:
    <span style="color: #ff7700;font-weight:bold;">pass</span>
&nbsp;
<span style="color: #808080; font-style: italic;"># Déclarer et appeler une procédure dans process 2</span>
@p2.<span style="color: black;">wamp</span>.<span style="color: black;">event</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'auth.signedin'</span><span style="color: black;">&#41;</span>
<span style="color: #ff7700;font-weight:bold;">def</span> _<span style="color: black;">&#40;</span>ctx, args<span style="color: black;">&#41;</span>:
    <span style="color: #ff7700;font-weight:bold;">pass</span></pre></div></div>

<p>Ainsi on profite enfin de plusieurs CPU. La même chose en plus facile à changer:</p>

<div class="wp_syntax"><div class="code"><pre class="python" style="font-family:monospace;"><span style="color: #808080; font-style: italic;"># Déclarer et appeler une procédure</span>
@app.<span style="color: black;">wamp</span>.<span style="color: black;">remote</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'auth.signin'</span><span style="color: black;">&#41;</span>
<span style="color: #ff7700;font-weight:bold;">def</span> _<span style="color: black;">&#40;</span>ctx, args<span style="color: black;">&#41;</span>:
    <span style="color: #ff7700;font-weight:bold;">pass</span>
&nbsp;
<span style="color: #808080; font-style: italic;"># Déclarer et appeler une procédure</span>
@app.<span style="color: black;">wamp</span>.<span style="color: black;">event</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'auth.signedin'</span><span style="color: black;">&#41;</span>
<span style="color: #ff7700;font-weight:bold;">def</span> _<span style="color: black;">&#40;</span>ctx, args<span style="color: black;">&#41;</span>:
    <span style="color: #ff7700;font-weight:bold;">pass</span>
&nbsp;
app.<span style="color: black;">processes</span><span style="color: black;">&#40;</span><span style="color: black;">&#123;</span>
    <span style="color: #ff4500;">1</span>: <span style="color: black;">&#91;</span><span style="color: #483d8b;">'wamp.remote:auth.signin'</span><span style="color: black;">&#93;</span>
    <span style="color: #ff4500;">2</span>: <span style="color: black;">&#91;</span><span style="color: #483d8b;">'wamp.event:auth.signedin'</span><span style="color: black;">&#93;</span>
<span style="color: black;">&#125;</span><span style="color: black;">&#41;</span></pre></div></div>

<p>En bonus, on fait la nique au GIL.</p>
<p>Mieux, on peut bouger ses process sur plusieurs machines :</p>
<p>Machine 1 (routeur):</p>

<div class="wp_syntax"><div class="code"><pre class="python" style="font-family:monospace;">router = Application<span style="color: black;">&#40;</span>endpoint=<span style="color: #483d8b;">&quot;0.0.0.0:8080&quot;</span><span style="color: black;">&#41;</span>
router.<span style="color: black;">run</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span></pre></div></div>

<p>Machine 2 (authentification):</p>

<div class="wp_syntax"><div class="code"><pre class="python" style="font-family:monospace;"><span style="color: #808080; font-style: italic;"># IP du router</span>
auth = Application<span style="color: black;">&#40;</span><span style="color: #483d8b;">'auth'</span>, connect_to=<span style="color: #483d8b;">&quot;182.64.1.15:8080&quot;</span><span style="color: black;">&#41;</span>
&nbsp;
<span style="color: #808080; font-style: italic;"># Nommage automatique en fonction du nom de la fonction</span>
<span style="color: #808080; font-style: italic;"># et de l'app, avec possibilité d'annuler ou overrider le prefix.</span>
<span style="color: #808080; font-style: italic;"># Ici du coup la fonction s'appellera en RPC via 'auth.signin'</span>
@auth.<span style="color: black;">wamp</span>.<span style="color: black;">remote</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
<span style="color: #ff7700;font-weight:bold;">def</span> signin<span style="color: black;">&#40;</span>ctx, args<span style="color: black;">&#41;</span>:
    <span style="color: #ff7700;font-weight:bold;">pass</span>
&nbsp;
auth.<span style="color: black;">run</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span></pre></div></div>

<p>Machine 3 (API REST):</p>

<div class="wp_syntax"><div class="code"><pre class="python" style="font-family:monospace;">web = Application<span style="color: black;">&#40;</span><span style="color: #483d8b;">'site'</span>, connect_to=<span style="color: #483d8b;">&quot;182.64.1.15:8080&quot;</span><span style="color: black;">&#41;</span>
&nbsp;
@web.<span style="color: black;">http</span>.<span style="color: black;">post</span><span style="color: black;">&#40;</span>r<span style="color: #483d8b;">'api/auth/'</span><span style="color: black;">&#41;</span>
<span style="color: #ff7700;font-weight:bold;">def</span> _<span style="color: black;">&#40;</span>req, res<span style="color: black;">&#41;</span>:
    <span style="color: #dc143c;">user</span> = <span style="color: #ff7700;font-weight:bold;">yield</span> res.<span style="color: black;">wamp</span>.<span style="color: black;">call</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'auth.signin'</span>,
                               req.<span style="color: black;">POST</span><span style="color: black;">&#91;</span><span style="color: #483d8b;">'username'</span><span style="color: black;">&#93;</span>,
                               req.<span style="color: black;">POST</span><span style="color: black;">&#91;</span><span style="color: #483d8b;">'password'</span><span style="color: black;">&#93;</span><span style="color: black;">&#41;</span><span style="color: #66cc66;">*</span>
    <span style="color: #ff7700;font-weight:bold;">if</span> <span style="color: #dc143c;">user</span>
        <span style="color: #dc143c;">user</span> = <span style="color: #ff7700;font-weight:bold;">yield</span> res.<span style="color: black;">wamp</span>.<span style="color: black;">pub</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'auth.signedin'</span>, <span style="color: #dc143c;">user</span>.<span style="color: black;">userid</span><span style="color: black;">&#41;</span>
        res.<span style="color: black;">json</span><span style="color: black;">&#40;</span><span style="color: black;">&#123;</span><span style="color: #483d8b;">'token'</span>: <span style="color: #dc143c;">user</span>.<span style="color: #dc143c;">token</span><span style="color: black;">&#125;</span><span style="color: black;">&#41;</span>
    <span style="color: #ff7700;font-weight:bold;">else</span>:
        res.<span style="color: black;">json</span><span style="color: black;">&#40;</span><span style="color: black;">&#123;</span><span style="color: #483d8b;">'error'</span>: <span style="color: #483d8b;">'nope'</span><span style="color: black;">&#125;</span><span style="color: black;">&#41;</span>
&nbsp;
&nbsp;
@web.<span style="color: black;">http</span>.<span style="color: black;">get</span><span style="color: black;">&#40;</span>r<span style="color: #483d8b;">'api/stuff/'</span><span style="color: black;">&#41;</span>
<span style="color: #ff7700;font-weight:bold;">def</span> _<span style="color: black;">&#40;</span>req, res<span style="color: black;">&#41;</span>:
    res.<span style="color: black;">json</span><span style="color: black;">&#40;</span>get_stuff<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span><span style="color: black;">&#41;</span>
&nbsp;
@web.<span style="color: black;">http</span>.<span style="color: black;">serve</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'uri'</span>, <span style="color: #483d8b;">'/path/to/dir'</span>, <span style="color: black;">&#91;</span>allow_index<span style="color: black;">&#93;</span><span style="color: black;">&#41;</span>
&nbsp;
web.<span style="color: black;">run</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span></pre></div></div>

<p>Et vous savez le plus beau dans tout ça ? En Python on a plein de libs qui sont encore bloquantes. En théorie on ne peut pas les utiliser dans les apps asynchrones. Quand on a toute sa logique métiers dans des classes d&#8217;ORM, c&#8217;est balot. Mais pas ici ! On met un process avec tous ces appels bloquants, et on les appelle depuis des process non bloquant en RPC de manière asynchrone. Pif, paf, pouf, problème isolé.</p>
<p>Après, libre à son imagination de rajouter des fonctionnalités de confort&#8230;</p>
<p>Callback qui sera appelé seulement x fois :</p>

<div class="wp_syntax"><div class="code"><pre class="python" style="font-family:monospace;"><span style="color: #808080; font-style: italic;"># Déclarer et appeler une procédure</span>
@p1.<span style="color: black;">wamp</span>.<span style="color: black;">event</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'auth.signedin'</span>, options=<span style="color: black;">&#123;</span><span style="color: #483d8b;">'limit_calls'</span>: x<span style="color: black;">&#125;</span> <span style="color: black;">&#41;</span>
<span style="color: #ff7700;font-weight:bold;">def</span> _<span style="color: black;">&#40;</span>ctx, args<span style="color: black;">&#41;</span>:
    <span style="color: #ff7700;font-weight:bold;">pass</span></pre></div></div>

<p>Raccourcis pour les opérations courantes :</p>

<div class="wp_syntax"><div class="code"><pre class="python" style="font-family:monospace;"><span style="color: #808080; font-style: italic;"># Recevoir et envoyer un événement</span>
@app.<span style="color: black;">sub</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'auth.signin'</span><span style="color: black;">&#41;</span>
<span style="color: #ff7700;font-weight:bold;">def</span> _<span style="color: black;">&#40;</span>ctx, <span style="color: #66cc66;">*</span>args<span style="color: black;">&#41;</span>:
    <span style="color: #808080; font-style: italic;"># ctx.pub</span>
@app.<span style="color: black;">pub</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'auth.signedin'</span><span style="color: black;">&#41;</span>
&nbsp;
<span style="color: #808080; font-style: italic;"># Déclarer et appeler une procédure</span>
@app.<span style="color: black;">proc</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'auth.signedin'</span><span style="color: black;">&#41;</span>
<span style="color: #ff7700;font-weight:bold;">def</span> _<span style="color: black;">&#40;</span>ctx, args<span style="color: black;">&#41;</span>:
    <span style="color: #808080; font-style: italic;"># ctx.call</span>
app.<span style="color: black;">rpc</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span></pre></div></div>

<p>Comme je vous l&#8217;avais expliqué, crossbar peut gérer le cycle de vie de services externes à votre application au démarrage. Autant exposer cette API programativement :</p>

<div class="wp_syntax"><div class="code"><pre class="python" style="font-family:monospace;">@app.<span style="color: black;">service</span><span style="color: black;">&#40;</span><span style="color: black;">&#91;</span><span style="color: #483d8b;">'/urs/bin/nodejs'</span>, <span style="color: #483d8b;">'script.js'</span><span style="color: black;">&#93;</span>, <span style="color: black;">&#91;</span><span style="color: #dc143c;">user</span><span style="color: black;">&#93;</span>, <span style="color: black;">&#91;</span>group<span style="color: black;">&#93;</span><span style="color: black;">&#41;</span></pre></div></div>

<p><code>.run()</code>, c&#8217;est cool, mais si on veut changer des options via la ligne de commande, faut se taper tout le boulot alors que ça pourrait très bien se générer automatiquement :</p>

<div class="wp_syntax"><div class="code"><pre class="python" style="font-family:monospace;">@app.<span style="color: black;">cmd_run</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span></pre></div></div>

<p>Et si vous faites : <code>python sites.py --debug=true --endpoint=0.0.0.0:5252</code>, ça le prend automatiquement en compte. Y a pas de raison de se faire chier.</p>
<p>En parlant de générer automatiquement des trucs, le fichiers de configs pour les services externes sur lesquels on peut avoir envie de brancher notre app, c&#8217;est toujours galère. Autant fournir un exemple de base qui est sûr de toujours marcher, généré avec les paramètres de notre app :</p>

<div class="wp_syntax"><div class="code"><pre class="bash" style="font-family:monospace;">python site.py template centos:nginx
python site.py template ubuntu:upstart
python site.py template bsd:systemd <span style="color: #666666; font-style: italic;"># :D</span></pre></div></div>

<p>On peut partir très loin dans le délire &#8220;battery included&#8221;. Typiquement, on peut fournir des services externes nous même puisque crossbar nous le propose, et coder des versions moins bien, mais compatibles (et suffisantes pour les petits sites), de projets toujours utilses :</p>
<ul>
<li>cache (compatible redis)</li>
<li>live settings (compatible etcd) mais avec en prime un event wamp propagé à chaque</li>
<p>  changement de valeur</p>
<li>build (compatible, heu, j&#8217;en sais rien) qui s&#8217;occupe en tâche de fond de surveiller le >système de fichier et lancer les compilations, les minifications, les copies, les tests unittaires, etc.</li
<li>logging centralisé (compatible sentry).</li>
<li>Un bridge WAMP/REST qui permet d&#8217;envoyer et recevoir des events WAMP sur votre app Django ou flask en utilisant HTTP.</li>
</ul>
<p>On plug tout ça a une admin Web.</p>
<p>J&#8217;espère que je vous ai donné maintenant l&#8217;envie de vous plonger un peu plus dans cette techno, et peut être coder quelque chose avec.</p>
<p>Il n&#8217;y a plus d&#8217;excuses pour ne pas avoir de framework web next gen, ultime de la mort qui tue en Python. A part le fait qu&#8217;on soit des feignasses.</p>
<p>Ah, merde, on est foutus.</p>
 <p><a href="http://sametmax.com/?flattrss_redirect&amp;id=10380&amp;md5=3eea15c9f696fc0f66b07cf105a749c1" title="Flattr" target="_blank"><img src="media/fc229d00.flattr-badge-large.png" data-original-source="http://sametmax.com/wp-content/plugins/flattr/img/flattr-badge-large.png" alt="flattr this!"/></a></p>


feed_id: http://sametmax.com/?p=10371
title: 10 raisons égoïstes pour s’investir dans les préliminaires
uri: 10-raisons-égoïstes-pour-s-investir-dans-les-préliminaires
url: http://sametmax.com/10-raisons-egoistes-pour-sinvestir-dans-les-preliminaires/
content:
<p>Je pense que vous vous êtes TOUS fait rabâcher que les préliminaires c&#8217;est important  qu&#8217;il faut s&#8217;occuper de l&#8217;autre, le sexe c&#8217;est du partage, et gna gna gna, bla bla bla, trolololo lololo lololo.</p>
<p>Le truc c&#8217;est que ce genre d&#8217;arguments ne va parler qu&#8217;à des gens qui sont DÉJÀ de nature à en faire. Prêcher des convertis, c&#8217;est bon pour l&#8217;égo mais ça construit pas grand chose. C&#8217;est pas bon comme Lego. Ok, je => [].</p>
<p>Bref, voici 10 bonnes raisons de faire des préliminaires, parce que ça vous rapporte quelque chose <strong>à vous</strong>.</p>
<h2>1 &#8211; On dure plus longtemps</h2>
<p>Passé une certaine durée d’érection, le corps s&#8217;adapte pour passer en mode endurance. Du coup plus de préliminaires = plus de temps à bander sans trop monter en sauce = plus de temps à bander tout court. </p>
<p>Donc plus de baise. Des performances plus flatteuses. Le temps d&#8217;essayer plus de positions.</p>
<p>Et puis plus d&#8217;opportunité de la faire jouir, puisque ça prend souvent plus de temps que pour nous. Et franchement, qu&#8217;est-ce qu&#8217;il y a de plus sexy qu&#8217;une meuf à qui on est en train de faire prendre son pied ?</p>
<h2>2 &#8211; On chope plus</h2>
<p>Les meufs parlent entre elles. Si vous êtes un bon coup, ça se saura : un secret, c&#8217;est quelque chose que l&#8217;on ne répète qu&#8217;à une personne à la fois. Du coup, rapidement, les copines des copines (un degré de séparation est nécessaire le plus souvent pour en bénéficier) vous voient comme un partenaire sexuel valable.</p>
<h2>3 &#8211; On est en meilleure forme</h2>
<p>Les préliminaire impliquent souvent de tenir des positions qui font travailler vos muscles, votre cœur, votre respiration. Plus il y a de préliminaires, plus votre séance de gym en l&#8217;air devient complète.</p>
<p>Franchement, garder la forme en niquant, y a pire.</p>
<h2>4 &#8211; On a une plus belle peau</h2>
<p>Plus de transpiration et plus de caresses, plus de frictions donc un scrubing + hammam gratos. Votre peau va s&#8217;en trouver toute renouvelée.</p>
<p>Or, scoop, la peau est un facteur hyper important de sex appeal. En fait, avoir une belle peau compense beaucoup d&#8217;autres problèmes. Par exemple, si vous êtes en surpoids avec un superbe épiderme, vous paraissez en bonne santé et donc baisable, si vous avez une super silhouette et une peau de merde, vous donnez autant d&#8217;envie qu&#8217;un lépreux.</p>
<p>Désolé pour les gens à peau difficile, je connais ça, j&#8217;ai été victime de tout un tas de trucs dermatologiquement inesthétiques.</p>
<h2>5 &#8211; Plus de chance de pouvoir choisir où vous venez </h2>
<p>Si vous aimez lui gicler dans la bouche, le cul ou sur les nichons, avoir bien fait monter en température la demoiselle aide beaucoup. Dans le feu de l&#8217;action, on accepte beaucoup plus de choses. La sodomie, douloureuse à froid, peut devenir super excitante quand elle est à fond. Avaler se fera parfois naturellement si vous lui avez léché copieusement la touffe avant. Etc.</p>
<h2>6 &#8211; Plus de sexe avec la même partenaire</h2>
<p>Ça stimule la libido. Et donc si vous avez une fille qui à pas souvent envie, tout d&#8217;un coup, paf, elle répond vachement mieux aux sollicitations. Merci capitaine obvious, tu peux refermer ta braguette maintenant.</p>
<h2>7 &#8211; Plus de jeux pervers</h2>
<p>Ça va avec les deux précédents, mais si vous faites frémir votre partenaire, elle sera forcément plus en confiance pour mettre un costume d&#8217;écolière et s&#8217;empaler sur une tentacule en plastique tout en criant &#8220;Yameru, Kyōju-Sama !&#8221;.</p>
<p>Il faut dire que le cul, ça peut facilement tourner en rond. Du coup, faire des trucs funs et osés en entrée, ça amène un peu de renouveau.</p>
<h2>8 &#8211; Plus de temps pour mettre la capote</h2>
<p>Franchement, c&#8217;est toujours mieux d&#8217;enfiler le bout de plastique quand on a sa langue dans sa chatte que de s&#8217;arrêter pour ça.</p>
<h2>9 &#8211; Plus de temps pour voir si c&#8217;est une vraie rousse</h2>
<p>Je me pose toujours la question.</p>
<h2>10 &#8211; Un passport pour les quickies</h2>
<p>Envie de venir en deux minutes ? Vous pouvez, vous lui avez révolutionné la Sardaigne hier ! C&#8217;est le moment de la monter directement sur la table basse, lui baisser le fut de 20 cm, venir, et repartir comme si de rien n&#8217;était.</p>
 <p><a href="http://sametmax.com/?flattrss_redirect&amp;id=10371&amp;md5=8429c4b801f4f3460ea2991692e2f6a3" title="Flattr" target="_blank"><img src="media/fc229d00.flattr-badge-large.png" data-original-source="http://sametmax.com/wp-content/plugins/flattr/img/flattr-badge-large.png" alt="flattr this!"/></a></p>


feed_id: http://sametmax.com/?p=10360
title: Requests fait partie de la lib standard !
uri: Requests-fait-partie-de-la-lib-standard
url: http://sametmax.com/requests-fait-partie-de-la-lib-standard/
content:
<p>Enfin, presque :)</p>
<p>J&#8217;étais en train d&#8217;installer en vieux requirements.txt quand soudain :</p>

<div class="wp_syntax"><div class="code"><pre class="python" style="font-family:monospace;">  File <span style="color: #483d8b;">&quot;
&nbsp;
/usr/lib/python2.7/dist-packages/pip/download.py&quot;</span>, line <span style="color: #ff4500;">23</span>, <span style="color: #ff7700;font-weight:bold;">in</span> <span style="color: #66cc66;">&lt;</span>module<span style="color: #66cc66;">&gt;</span>
    <span style="color: #ff7700;font-weight:bold;">from</span> requests.<span style="color: black;">adapters</span> <span style="color: #ff7700;font-weight:bold;">import</span> BaseAdapter
<span style="color: #008000;">ImportError</span>: No module named adapters</pre></div></div>

<p>Wut ?</p>
<p>Mon erreur était due à une version de requests trop ancienne. J&#8217;upgrade et ça remarche. Mais ça m&#8217;a intrigué :</p>

<div class="wp_syntax"><div class="code"><pre class="bash" style="font-family:monospace;">⟩ grin <span style="color: #ff0000;">&quot;requests&quot;</span>  <span style="color: #000000; font-weight: bold;">/</span>usr<span style="color: #000000; font-weight: bold;">/</span>lib<span style="color: #000000; font-weight: bold;">/</span>python2.7<span style="color: #000000; font-weight: bold;">/</span>dist-packages<span style="color: #000000; font-weight: bold;">/</span>pip<span style="color: #000000; font-weight: bold;">/</span>
<span style="color: #000000; font-weight: bold;">/</span>usr<span style="color: #000000; font-weight: bold;">/</span>lib<span style="color: #000000; font-weight: bold;">/</span>python2.7<span style="color: #000000; font-weight: bold;">/</span>dist-packages<span style="color: #000000; font-weight: bold;">/</span>pip<span style="color: #000000; font-weight: bold;">/</span>download.py:
   <span style="color: #000000;">22</span> : import requests, six
   <span style="color: #000000;">23</span> : from requests.adapters import BaseAdapter
   <span style="color: #000000;">24</span> : from requests.auth import AuthBase, HTTPBasicAuth
   <span style="color: #000000;">25</span> : from requests.compat import IncompleteRead
   <span style="color: #000000;">26</span> : from requests.exceptions import InvalidURL, ChunkedEncodingError
   <span style="color: #000000;">27</span> : from requests.models import Response
   <span style="color: #000000;">28</span> : from requests.structures import CaseInsensitiveDict
  <span style="color: #000000;">120</span> :         <span style="color: #666666; font-style: italic;"># Store the new username and password to use for future requests</span>
  <span style="color: #000000;">182</span> :         <span style="color: #666666; font-style: italic;"># We only work for requests with a host of localhost</span>
  <span style="color: #000000;">211</span> : class PipSession<span style="color: #7a0874; font-weight: bold;">&#40;</span>requests.Session<span style="color: #7a0874; font-weight: bold;">&#41;</span>:
  <span style="color: #000000;">548</span> :         except requests.HTTPError <span style="color: #c20cb9; font-weight: bold;">as</span> exc:
<span style="color: #000000; font-weight: bold;">/</span>usr<span style="color: #000000; font-weight: bold;">/</span>lib<span style="color: #000000; font-weight: bold;">/</span>python2.7<span style="color: #000000; font-weight: bold;">/</span>dist-packages<span style="color: #000000; font-weight: bold;">/</span>pip<span style="color: #000000; font-weight: bold;">/</span>index.py:
   <span style="color: #000000;">17</span> : import html5lib, requests, pkg_resources
   <span style="color: #000000;">18</span> : from requests.exceptions import SSLError
   <span style="color: #000000;">80</span> :         <span style="color: #666666; font-style: italic;"># The Session we'll use to make requests</span>
  <span style="color: #000000;">689</span> :         except requests.HTTPError <span style="color: #c20cb9; font-weight: bold;">as</span> exc:
  <span style="color: #000000;">692</span> :         except requests.ConnectionError <span style="color: #c20cb9; font-weight: bold;">as</span> exc:
  <span style="color: #000000;">697</span> :         except requests.Timeout:</pre></div></div>

<p>Je regarde le code source github, qui <a href="https://github.com/pypa/pip/blob/develop/pip/download.py">diffère</a> de celui de mon système :</p>

<div class="wp_syntax"><div class="code"><pre class="python" style="font-family:monospace;"><span style="color: #ff7700;font-weight:bold;">from</span> pip._vendor.<span style="color: black;">requests</span>.<span style="color: black;">adapters</span> <span style="color: #ff7700;font-weight:bold;">import</span> BaseAdapter, HTTPAdapter
<span style="color: #ff7700;font-weight:bold;">from</span> pip._vendor.<span style="color: black;">requests</span>.<span style="color: black;">auth</span> <span style="color: #ff7700;font-weight:bold;">import</span> AuthBase, HTTPBasicAuth
<span style="color: #ff7700;font-weight:bold;">from</span> pip._vendor.<span style="color: black;">requests</span>.<span style="color: black;">compat</span> <span style="color: #ff7700;font-weight:bold;">import</span> IncompleteRead
<span style="color: #ff7700;font-weight:bold;">from</span> pip._vendor.<span style="color: black;">requests</span>.<span style="color: #dc143c;">exceptions</span> <span style="color: #ff7700;font-weight:bold;">import</span> ChunkedEncodingError</pre></div></div>

<p>Mais ça reste bien requests.</p>
<p>Donc, si vous avez la 3.4, vous avez pip installé, qui maintenant embed requests. Donc Python 3.4 vient forcément avec requests \o/</p>
<p>Si vous supportez uniquement cette plateforme, vous pouvez faire :</p>

<div class="wp_syntax"><div class="code"><pre class="python" style="font-family:monospace;"><span style="color: #ff7700;font-weight:bold;">try</span>:
    <span style="color: #ff7700;font-weight:bold;">import</span> requests
<span style="color: #ff7700;font-weight:bold;">except</span> <span style="color: #008000;">ImportError</span>:
    <span style="color: #ff7700;font-weight:bold;">import</span> pip._vendor.<span style="color: black;">requests</span> <span style="color: #ff7700;font-weight:bold;">as</span> requests</pre></div></div>

<p>Et bénéficier de requests sans rien installer.</p>
<p>Tout ça pour ça.</p>
 <p><a href="http://sametmax.com/?flattrss_redirect&amp;id=10360&amp;md5=e11dac73209a8ba3f9bfbd52186e5fd6" title="Flattr" target="_blank"><img src="media/fc229d00.flattr-badge-large.png" data-original-source="http://sametmax.com/wp-content/plugins/flattr/img/flattr-badge-large.png" alt="flattr this!"/></a></p>


feed_id: http://sametmax.com/?p=10329
title: Crossbar, le futur des applications Web Python ?
uri: Crossbar-le-futur-des-applications-Web-Python
url: http://sametmax.com/crossbar-le-futur-des-applications-web-python/
content:
<p>Je suis <a href="http://crossbar.io/">crossbar.io</a>  depuis quelques temps maintenant, et je suis très, très étonné de ne pas plus en entendre parler dans la communauté Python.</p>
<p>Bon, en fait, à moitié étonné.</p>
<p>D&#8217;un côté, c&#8217;est une techno qui, à mon sens, représente ce vers quoi Python doit se diriger pour faire copain-copain avec Go/NodeJs et proposer une &#8220;killer feature&#8221; dans le monde des applications serveurs complexes.</p>
<p>De l&#8217;autre, hum, leur page d&#8217;accueil explique à quoi ça sert de cette manière :</p>
<blockquote><p> Crossbar.io is an application router which implements the Web Application Messaging Protocol (WAMP). WAMP provides asynchronous Remote Procedure Calls and Publish &#038; Subscribe (with <a href="http://fr.wikipedia.org/wiki/WebSocket" title="WebSocket" target="_blank">WebSocket</a> being one transport option) and allows to connect application components in distributed systems</p></blockquote>
<p>Moui, moui, moui monseigneur, mais concrètement, là, hein, je peux faire quoi avec ?</p>
<p>C&#8217;est toujours le problème avec les gens intelligents (hein Cortex ?) : ils font des trucs super cool, et personne ne comprend à quoi ça sert parce qu&#8217;il ne sont pas foutu de l&#8217;expliquer.</p>
<p>Moi je suis un peu con, alors je vais profiter qu&#8217;on soit tous au même niveau pour vous faire passer le message.</p>
<p>J&#8217;étais persuadé d&#8217;avoir mis la musique habituel&#8230; Je la remet:</p>

<!-- iframe plugin v:2.3 - wordpress.org/extend/plugins/iframe/ -->
<iframe width="560" height="315" src="//www.youtube.com/embed/Z4IWG7IyizU" frameborder="0" scrolling="no" class="iframe-class"></iframe>
<h2>Web Application Message Protocol</h2>
<p>Je vous avais parlé d&#8217;<a href="http://sametmax.com/un-petit-gout-de-meteor-js-en-python/">autobahn</a> dernièrement, un client WAMP qui embarque aussi un routeur basique. Crossbar est la partie serveur, un routeur WAMP plus serieux.</p>
<p>Crossbar permet à tous les clients d&#8217;échanger des messages WAMP à travers lui. Bien entendu, un client WAMP peut parler au serveur crossbar et inversement comme un client HTTP peut parler à un serveur Apache/Nginx et inversement. Mais plus que ça, <strong>les clients peuvent parler entre eux, de manière transparente et simple.</strong> Comme un client <a href="http://fr.wikipedia.org/wiki/Advanced_Message_Queuing_Protocol" title="Advanced Message Queuing Protocol" target="_blank">AMQP</a> peut parler aux autres à travers un serveur <a href="http://en.wikipedia.org/wiki/RabbitMQ" title="RabbitMQ" target="_blank">RabbitMQ</a>.</p>
<p> Cependant ça ne vous avance pas si vous ne savez pas ce qu&#8217;est WAMP ou à quoi ça sert. La charrue avec la peau de l&#8217;ours, tout ça.</p>
<p>WAMP est un <a href="http://wamp.ws/spec/">protocole standardisé</a> pour échanger des messages entre deux systèmes. Ce n&#8217;est pas particulièrement lié à Python, on peut parler <strong>WAMP dans n&#8217;importe quel langage.</strong></p>
<p>Il fonctionne en effet, principalement, au dessus de Websocket, donc <strong>on peut l&#8217;utiliser directement dans le navigateur</strong>, dans Firefox, Chrome, Opera, Safari et même IE10, via une lib Javascript. Qui est en fait  juste un gros wrapper autour de l&#8217;API websocket standardisant la manière d&#8217;envoyer des données. Il n&#8217;y a rien de magique derrière, pas de formats compliqués, pas de binaire, c&#8217;est vraiment juste des appels websocket contenant des données formatées en JSON avec une certaine convention. <strong>En ce sens il fait penser à <a href="http://fr.slideshare.net/ngocdaothanh/sockjs-intro" title="sockjs" target="_blank">SockJS</a></strong> et (feu) socket.io.</p>
<p>Seulement contrairement aux solutions type SocketJS, <strong>il n&#8217;est pas limité au navigateur</strong>. Il y a <a href="http://wamp.ws/implementations/">des libs</a> pour l&#8217;utiliser dans un code serveur Python, C++ ou NodeJS, dans une app Android et même directement depuis les entrailles de Nginx ou d&#8217;une base de données Oracle (en SQL) avec certains routeurs.</p>
<div id="attachment_10340" class="wp-caption aligncenter" style="width: 567px"><a href="media/f62bdaeb.general1.png" data-original-source="http://sametmax.com/wp-content/uploads/2014/05/general1.png"><img src="media/93b86887.general.png" data-original-source="http://sametmax.com/wp-content/uploads/2014/05/general.png" alt="Schéma général du fonctionnement de WAMP" title="Comme HTTP, WAMP est juste un moyen de faire parvenir des données d&#039;un point A à un point B. Mais contrairement à HTTP, WAMP permet aux clients de parler entre eux, et pas juste au serveur." width="557" height="538" class="size-full wp-image-10340" /></a><p class="wp-caption-text">Comme HTTP, WAMP est juste un moyen de faire parvenir des données d&#039;un point A à un point B. Mais contrairement à HTTP, WAMP permet aux clients de parler entre eux, et pas juste au serveur.</p></div>
<p><strong>Comprenez bien, ça veut dire qu&#8217;on peut envoyer et recevoir des données arbitraires, en temps réel, entre tous ces systèmes, sans se prendre la tête, et de manière transparente.</strong></p>
<p>WAMP c&#8217;est donc comme, mais mieux que :</p>
<ul>
<li>des requêtes HTTP, car c&#8217;est du push, asynchrone et temps réel;</li>
<li>des requêtes websocket via SocketJS car c&#8217;est un standard, qui fonctionne SUR et ENTRE plusieurs services côtés serveurs malgré les différents langages;</li>
<li>des messages AMQP car ça marche dans le navigateur et ça se configure facilement.</li>
</ul>
<p>Bien utilisé, Crossbar permet d&#8217;amener Python dans la cours de frameworks &#8220;temps réel&#8221; novateurs comme <a href="http://fr.wikipedia.org/wiki/Meteor_(framework)" title="Meteor" target="_blank">MeteorJS</a>, et potentiellement les dépasser.</p>
<p>Car WAMP permet de faire deux choses. Simplement. Et bien.</p>
<h3>1 &#8211; Du PUB/SUB</h3>
<p>Donc de dire dans son code &#8220;appelle cette fonction quand cet événement arrive&#8221;. <strong>C&#8217;est comme les signaux de Django ou QT, mais ça marche à travers le réseau.</strong> On le fait souvent avec Redis ces temps-ci. Avec WAMP et Javascript, ça donne :</p>

<div class="wp_syntax"><div class="code"><pre class="javascript" style="font-family:monospace;"><span style="color: #006600; font-style: italic;">// connection au routeur WAMP (par exemple, crossbar.io)</span>
ab.<span style="color: #660066;">connect</span><span style="color: #009900;">&#40;</span><span style="color: #3366CC;">&quot;ws://localhost:9000&quot;</span><span style="color: #339933;">,</span> <span style="color: #003366; font-weight: bold;">function</span><span style="color: #009900;">&#40;</span>session<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
    <span style="color: #006600; font-style: italic;">// je m'inscris à un événement</span>
    session.<span style="color: #660066;">subscribe</span><span style="color: #009900;">&#40;</span><span style="color: #3366CC;">'un_evenement_qui_vous_plait'</span><span style="color: #339933;">,</span> <span style="color: #003366; font-weight: bold;">function</span><span style="color: #009900;">&#40;</span>topic<span style="color: #339933;">,</span> evt<span style="color: #009900;">&#41;</span><span style="color: #009900;">&#123;</span>
        <span style="color: #006600; font-style: italic;">// faire un truc avec les données reçues</span>
        <span style="color: #006600; font-style: italic;">// à chaque fois que l'événement est envoyé</span>
        <span style="color: #006600; font-style: italic;">// par exemple mettre la page à jour</span>
    <span style="color: #009900;">&#125;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
<span style="color: #009900;">&#125;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span></pre></div></div>

<div id="attachment_10343" class="wp-caption aligncenter" style="width: 571px"><a href="media/768088a6.sub.png" data-original-source="http://sametmax.com/wp-content/uploads/2014/05/sub.png"><img src="media/768088a6.sub.png" data-original-source="http://sametmax.com/wp-content/uploads/2014/05/sub.png" alt="Schéma de fonctionnement du subscribe de WAMP" title="Des client SUBscribe à un événément. Un événément est un nom arbitrairement choisit par le programmeur, et qu&#039;il va déclencher lui-même quand il pense qu&#039;il se passe quelque chose d&#039;important auquel il faut que le reste du signal réagisse. " width="561" height="572" class="size-full wp-image-10343" /></a><p class="wp-caption-text">Des client SUBscribe à un événément. Un événément est un nom arbitrairement choisit par le programmeur, et qu&#039;il va déclencher lui-même quand il pense qu&#039;il se passe quelque chose d&#039;important auquel il faut que le reste du signal réagisse. </p></div>
<p>Et ailleurs, dans une autre partie du fichier, ou même sur un autre navigateur Web :</p>

<div class="wp_syntax"><div class="code"><pre class="javascript" style="font-family:monospace;">ab.<span style="color: #660066;">connect</span><span style="color: #009900;">&#40;</span><span style="color: #3366CC;">&quot;ws://localhost:9000&quot;</span><span style="color: #339933;">,</span> <span style="color: #003366; font-weight: bold;">function</span><span style="color: #009900;">&#40;</span>session<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
    <span style="color: #006600; font-style: italic;">// création d'un événement auquel on attache des données</span>
    session.<span style="color: #660066;">publish</span><span style="color: #009900;">&#40;</span><span style="color: #3366CC;">'un_evenement_qui_vous_plait'</span><span style="color: #339933;">,</span> <span style="color: #009900;">&#91;</span><span style="color: #3366CC;">'des données'</span><span style="color: #009900;">&#93;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span></pre></div></div>

<div id="attachment_10344" class="wp-caption aligncenter" style="width: 565px"><a href="media/f07cb46a.pub.png" data-original-source="http://sametmax.com/wp-content/uploads/2014/05/pub.png"><img src="media/f07cb46a.pub.png" data-original-source="http://sametmax.com/wp-content/uploads/2014/05/pub.png" alt="Schéma de fonctionnement de PUB avec WAMP" title="Le programmeur décide que quelque chose d&#039;important arrive (création d&#039;un contenu, login d&#039;un utilisateur, notification), et PUBlish l&#039;événement" width="555" height="452" class="size-full wp-image-10344" /></a><p class="wp-caption-text">Le programmeur décide que quelque chose d&#039;important arrive (création d&#039;un contenu, login d&#039;un utilisateur, notification), et PUBlish l&#039;événement</p></div>
<p>Et oui, c&#8217;est tout. On se connecte à crossbar, et on discute. La fonction du <code>subscribe</code> sera alors appelée avec les données du <code>publish</code>. Même si il y a 3000 km entre les deux codes. Même si le code A est sur un navigateur et le B sur un autre, ou sur un serveur NodeJS, ou une app Android.</p>
<p>Ce qui fait peur au début, c&#8217;est qu&#8217;il y a TROP de flexibilité :</p>
<ul>
<li>Je dois attendre quoi comme événements ?</li>
<li>Qu&#8217;est-ce que je passe comme données ?</li>
<li>Est-ce que c&#8217;est rapide ? Léger ?</li>
</ul>
<p>Mais en fait c&#8217;est super simple : un événement c&#8217;est juste une action de votre application comme un élément (un post, un commentaire, un utilisateur&#8230;) ajouté, supprimé ou modifié. Finalement c&#8217;est le bon vieux <a href="http://fr.wikipedia.org/wiki/CRUD" title="CRUD" target="_blank">CRUD</a>, mais en temps réel, et en push, au lieu du pull. Vous choisissez un nom qui représente cette action, vous attachez des données à ce nom, voilà, c&#8217;est un événement que tous les abonnés peuvent recevoir.</p>
<p>Avec un bonus : ça marche sur le serveur aussi ! Votre code Python reçoit &#8220;ajouter un commentaire&#8221; comme événement ? Il peut ajouter le commentaire en base de données, envoyer un message à un service de cache ou à un autre site en NodeJS pour le mettre à jour, renvoyer un événement pour mettre à jour les pages Web et l&#8217;app Android, etc.</p>
<p>On peut passer n&#8217;importe quelles données qui peut se JSONiser. En gros n&#8217;importe quoi qu&#8217;on enverrait via HTTP. Donc des données très structurées, imbriquées et complexes comme des données géographiques, ou très simples comme des notifications.</p>
<p><strong>Avec PUB / SUB, WAMP remplace tout ce qu&#8217;on ferait normalement avec des appels AJAX dans le browser, et tout ce qu&#8217;on ferait avec des files de message côté serveur. </strong>Plus puissant encore, il permet de relier ces deux mondes.</p>
<p>Et même si on atteint pas les perfs de <a href="http://en.wikipedia.org/wiki/%C3%98MQ" title="ZeroMQ" target="_blank">ZeroMQ</a> (qui n&#8217;a pas de serveur central), c&#8217;est très <a href="https://into.aalto.fi/download/attachments/12324178/Huang_Fuguo_thesis_2.pdf?version=1&#038;modificationDate=1383290628000">performant et léger</a>.</p>
<h3>2 &#8211; Du RPC</h3>
<p>Appeler une fonction située ailleurs que dans son code. C&#8217;est vieux comme le monde (si vous avez des souvenirs douloureux de CORBA et SOAP, levez la main), et c&#8217;est extrêmement pratique. Pour faire simple, continuons avec un exemple en Javascript, mais rappelez-vous que ça marche pareil en C++ ou Python :</p>

<div class="wp_syntax"><div class="code"><pre class="javascript" style="font-family:monospace;">ab.<span style="color: #660066;">connect</span><span style="color: #009900;">&#40;</span><span style="color: #3366CC;">&quot;ws://localhost:9000&quot;</span><span style="color: #339933;">,</span> <span style="color: #003366; font-weight: bold;">function</span><span style="color: #009900;">&#40;</span>session<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
   <span style="color: #003366; font-weight: bold;">function</span> une_fonction<span style="color: #009900;">&#40;</span>a<span style="color: #339933;">,</span> b<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
      <span style="color: #000066; font-weight: bold;">return</span> a <span style="color: #339933;">+</span> b<span style="color: #339933;">;</span>
   <span style="color: #009900;">&#125;</span>
   <span style="color: #006600; font-style: italic;">// on déclare que cette fonction est appelable à distance</span>
   session.<span style="color: #660066;">register</span><span style="color: #009900;">&#40;</span><span style="color: #3366CC;">'une_fonction'</span><span style="color: #339933;">,</span> une_fonction<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
<span style="color: #009900;">&#125;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span></pre></div></div>

<div id="attachment_10347" class="wp-caption aligncenter" style="width: 574px"><a href="media/48127c83.register1.png" data-original-source="http://sametmax.com/wp-content/uploads/2014/05/register1.png"><img src="media/a7786312.register.png" data-original-source="http://sametmax.com/wp-content/uploads/2014/05/register.png" alt="Schéma expliquant register avec WAMP" title="RPC marche à l&#039;envers de PUB/SUB. Un client expose du code, et un autre demande explicitement qu&#039;il soit exécuté." width="564" height="469" class="size-full wp-image-10347" /></a><p class="wp-caption-text">RPC marche à l&#039;envers de PUB/SUB. Un client expose du code, et un autre demande explicitement qu&#039;il soit exécuté.</p></div>
<p>Côté appelant :</p>

<div class="wp_syntax"><div class="code"><pre class="javascript" style="font-family:monospace;">ab.<span style="color: #660066;">connect</span><span style="color: #009900;">&#40;</span><span style="color: #3366CC;">&quot;ws://localhost:9000&quot;</span><span style="color: #339933;">,</span> <span style="color: #003366; font-weight: bold;">function</span><span style="color: #009900;">&#40;</span>session<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
    <span style="color: #006600; font-style: italic;">// on appelle la fonction à distance, on récupère une</span>
    <span style="color: #006600; font-style: italic;">// promise qui nous permet de travailler sur le résultat</span>
   session.<span style="color: #660066;">call</span><span style="color: #009900;">&#40;</span><span style="color: #3366CC;">'une_fonction'</span><span style="color: #339933;">,</span> <span style="color: #CC0000;">2</span><span style="color: #339933;">,</span> <span style="color: #CC0000;">3</span><span style="color: #009900;">&#41;</span>.<span style="color: #660066;">then</span><span style="color: #009900;">&#40;</span>
      <span style="color: #003366; font-weight: bold;">function</span> <span style="color: #009900;">&#40;</span>res<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
         console.<span style="color: #660066;">log</span><span style="color: #009900;">&#40;</span>res<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
      <span style="color: #009900;">&#125;</span>
   <span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span></pre></div></div>

<div id="attachment_10348" class="wp-caption aligncenter" style="width: 552px"><a href="media/c8ffda1b.call.png" data-original-source="http://sametmax.com/wp-content/uploads/2014/05/call.png"><img src="media/c8ffda1b.call.png" data-original-source="http://sametmax.com/wp-content/uploads/2014/05/call.png" alt="Schéma expliquant CALL en WAMP" title="Contrairement à PUB/SUB, RPC ne concerne que deux clients à la fois. Mais ça reste asynchrone. Le client demandeur n&#039;attend pas le résultat de l&#039;appel de la fonction. Il est signalé par le serveur quand il est prêt." width="542" height="548" class="size-full wp-image-10348" /></a><p class="wp-caption-text">Contrairement à PUB/SUB, RPC ne concerne que deux clients à la fois. Mais ça reste asynchrone. Le client demandeur n&#039;attend pas le résultat de l&#039;appel de la fonction. Il est signalé par le serveur quand il est prêt.</p></div>
<p>Pareil que pour le PUB/SUB, les gens ont du mal à voir l&#8217;utilité à cause du trop de flexibilité que ça apporte. Imaginez que votre projet soit maintenant éclaté en de nombreux petits services qui tournent et qui sont indépendants :</p>
<ul>
<li>Un service pour le site Web.</li>
<li>Un service d&#8217;authentification.</li>
<li>Un service pour l&#8217;API.</li>
<li>Un service pour les tâches longues.</li>
<li>Un service de monitoring et administration technique.</li>
</ul>
<p>Tous ces services peuvent ainsi communiquer entre eux via RPC, mais n&#8217;ont pas besoin d&#8217;être dans le même processus. On peut profiter pleinement de tous les cœurs de sa machine, on peut même les mettre sur des serveurs séparés.</p>
<p>Mieux, avoir un service bloquant ne pénalise pas tout le système. En effet, un problème avec les systèmes asynchrones en Python est que beaucoup de libs sont encore bloquantes (typiquement les ORMs). <strong>Avec ce genre d&#8217;architecture, on peut créer un service qui ne fait que les appels bloquant et laisser les autres services non bloquant l&#8217;appeler de manière asynchrone.</strong> Pendant qu&#8217;il bloque, le reste du système peut traiter d&#8217;autres requêtes.</p>
<h2>Crossbar, plus qu&#8217;un routeur WAMP</h2>
<p>L&#8217;idée des concepteurs de crossbar est de permettre de créer des systèmes avec des services composables qui communiquent entre eux plutôt que tout dans un gros processus central. Ils ne se sont donc pas arrêtés au routing.</p>
<p>Crossar est également un gestionnaire de processus, comme <a href="https://pypi.python.org/pypi/supervisor/3.0">supervisor</a> ou, plus légitimement, <a href="https://pypi.python.org/pypi/circus/0.11.1">circus</a> (Tarek, fait une pause, vient ici !) et sa communication ZeroMQ.</p>
<p>Il se configure avec un simple fichier JSON, et on peut y définir des classes Python qui seront lancées dans un processus séparé et pourront discuter avec les autres clients via WAMP :</p>

<div class="wp_syntax"><div class="code"><pre class="javascript" style="font-family:monospace;"><span style="color: #009900;">&#123;</span>
   <span style="color: #3366CC;">&quot;processes&quot;</span><span style="color: #339933;">:</span> <span style="color: #009900;">&#91;</span>
      <span style="color: #009900;">&#123;</span> <span style="color: #006600; font-style: italic;">// premier processus</span>
         <span style="color: #3366CC;">&quot;type&quot;</span><span style="color: #339933;">:</span> <span style="color: #3366CC;">&quot;worker&quot;</span><span style="color: #339933;">,</span>
         <span style="color: #3366CC;">&quot;modules&quot;</span><span style="color: #339933;">:</span> <span style="color: #009900;">&#91;</span>
            <span style="color: #009900;">&#123;</span>
               un_worker.<span style="color: #660066;">Classe</span>
            <span style="color: #009900;">&#125;</span><span style="color: #339933;">,</span>
            <span style="color: #009900;">&#123;</span>
               un_autre_worker.<span style="color: #660066;">Classe</span>
            <span style="color: #009900;">&#125;</span>
         <span style="color: #009900;">&#93;</span>
      <span style="color: #009900;">&#125;</span><span style="color: #339933;">,</span>
      <span style="color: #009900;">&#123;</span>  <span style="color: #006600; font-style: italic;">// second processus</span>
         <span style="color: #3366CC;">&quot;type&quot;</span><span style="color: #339933;">:</span> <span style="color: #3366CC;">&quot;worker&quot;</span><span style="color: #339933;">,</span>
         <span style="color: #3366CC;">&quot;modules&quot;</span><span style="color: #339933;">:</span> <span style="color: #009900;">&#91;</span>
            <span style="color: #009900;">&#123;</span>
               un_autre_worker_dans_un_autre_process.<span style="color: #660066;">Classe</span>
            <span style="color: #009900;">&#125;</span>
         <span style="color: #009900;">&#93;</span>
      <span style="color: #009900;">&#125;</span>
   <span style="color: #009900;">&#93;</span>
<span style="color: #009900;">&#125;</span></pre></div></div>

<p>Mais si ça ne suffit pas, on peut également lancer des programmes extérieurs non Python dont crossbar va gérer le cycle de vie :</p>

<div class="wp_syntax"><div class="code"><pre class="javascript" style="font-family:monospace;"><span style="color: #009900;">&#123;</span>
   <span style="color: #3366CC;">&quot;processes&quot;</span><span style="color: #339933;">:</span> <span style="color: #009900;">&#91;</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;guest&quot;</span><span style="color: #339933;">,</span>
         <span style="color: #3366CC;">&quot;executable&quot;</span><span style="color: #339933;">:</span> <span style="color: #3366CC;">&quot;/usr/bin/node&quot;</span><span style="color: #339933;">,</span>
         <span style="color: #3366CC;">&quot;arguments&quot;</span><span style="color: #339933;">:</span> <span style="color: #009900;">&#91;</span><span style="color: #3366CC;">&quot;votre_script.js&quot;</span><span style="color: #009900;">&#93;</span><span style="color: #339933;">,</span>
         <span style="color: #3366CC;">&quot;stdout&quot;</span><span style="color: #339933;">:</span> <span style="color: #3366CC;">&quot;log&quot;</span>
      <span style="color: #009900;">&#125;</span>
   <span style="color: #009900;">&#93;</span>
<span style="color: #009900;">&#125;</span></pre></div></div>

<p>Vous avez donc ainsi les deux atouts pour avoir <strong>une architecture découplée, scalable, exploitant plusieurs cœurs, et compensant en partie les bibliothèques blocantes</strong> :</p>
<ul>
<li>Un protocole flexible, simple, qui permet à tout le monde se parler entre eux (WAMP).</li>
<li>Une API qui permet soit de réagir à un changement (PUB/SUB), soit de demander une action (RPC).</li>
<li>Un programme qui gère cette communication, et le cycle de vie des composants qui parlent entre eux.</li>
</ul>
<h2>Cas concret</h2>
<p>WAMP est typiquement le genre de techno qui ne permet PAS de faire quelque chose qu&#8217;on ne faisait pas avant. Ce n&#8217;est pas nouveau.</p>
<p>En revanche, WAMP permet de le faire mieux et plus facilement.</p>
<p>Prenez le cas d&#8217;un utilisateur qui se connecte sur un forum. Il va sur un formulaire, il post ses identifiants, ça recharche la page, il est connecté. Si les autres utilisateurs rechargent leurs pages, ils verront un utilisateur de plus connecté.</p>
<p>Si on veut rendre ça plus dynamique, il faut utiliser de l&#8217;AJAX, et si on veut avoir une mise à jour presque en temps réel, il faut faire des requêtes Ajax régulière. Ce qui est assez bancal et demande beaucoup de travail manuel.</p>
<p>Certains sites modernes utilisent Websocket, et des serveurs asyncrones comme NodeJS, et un routeur PUB/SUB comme redis, pour faire cela de manière rapide et plus facile. L&#8217;application est très réactive. Mais le système est hétéroclite. Et si on veut envoyer des messages entre des composants serveurs, ça demande encore quelque chose de différent.</p>
<p>WAMP unifie tout ça. Un coup de RPC pour le login pour effectuer l&#8217;action:</p>
<div id="attachment_10351" class="wp-caption aligncenter" style="width: 552px"><a href="media/3ff3af7b.rpc_concret.png" data-original-source="http://sametmax.com/wp-content/uploads/2014/05/rpc_concret.png"><img src="media/3ff3af7b.rpc_concret.png" data-original-source="http://sametmax.com/wp-content/uploads/2014/05/rpc_concret.png" alt="Schéma d&#039;un exemple concret de RPC WAMP" title="Notez que le RPC marche de n&#039;importe quel client à n&#039;importe quel client. Il n&#039;y a pas de sens obligatoire. Le login est un exemple simple mais on peut faire des choses bien plus complexes." width="542" height="592" class="size-full wp-image-10351" /></a><p class="wp-caption-text">Notez que le RPC marche de n&#039;importe quel client à n&#039;importe quel client. Il n&#039;y a pas de sens obligatoire. Le login est un exemple simple mais on peut faire des choses bien plus complexes.</p></div>
<p>Et un coup de PUB/SUB pour prévenir tout le monde que quelque chose s&#8217;est passé :</p>
<div id="attachment_10352" class="wp-caption aligncenter" style="width: 549px"><a href="media/a4ce678d.pub_sub_concret.png" data-original-source="http://sametmax.com/wp-content/uploads/2014/05/pub_sub_concret.png"><img src="media/a4ce678d.pub_sub_concret.png" data-original-source="http://sametmax.com/wp-content/uploads/2014/05/pub_sub_concret.png" alt="Schéma d&#039;exemple concret de PUB/SUB avec WAMP" title="Je n&#039;ai mis que des clients légers ici, mais je vous rappelle qu&#039;un client peut être un serveur NodeJS, une base de données, un code C++..." width="539" height="626" class="size-full wp-image-10352" /></a><p class="wp-caption-text">Je n&#039;ai mis que des clients légers ici, mais je vous rappelle qu&#039;un client peut être un serveur NodeJS, une base de données, un code C++...</p></div>
<p>Bien entendu, on pourrait faire ça avec les technos existantes. C&#8217;est juste moins pratique.</p>
<p>Notez également que Crossbar encourage à avoir un service qui ne se charge que du login, sans avoir à faire une usine à gaz pour cela. Si demain votre service de login a besoin d&#8217;être sur un coeur/serveur/une VM séparé pour des raisons de perfs ou de sécurité, c&#8217;est possible. Crossbar encourage ce genre de design.</p>
<h2>Voici où est le piège</h2>
<p>Car évidement, il y en a toujours un, putain de métier à la con.</p>
<p><strong>Et c&#8217;est la jeunesse du projet.</strong></p>
<p>Le projet est stable, le code marche, et les sources sont propres.</p>
<p>Mais la doc, mon dieu la doc&#8230; Les exemples sont pas à jour, il y a deux versions qui se battent en duel, on sait pas trop quelle partie sert à quoi.</p>
<p>Et comme tout projet jeune, l&#8217;API n&#8217;a pas été assez étudiée. Or la partie Python est basée sur Twisted, sans polish. Twisted, c&#8217;est puissant, c&#8217;est solide, et c&#8217;est aussi une API dégueulasse.</p>
<p>Un exemple ? Comment écouter un événement :</p>

<div class="wp_syntax"><div class="code"><pre class="python" style="font-family:monospace;"><span style="color: #808080; font-style: italic;"># Des imports légers</span>
<span style="color: #ff7700;font-weight:bold;">from</span> twisted.<span style="color: black;">python</span> <span style="color: #ff7700;font-weight:bold;">import</span> log
<span style="color: #ff7700;font-weight:bold;">from</span> twisted.<span style="color: black;">internet</span>.<span style="color: black;">defer</span> <span style="color: #ff7700;font-weight:bold;">import</span> inlineCallbacks
&nbsp;
<span style="color: #ff7700;font-weight:bold;">from</span> autobahn.<span style="color: black;">twisted</span>.<span style="color: black;">wamp</span> <span style="color: #ff7700;font-weight:bold;">import</span> ApplicationSession
<span style="color: #ff7700;font-weight:bold;">from</span> autobahn.<span style="color: black;">twisted</span>.<span style="color: black;">wamp</span> <span style="color: #ff7700;font-weight:bold;">import</span> ApplicationRunner
&nbsp;
<span style="color: #808080; font-style: italic;"># Une bonne classe bien subtile pour copier Java</span>
<span style="color: #ff7700;font-weight:bold;">class</span> ListenForEvent<span style="color: black;">&#40;</span>ApplicationSession<span style="color: black;">&#41;</span>:
&nbsp;
    <span style="color: #808080; font-style: italic;"># Deux méthodes de boiler plate obligatoires</span>
    <span style="color: #808080; font-style: italic;"># et parfaitement soulantes pour l'utilisateur</span>
    <span style="color: #808080; font-style: italic;"># final. Cachez moi ça bordel !</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>, config<span style="color: black;">&#41;</span>:
        ApplicationSession.<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;">self</span>.<span style="color: black;">config</span> = config
&nbsp;
    <span style="color: #ff7700;font-weight:bold;">def</span> onConnect<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;">join</span><span style="color: black;">&#40;</span><span style="color: #008000;">self</span>.<span style="color: black;">config</span>.<span style="color: black;">realm</span><span style="color: black;">&#41;</span>
&nbsp;
    <span style="color: #808080; font-style: italic;"># Bon, on se décide, soit on fait une classe avec des noms</span>
    <span style="color: #808080; font-style: italic;"># de méthode conventionnés, soit on met des décorateurs, </span>
    <span style="color: #808080; font-style: italic;"># mais pas les deux, pitié !</span>
    @inlineCallbacks
    <span style="color: #ff7700;font-weight:bold;">def</span> onJoin<span style="color: black;">&#40;</span><span style="color: #008000;">self</span>, details<span style="color: black;">&#41;</span>:
        callback = <span style="color: #ff7700;font-weight:bold;">lambda</span> x: log.<span style="color: black;">msg</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">&quot;Received event %s&quot;</span> <span style="color: #66cc66;">%</span> x<span style="color: black;">&#41;</span>
        <span style="color: #ff7700;font-weight:bold;">yield</span> <span style="color: #008000;">self</span>.<span style="color: black;">subscribe</span><span style="color: black;">&#40;</span>callback, <span style="color: #483d8b;">'un_evenement'</span><span style="color: black;">&#41;</span>
&nbsp;
<span style="color: #808080; font-style: italic;"># Python doit lancer explicitement un event loop.</span>
<span style="color: #808080; font-style: italic;"># Ca pourrait (devrait) aussi être embeded dans une</span>
<span style="color: #808080; font-style: italic;"># sousclasse de ApplicationSession.</span>
<span style="color: #808080; font-style: italic;"># /me prend un colt. BANG !</span>
<span style="color: #ff7700;font-weight:bold;">if</span> __name__ == <span style="color: #483d8b;">'__main__'</span>:
   runner = ApplicationRunner<span style="color: black;">&#40;</span>endpoint=<span style="color: #483d8b;">&quot;tcp:127.0.0.1:8080&quot;</span>,
                              url=<span style="color: #483d8b;">&quot;ws://localhost:8080/ws&quot;</span>,
                              realm=<span style="color: #483d8b;">&quot;realm1&quot;</span><span style="color: black;">&#41;</span>
   runner.<span style="color: black;">run</span><span style="color: black;">&#40;</span>ListenForEvent<span style="color: black;">&#41;</span></pre></div></div>

<p>C&#8217;est la raison pour laquelle je vous ai montré le code JS et pas Python pour vous vendre le truc. Sur sametmax.com, on aura tout vu :(</p>
<p>Voilà à quoi devrait ressembler le code Python si l&#8217;API était mature :</p>

<div class="wp_syntax"><div class="code"><pre class="python" style="font-family:monospace;"><span style="color: #ff7700;font-weight:bold;">from</span> autobahn.<span style="color: black;">app</span> <span style="color: #ff7700;font-weight:bold;">import</span> App
&nbsp;
app = App<span style="color: black;">&#40;</span>url=<span style="color: #483d8b;">&quot;ws://localhost:8080/ws&quot;</span><span style="color: black;">&#41;</span>
&nbsp;
@event<span style="color: black;">&#40;</span><span style="color: #483d8b;">&quot;un_evenement&quot;</span><span style="color: black;">&#41;</span>
<span style="color: #ff7700;font-weight:bold;">def</span> handle<span style="color: black;">&#40;</span>details<span style="color: black;">&#41;</span>:
    app.<span style="color: black;">log</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">&quot;Received event %s&quot;</span> <span style="color: #66cc66;">%</span> x<span style="color: black;">&#41;</span>
&nbsp;
<span style="color: #ff7700;font-weight:bold;">if</span> __name__ == <span style="color: #483d8b;">'__main__'</span>:
   app.<span style="color: black;">run</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span></pre></div></div>

<p>Chose que je vais proposer sur <a href="https://groups.google.com/forum/#!forum/autobahnws">la mailing list</a> (ils sont réactifs et sympas, vive les allemands !) dans pas longtemps. Et si ils n&#8217;ont pas le temps de le faire, il est possible que je m&#8217;y colle. Ca me fait mal aux yeux.</p>
 <p><a href="http://sametmax.com/?flattrss_redirect&amp;id=10329&amp;md5=2b5a9171d8db1d78ae97c44b719b748b" title="Flattr" target="_blank"><img src="media/fc229d00.flattr-badge-large.png" data-original-source="http://sametmax.com/wp-content/plugins/flattr/img/flattr-badge-large.png" alt="flattr this!"/></a></p>


feed_id: http://sametmax.com/?p=10318
title: Les annonces hypes
uri: Les-annonces-hypes
url: http://sametmax.com/les-annonces-hypes/
content:
<blockquote><p>Nous sommes une start up innovante</p></blockquote>
<p>on a pas de business model</p>
<blockquote><p>à fort potentiel</p></blockquote>
<p>c&#8217;est ma première boîte et ma mère me dit que j&#8217;ai toutes mes chances</p>
<blockquote><p>travaillant à la pointe de la pointe de la technologie</p></blockquote>
<p>mon dernier dev a créé un projet NodeJS sans documentation ni commentaire et s&#8217;est barré</p>
<blockquote><p>en utilisant des méthodes agiles.</p></blockquote>
<p>on a pas de cahier des charges ni de formation à l&#8217;embauche</p>
<blockquote><p>Nous somme à la recherche d&#8217;un dev ninja passionné et autonome</p></blockquote>
<p>on veut un mec qui travaille 80 heures / semaine pas cher à payer</p>
<blockquote><p>capable de mener le projet de bout en bout.</p></blockquote>
<p>je ne sais pas parler au client, coder ou même conduire un projet. Je ne sais rien faire. Je ne sert à rien. J&#8217;ai juste eu une idée un soir d&#8217;apéro. A l&#8217;aide.</p>
<blockquote><p>Vous serez en mesure de travailler avec Linux, HTML5, Postgres, Flask, NodeJS, Coffeescript, MongoDB, Mémécash, AmazonS3, Notepad++, duckduckgo et VLC.</p></blockquote>
<p>Je n&#8217;ai aucune idée de quoi je parle, alors j&#8217;ai mis tout ce que je comprends pas sur la même ligne.</p>
<blockquote><p>Télétravail possible.</p></blockquote>
<p>Je ne veux pas parler de la rémunération alors je finis là dessus. Ça sera surement une part des bénéfices potentiels assurés mirobolants. Pas une trop grosse part, c&#8217;est quand même moi qui a eu l&#8217;idée.</p>
<p>Merde c&#8217;est quoi le mail de ma boîte déjà ? Tant pis je met l&#8217;adresse hotmail.</p>
<p><br/><br />
<br/></p>
<p>Ils prennent whatsapp ? Nan vaut mieux pas, faudrait pas que je passe pour un con.</p>
<p><br/><br />
<br/></p>
<p>Je vais rajouter mon num&#8230; MERDEEEEEEE. J&#8217;ai envoyé le formulaire par erreur.</p>
<p><br/><br />
<br/><br />
<br/><br />
<br/></p>
<p>Ah, c&#8217;est pas le bon site en fait.</p>
<p><br/></p>
 <p><a href="http://sametmax.com/?flattrss_redirect&amp;id=10318&amp;md5=54807f563777b509247d60986e8ded44" title="Flattr" target="_blank"><img src="media/fc229d00.flattr-badge-large.png" data-original-source="http://sametmax.com/wp-content/plugins/flattr/img/flattr-badge-large.png" alt="flattr this!"/></a></p>


feed_id: http://sametmax.com/?p=10304
title: Le Web n’est plus HTTP + HTML
uri: Le-Web-n-est-plus-HTTP-HTML
url: http://sametmax.com/le-web-nest-plus-http/
content:
<p>Si vous voulez énerver un blogger technophile, utilisez le mot Web là où vous devriez utiliser le mot <a href="http://sebsauvage.net/~sebsauva/comprendre/internet/index.html">Internet</a> et inversement.</p>
<p>Internet, c&#8217;est beaucoup plus que le Web. C&#8217;est SSH, IMAP, TELNET, DNS, POP, SMTP, FTP, RTSP, NNTP, Bittorent, TOR, Freenet, Bitcoin, et quelques centaines d&#8217;autres protocoles qui se parlent.</p>
<p>Jusqu&#8217;ici, le Web, c&#8217;était juste HTTP. Des ressources Web, sur lesquelles on agissait via une requête textuelle verbalisée (GET, POST, PUT, OPTION, HEAD, etc) et qui retournait une réponse, généralement en forme de HTML.</p>
<p>Ça a un peu évolué, on a eu SSL qui s&#8217;est rajouté, et donc HTTPS, et AJAX, qui n&#8217;a pas changé le protocole, mais rendu la nature des requêtes un peu différente. Rien qui n&#8217;empêche de debugger avec CURL.</p>
<p>Mais c&#8217;est bientôt fini tout ça.</p>
<p>Aujourd&#8217;hui les nouveaux protocoles utilisés dans le cadre du Web sont en passe de prendre le pouvoir. Bien sûr il y a  <a href="https://en.wikipedia.org/wiki/SPDY">SPDY</a> et <a href="http://en.wikipedia.org/wiki/QUIC">QUIC</a>, mais surtout, il a les protocoles basés sur les websockets type <a href="http://wamp.ws/">WAMP.ws</a>, mais également les nouvelles capacités P2P promises par <a href="http://en.wikipedia.org/wiki/WebRTC">WebRTC</a>. Et des apps qui utilisent massivement les données hors ligne, le scripting JS pour des features essentielles, de la video, du son&#8230;</p>
<p>Et donc adios, l&#8217;époque où vous pouviez juste dégainer <a href="http://docs.python-requests.org/">requests</a> et parler à un site. Bye bye le state less, le human readable, le cycle requête / réponse.</p>
<p>Le nombre de technologies qu&#8217;on doit empiler aujourd&#8217;hui pour déployer un site devient énorme : un moteur de recherche, un message broker, un gestionnaire de fil d&#8217;attente, un gestionnaire de déploiement, des technos d&#8217;isolation&#8230;</p>
<p>C&#8217;est fini la simplicité. C&#8217;est fini la transparence. L&#8217;ère du hacker amateur qui pouvait s&#8217;occuper d&#8217;un peu de tout, touche doucement à sa fin.</p>
<p>Au revoir et merci. Je me suis super amusé.</p>
<p>Et désolé pour les mômes qui arrivent maintenant, vous allez en chier. Mais vous avez aussi plus de possibilités qu&#8217;il n&#8217;y en a jamais eu. Plus qu&#8217;un homme ne peut concevoir. Plus que tous les hommes en fait.</p>
<p>Et RIP HTTP. Ou pas, puisqu&#8217;on passe notre temps à faire des APIs REST maintenant, mais aussi car on est en train de récréer un peu tout au dessus d&#8217;HTTP. Long live HTTP, alors, le nouveau TCP/IP. Sauf quand on fait du real time. Ou du P2P. Changement de status : &#8220;c&#8217;est compliqué entre moi et mon navigateur&#8221;.</p>
<p>Internet, phagocyté par le Web, sur lequel on reconstruit Internet et même le desktop ?</p>
<p>Je ne crois pas qu&#8217;il existe un seul métier qui ait autant changé en 10 ans. J&#8217;espère qu&#8217;on en laisse pas trop derrière en courant comme des fous en riant les yeux mi-clos. Pourvu qu&#8217;il y ait pas trop d&#8217;arbres en face. Pourvu qu&#8217;on aille pas dans la direction de la falaise.</p>
<p>En tout cas, c&#8217;est toujours fun. Je crois que je vais descendre la prochaine pente en roulant sur le côté. Et avoir la tête qui tourne. Vomir. Et dire que c&#8217;est la faute de Javascript.</p>
<p>Et recommencer.</p>
 <p><a href="http://sametmax.com/?flattrss_redirect&amp;id=10304&amp;md5=13de9b9bf0222d6b72dd32142bca12d2" title="Flattr" target="_blank"><img src="media/fc229d00.flattr-badge-large.png" data-original-source="http://sametmax.com/wp-content/plugins/flattr/img/flattr-badge-large.png" alt="flattr this!"/></a></p>


feed_id: http://sametmax.com/?p=10298
title: Rejouer un session de terminal avec playitagainsam
uri: Rejouer-un-session-de-terminal-avec-playitagainsam
url: http://sametmax.com/rejouer-un-session-de-terminal-avec-playitagainsam/
content:
<p>Combien de fois, durant une formation, on m&#8217;a demandé si je pouvais donner un dump de la session shell.</p>
<p><a href="https://github.com/rfk/playitagainsam">playitagainsam</a>, ou PIAS, a exactement pour but de faire cela: il lance un shell, enregistre ce qu&#8217;on y a tapé, et quand on quitte le shell, sauvegarde le tout dans un fichier. Ensuite, on peut lire le contenu du fichier et revoir la session shell telle qu&#8217;elle a été tapée.</p>
<p>C&#8217;est un programme Python, on peut donc le <a href="http://sametmax.com/votre-python-aime-les-pip/">pip</a> installer :</p>

<div class="wp_syntax"><div class="code"><pre class="bash" style="font-family:monospace;">pip <span style="color: #c20cb9; font-weight: bold;">install</span> playitagainsam <span style="color: #660033;">--user</span></pre></div></div>

<p>Ensuite, on lance une session à enregistrer avec la sous commande record :</p>

<div class="wp_syntax"><div class="code"><pre class="bash" style="font-family:monospace;">pias record nom_du_fichier_ou_sauver_l_enregistrement</pre></div></div>

<p>PIAS va lancer un nouveau shell, par défaut le même que votre shell actuel (souvent bash, pour moi fish), mais vous pouvez préciser un type de shell spécial avec <code>--shell</code>. Ex :</p>

<div class="wp_syntax"><div class="code"><pre class="bash" style="font-family:monospace;">pias record ex1 <span style="color: #660033;">--shell</span> ipython</pre></div></div>

<p>A partir de là il n&#8217;y a rien à faire. On peut travailler normalement et oublier qu&#8217;on a utilisé PIAS. Une fois qu&#8217;on a terminé sa petite affaire, il suffit de sortir du shell (CTRL + D, exit, etc), et la session va être sauvegardée dans un format JSON facile à corriger à la main si vous souhaitez la rendre plus agréable :</p>

<div class="wp_syntax"><div class="code"><pre class="bash" style="font-family:monospace;"><span style="color: #7a0874; font-weight: bold;">&#123;</span>
  <span style="color: #ff0000;">&quot;events&quot;</span>: <span style="color: #7a0874; font-weight: bold;">&#91;</span>
    <span style="color: #7a0874; font-weight: bold;">&#123;</span>
      <span style="color: #ff0000;">&quot;act&quot;</span>: <span style="color: #ff0000;">&quot;OPEN&quot;</span>,
      <span style="color: #ff0000;">&quot;size&quot;</span>: <span style="color: #7a0874; font-weight: bold;">&#91;</span>
        <span style="color: #000000;">162</span>,
        <span style="color: #000000;">33</span>
      <span style="color: #7a0874; font-weight: bold;">&#93;</span>,
      <span style="color: #ff0000;">&quot;term&quot;</span>: <span style="color: #ff0000;">&quot;1c53f048e3a74984b2a93af175e24e87&quot;</span>
    <span style="color: #7a0874; font-weight: bold;">&#125;</span>,
    <span style="color: #7a0874; font-weight: bold;">&#123;</span>
      <span style="color: #ff0000;">&quot;act&quot;</span>: <span style="color: #ff0000;">&quot;PAUSE&quot;</span>,
      <span style="color: #ff0000;">&quot;duration&quot;</span>: <span style="color: #000000;">0.10042214393615723</span>
    <span style="color: #7a0874; font-weight: bold;">&#125;</span>,
    <span style="color: #7a0874; font-weight: bold;">&#123;</span>
      <span style="color: #ff0000;">&quot;act&quot;</span>: <span style="color: #ff0000;">&quot;WRITE&quot;</span>,
      <span style="color: #ff0000;">&quot;data&quot;</span>: <span style="color: #ff0000;">&quot;\u001b]0;sam@sametmax.com: /tmp\u0007sam@sametmax.com:/tmp$ &quot;</span>,
      <span style="color: #ff0000;">&quot;term&quot;</span>: <span style="color: #ff0000;">&quot;1c53f048e3a74984b2a93af175e24e87&quot;</span>
    <span style="color: #7a0874; font-weight: bold;">&#125;</span>,
    <span style="color: #7a0874; font-weight: bold;">&#123;</span>
      <span style="color: #ff0000;">&quot;act&quot;</span>: <span style="color: #ff0000;">&quot;ECHO&quot;</span>,
      <span style="color: #ff0000;">&quot;data&quot;</span>: <span style="color: #ff0000;">&quot;touch essai&quot;</span>,
      <span style="color: #ff0000;">&quot;term&quot;</span>: <span style="color: #ff0000;">&quot;1c53f048e3a74984b2a93af175e24e87&quot;</span>
...
<span style="color: #7a0874; font-weight: bold;">&#125;</span></pre></div></div>

<p>Quand vous voulez rejouer la session :</p>

<div class="wp_syntax"><div class="code"><pre class="bash" style="font-family:monospace;">pias play fichier_d_enregistrement</pre></div></div>

<p>Par défaut il faut appuyer sur une touche quelconque du clavier pour voir apparaitre chaque caractère, donnant l&#8217;impression que vous êtes un hacker de Hollywood. C&#8217;est rigolo, mais parfaitement chiant à la longue. On peut faire jouer la session automatiquement avec l&#8217;option <code>--auto-type</code> qui va taper chaque ligne automatiquement. Il suffira alors d&#8217;appuyer sur entrée à chaque ligne pour avoir le résultat suivant. C&#8217;est mon mode préféré. </p>
<p>Si vous êtes un gros feignant, vous pouvez tout jouer en automatique en rajoutant l&#8217;option <code>--auto-waypoint</code> qui va vous dispenser de taper entrée à chaque fin de ligne. Ex :</p>

<div class="wp_syntax"><div class="code"><pre class="bash" style="font-family:monospace;">pias play ex1 <span style="color: #660033;">--auto-type</span> <span style="color: #660033;">--auto-waypoint</span></pre></div></div>

<p><strong>La session jouée est simulée.</strong> Elle n&#8217;a aucun impacte sur le système. Si vous faites un <code>mkdir</code> dans bash ou un <code>shutil.rmtree</code> dans python, ça ne fera rien.</p>
<p>Néanmoins, parfois il est utile d&#8217;avoir le effets réels des commandes. Dans ce cas on peut utiliser <code>--live-replay</code>. Attention, faites bien gaffe à ce que vous allez jouer&#8230;</p>
<p>playitagainsam est intéressant pour tout ce qui est formation et tutorial, d&#8217;autant plus qu&#8217;il existe un <a href="https://github.com/rfk/playitagainsam-js/">player javascript</a> pour faire la démonstration de vos sessions shell sur un blog ou un slideshow.</p>
 <p><a href="http://sametmax.com/?flattrss_redirect&amp;id=10298&amp;md5=f8ad40489789854c2a7e1de921a8839a" title="Flattr" target="_blank"><img src="media/fc229d00.flattr-badge-large.png" data-original-source="http://sametmax.com/wp-content/plugins/flattr/img/flattr-badge-large.png" alt="flattr this!"/></a></p>


feed_id: http://sametmax.com/?p=10288
title: Qu’est-ce que la bibliothèque standard Python ?
uri: Qu-est-ce-que-la-bibliothèque-standard-Python
url: http://sametmax.com/quest-ce-que-la-bibliotheque-standard-python/
content:
<p>La lib standard ou stdlib, on vous en parle partout. Ce tuto vous explique comment faire telle tâche avec la lib standard. Ce README dit qu&#8217;il n&#8217;y a que la lib standard comme dépendance, c&#8217;est génial ! Cet article voudrait que le code X soit intégré dans la lib standard.</p>
<p>Mais au fait, c&#8217;est quoi la lib standard ?</p>
<p>Et bien c&#8217;est tout simplement l&#8217;ensemble du code qui est disponible si vous avez Python installé sur votre machine. Si un module fait partie de la bibliothèque standard, vous êtes certain que votre programme Python peut toujours l&#8217;utiliser : il n&#8217;y a rien à installer.</p>
<p>Cette lib standard est également intégralement <a href="https://docs.python.org/3/">documentée</a> sur le site de Python, si bien que si vous <a href="https://docs.python.org/3/download.html">téléchargez</a> la doc hors ligne, vous pouvez quasiment dev en totale autonomie. En effet, même si le principe de la lib standard est le même pour tous les langages, en Python, elle est particulièrement intéressante car vraiment <a href="http://sametmax.com/tres-grand-listing-des-libs-standard-partie-les-plus-utiles-en-python/">énorme</a>. Et en plus vous avez <a href="https://github.com/sametmax/miroir-du-blog">le dump de notre blog</a> :)</p>
<p>Le code de la lib standard évolue lentement, contrairement aux libs tierces parties, car elles sont soumises à des règles strictes et la revue de la communauté à travers des documents officiels : <a href="http://legacy.python.org/dev/peps/">les PEP</a>. Le PEP le plus célèbre étant le <a href="http://legacy.python.org/dev/peps/pep-0008/">PEP 8</a>, qui décrit le style recommandé pour du code Python. </p>
<p>De fait le code de la lib standard est un code fiable, sans surprise, mais qui proposera rarement les dernières innovations. Pour reprendre <a href="https://mail.python.org/pipermail/python-dev/2010-February/097865.html">les mots de Guido</a> :</p>
<blockquote><p>Le code de la stdlib a un pied dans la tombe.</p></blockquote>
<p>Car Python garanti la compatibilité ascendante de TOUTE la lib standard pour une version majeure. Ce qui veut dire que si vous utilisez un module de la stdlib écrit pour Python 2.5, il marchera en Python 2.7, et un module pour 3.1 marchera pour la 3.4.</p>
<p>Mais ça veut dire aussi que le module pourrira ici quand plus personne ne l&#8217;utilisera. La preuve en est <a href="https://docs.python.org/2/library/getopt.html">getopt</a>, qui a été remplacé par <a href="https://docs.python.org/2/library/optparse.html">optparse</a>, qui a été remplacé par <a href="https://docs.python.org/dev/library/argparse.html">argparse</a>. Deux générations de libs plus tard, getopt est toujours importable en 2.7, bien que plus personne ne s&#8217;en serve, pour garantir cette compatibilité. Et en fait, on peut dire 5 générations, car <a href="http://sametmax.com/la-plus-belle-maniere-de-parser-les-arguments-de-script-en-python/">nous-même utilisons docopt après prôné clize</a>. Le monde de l&#8217;IT est comique.</p>
<p>Certains systèmes ébrèchent le principe de la lib standard, comme Ubuntu, qui package la lib TK séparément, et donc oblige à installer le paquet <code>python-tk</code> pour avoir le module <a href="https://docs.python.org/3/library/tkinter.html">tkinter</a>, normalement inclus dans la lib standard. Ce comportement est heureusement assez rare.</p>
 <p><a href="http://sametmax.com/?flattrss_redirect&amp;id=10288&amp;md5=e2cd8b8cc19da7088cef06833ad7a43d" title="Flattr" target="_blank"><img src="media/fc229d00.flattr-badge-large.png" data-original-source="http://sametmax.com/wp-content/plugins/flattr/img/flattr-badge-large.png" alt="flattr this!"/></a></p>


feed_id: http://sametmax.com/?p=10270
title: Le déploiement par conteneurs avec Docker
uri: Le-déploiement-par-conteneurs-avec-Docker
url: http://sametmax.com/le-deploiement-par-conteneurs-avec-docker/
content:
<p>Mettre son projet en production, c&#8217;est la galère. Tellement que mille méthodes ont vu le jour pour automatiser tout ça. Chef, salt, fabric, des script bash, virtualenv, git hooks, etc.</p>
<p>Après, il y a ceux qui utilisent des VM, qui elles, ont leur propres outils d&#8217;automatisation type Vagrant.</p>
<p>Et comme ça ne suffit pas, des services ce sont mis en place pour faciliter la mise en prod dans le cloud comme Heroku, Gondor, dotCloud&#8230;</p>
<p>Malgré ça, Max fait encore beaucoup de trucs à la main parce que &#8220;ça marche jamais comme prévu&#8221;. Pas très scalable.</p>
<p>Dernièrement, grâce à notre cher <a href="http://sametmax.com/author/cortex/">Cortex</a>, j&#8217;ai découvert un projet écrit en Go nommé <a href="http://docker.io/">Docker</a>, qui propose encore une autre approche du problème.</p>
<p>J&#8217;ai été intrigué après avoir visionné une conf sur le sujet car le principe est très cool, mais aussi parce que j&#8217;ai bossé à une époque avec <a href="https://github.com/jpetazzo">un des mecs</a> de chez docker. Et ce gars est un monstre. Mais vraiment. Une brute. Le genre qui n&#8217;a pas besoin de souris ni de gestionnaire de fenêtre, mais qui peut vous régler votre problème en tapant d&#8217;une main tout en vous expliquant ce qu&#8217;il fait dans votre domaine d&#8217;expertise alors que ce n&#8217;est pas le sien. Je l&#8217;ai vu faire. C&#8217;est énervant.</p>
<p>Pour le moment, je dois dire que Docker est vraiment sympa. Petit tour du propriétaire.</p>
<h2>C&#8217;est comme si chaque process avait sa mini VM</h2>
<p>Pour faire simple, docker, c&#8217;est de la virtualisation légère.</p>
<p>Ça marche comme cela : on prend une image d&#8217;un Linux de base qu&#8217;on fait tourner dans docker, on lui installe de quoi faire tourner un process &#8211; par exemple redis &#8211; et on obtient une nouvelle image qui contient juste la différence avec l&#8217;image précédente. On fait ça avec plein d&#8217;autres process, et quand on a besoin de l&#8217;un d&#8217;entre eux, on lance l&#8217;image qui contient l&#8217;install de ce celui-ci.</p>
<p>Ce sont des images très légères, on peut en lancer 100 sur une même machine si on le souhaite. Mais elles sont parfaitement isolées : elles ont leur propre système de fichier, leurs propres arbres de processus, utilisateurs, permissions, ports&#8230; Donc si par exemple je fais un nginx compilé avec des extensions exotiques et une config zarb, je peux en faire une image puis l&#8217;utiliser sur n&#8217;importe quel serveur qui contient Docker.</p>
<p>Le but, c&#8217;est de créer plein de petits conteneurs légers et isolés de votre machine. Et au lieu de configurer chaque serveur, on envoie juste les conteneurs dont on a besoin, et on est certain qu&#8217;ils marchent partout pareil, peu importe la config à l&#8217;arrivée. <strong>On virtualise des services, qu&#8217;on combine, et non une machine complète.</strong></p>
<p>Quand je vous dis que c&#8217;est léger, c&#8217;est VRAIMENT léger. A peine plus gourmand que le process sans la VM. En fait, un conteneur docker démarre presque instantanément, il n&#8217;y a pas de &#8220;boot&#8221; comme pour une vraie VM. Mais on en a les avantages car votre redis dockerisé marchera pareil sur une Fedora et une Ubuntu.</p>
<p>Docker ne marche que sur Linux pour le moment, et encore, sur un Linux pas trop vieux  car il utilise une technologie récente, dont vous avez probablement peu entendu parlé : <a href="https://fr.wikipedia.org/wiki/LXC">LXC</a>.</p>
<p>Détail amusant : Docker est pas mal utilisé dans la communauté des devs sous Mac, qui du coup ont une VM Ubuntu dans laquelle ils font tourner Docker pour travailler. N&#8217;est-ce pas merveilleux ?</p>
<h2>La démonstration obligatoire</h2>
<p>Je ne vais pas vous laisser comme ça et vous demander de vous la mettre derrière l&#8217;oreille, donc exemple d&#8217;utilisation sous Bubuntoune.</p>
<p>Je suis en 14.04, mais je pense que ça marche pareil depuis la 12.04.</p>
<p>Installation :</p>

<div class="wp_syntax"><div 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</span> <span style="color: #c20cb9; font-weight: bold;">install</span> docker.io</pre></div></div>

<p>Cela va mettre la commande <code>docker.io</code> à votre disposition, mais sous d&#8217;autres systèmes, elle s&#8217;appelle juste <code>docker</code>. Perso je fais un
<pre lang="python bash">alias docker="sudo docker.io"</pre>
<p> puisque de toute façon il faut toujours lancer cette commande avec les droits admin.</p>
<p>Ensuite, on va télécharger une image de base sur laquelle baser toutes nos images :</p>

<div class="wp_syntax"><div class="code"><pre class="bash" style="font-family:monospace;">$ docker pull ubuntu</pre></div></div>

<p>Docker.io, ce n&#8217;est pas juste un software, c&#8217;est aussi un service qui héberge les images. Vous pouvez bien entendu créer vos propres images de base, et héberger votre  dépôt personnel d&#8217;images, mais on ne va pas se faire chier à faire ça ici. Prévoyez quelques gigas de place, Docker ne prend pas beaucoup de ressources CPU/RAM, mais par contre ça bouffe en espace disque.</p>
<p>Donc là, je demande la récupération des images &#8220;ubuntu&#8221;, qui vont nous servir d&#8217;images de base. Elles sont toutes faites et prêtes à l&#8217;emploie, que demande le peuple ?</p>
<p><strong>Ça va downloader pendant pas mal de temps</strong>, puis vous aurez plusieurs images à votre disposition, que l&#8217;on peut lister ainsi :</p>

<div class="wp_syntax"><div class="code"><pre class="bash" style="font-family:monospace;">$ docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             VIRTUAL SIZE
ubuntu              <span style="color: #000000;">12.04</span>               8dbd9e392a96        <span style="color: #000000;">4</span> months ago        <span style="color: #000000;">131.5</span> MB <span style="color: #7a0874; font-weight: bold;">&#40;</span>virtual <span style="color: #000000;">131.5</span> MB<span style="color: #7a0874; font-weight: bold;">&#41;</span>
ubuntu              <span style="color: #000000;">12.10</span>               b750fe79269d        <span style="color: #000000;">5</span> months ago        <span style="color: #000000;">24.65</span> kB <span style="color: #7a0874; font-weight: bold;">&#40;</span>virtual <span style="color: #000000;">180.1</span> MB<span style="color: #7a0874; font-weight: bold;">&#41;</span>
ubuntu              latest              8dbd9e392a96        <span style="color: #000000;">4</span> months ago        <span style="color: #000000;">131.5</span> MB <span style="color: #7a0874; font-weight: bold;">&#40;</span>virtual <span style="color: #000000;">131.5</span> MB<span style="color: #7a0874; font-weight: bold;">&#41;</span>
ubuntu              precise             8dbd9e392a96        <span style="color: #000000;">4</span> months ago        <span style="color: #000000;">131.5</span> MB <span style="color: #7a0874; font-weight: bold;">&#40;</span>virtual <span style="color: #000000;">131.5</span> MB<span style="color: #7a0874; font-weight: bold;">&#41;</span>
ubuntu              quantal             b750fe79269d        <span style="color: #000000;">5</span> months ago        <span style="color: #000000;">24.65</span> kB <span style="color: #7a0874; font-weight: bold;">&#40;</span>virtual <span style="color: #000000;">180.1</span> MB<span style="color: #7a0874; font-weight: bold;">&#41;</span></pre></div></div>

<p>Vous faites votre marché : quelle image de base voulez-vous utiliser ?</p>
<p>La 12.04 est une LTS qui est déjà bien field testée, donc je vais prendre celle là.</p>
<p>Ensuite, pour lancer une commande avec, il faut faire <code>docker run id_image commande</code>. Ceci va lancer l&#8217;image, lancer la commande, et dès que la commande se termine, arrêter l&#8217;image :</p>

<div class="wp_syntax"><div class="code"><pre class="bash" style="font-family:monospace;">$ docker run 74fe38d11401 <span style="color: #7a0874; font-weight: bold;">echo</span> <span style="color: #ff0000;">'YEAHHHHHHHHHHHHHH'</span>
WARNING: Local <span style="color: #7a0874; font-weight: bold;">&#40;</span>127.0.0.1<span style="color: #7a0874; font-weight: bold;">&#41;</span> DNS resolver found <span style="color: #000000; font-weight: bold;">in</span> resolv.conf and containers can<span style="color: #ff0000;">'t use it. Using default external servers : [8.8.8.8 8.8.4.4]
YEAHHHHHHHHHHHHHH</span></pre></div></div>

<p>Mesurons le temps pris en tout pour faire cela :</p>

<div class="wp_syntax"><div class="code"><pre class="bash" style="font-family:monospace;">$ <span style="color: #000000; font-weight: bold;">time</span> <span style="color: #660033;">-f</span> <span style="color: #ff0000;">&quot;%e seconds&quot;</span> <span style="color: #c20cb9; font-weight: bold;">sudo</span> docker.io run 74fe38d11401 <span style="color: #7a0874; font-weight: bold;">echo</span> <span style="color: #ff0000;">'YEAHHHHHHHHHHHHHH'</span>
WARNING: Local <span style="color: #7a0874; font-weight: bold;">&#40;</span>127.0.0.1<span style="color: #7a0874; font-weight: bold;">&#41;</span> DNS resolver found <span style="color: #000000; font-weight: bold;">in</span> resolv.conf and containers can<span style="color: #ff0000;">'t use it. Using default external servers : [8.8.8.8 8.8.4.4]
YEAHHHHHHHHHHHHHH
0.45 seconds</span></pre></div></div>

<p>Comme vous le voyez, démarrer un conteneur, c&#8217;est vraiment rapide. Mais faire un
<pre lang="python bash">echo</pre>
<p>, ça ne sert pas à grand chose :)</p>
<p>Faisons quelque chose de plus utile : lançons un terminal bash et demandons un accès à stdin/stdout (<code>-i</code>) ainsi qu&#8217;un tty (<code>-t</code>) :</p>

<div class="wp_syntax"><div class="code"><pre class="bash" style="font-family:monospace;">$ docker run <span style="color: #660033;">-i</span> <span style="color: #660033;">-t</span> 74fe38d11401 <span style="color: #c20cb9; font-weight: bold;">bash</span>
WARNING: Local <span style="color: #7a0874; font-weight: bold;">&#40;</span>127.0.0.1<span style="color: #7a0874; font-weight: bold;">&#41;</span> DNS resolver found <span style="color: #000000; font-weight: bold;">in</span> resolv.conf and containers can<span style="color: #ff0000;">'t use it. Using default external servers : [8.8.8.8 8.8.4.4]
root@8195e92a5e62:/#</span></pre></div></div>

<p>Et hop, comme le process ne se termine pas tant qu&#8217;on est connecté au tty, on a le conteneur qui continue de tourner, mais on a un terminal dessus, nous permettant d&#8217;entrer n&#8217;importe quelle commande.</p>
<p>Si on installait <a href="http://0bin.net/">0bin</a> ?</p>
<p>D&#8217;abord, on a besoin de pip dans le conteneur:</p>

<div class="wp_syntax"><div class="code"><pre class="bash" style="font-family:monospace;"><span style="color: #666666; font-style: italic;"># apt-get install python-pip</span></pre></div></div>

<p>Notez qu&#8217;on est toujours root dans son conteneur. Après tout, c&#8217;est virtuel et isolé, donc pas de raison de se faire chier.</p>
<p>Ensuite, on tape dans pypi :</p>

<div class="wp_syntax"><div class="code"><pre class="bash" style="font-family:monospace;"><span style="color: #666666; font-style: italic;"># pip install zerobin</span></pre></div></div>

<p>Pas besoin de virtualenv, on est déjà dans un environnement isolé.</p>
<p>Faisons un petit fichier de config. On va avoir besoin de vim :</p>

<div class="wp_syntax"><div class="code"><pre class="bash" style="font-family:monospace;"><span style="color: #666666; font-style: italic;"># apt-get install vim</span>
<span style="color: #666666; font-style: italic;"># cd /home</span>
<span style="color: #666666; font-style: italic;"># vi settings.py</span></pre></div></div>

<p>On met des settings perso pour zerobin, histoire de montrer le principe de faire un conteneur avec un setup sur mesure (on peut, bien entendu, faire 1000 fois plus compliqué, c&#8217;est un exemple bande de bananes) :</p>

<div class="wp_syntax"><div class="code"><pre class="python" style="font-family:monospace;"><span style="color: #808080; font-style: italic;"># on le fait ecouter sur l'exterieur</span>
HOST = <span style="color: #483d8b;">&quot;0.0.0.0&quot;</span>
<span style="color: #808080; font-style: italic;"># on change le menu de la page d'accueil</span>
MENU = <span style="color: black;">&#40;</span>
    <span style="color: black;">&#40;</span><span style="color: #483d8b;">'Home'</span>, <span style="color: #483d8b;">'/'</span><span style="color: black;">&#41;</span>,
    <span style="color: black;">&#40;</span><span style="color: #483d8b;">'Contact'</span>, <span style="color: #483d8b;">'mailto:mysupermail@awesomesite.com'</span><span style="color: black;">&#41;</span>
<span style="color: black;">&#41;</span></pre></div></div>

<p>Et on lance notre petit zerobin :</p>

<div class="wp_syntax"><div class="code"><pre class="bash" style="font-family:monospace;"><span style="color: #666666; font-style: italic;"># zerobin --settings-file=settings.py</span></pre></div></div>

<p>Et voilà, on a un zerobin qui tourne dans notre conteneur isolé.</p>
<p>On va sauvegarder tout ça. Sur notre machine hôte (donc pas dans le conteneur), on va sauvegarder notre nouvelle image. D&#8217;abord, on va trouver l&#8217;ID du conteneur qui tourne :</p>

<div class="wp_syntax"><div class="code"><pre class="bash" style="font-family:monospace;">$ docker <span style="color: #c20cb9; font-weight: bold;">ps</span>
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS                     NAMES
8195e92a5e62        ubuntu:<span style="color: #000000;">12.04</span>        <span style="color: #c20cb9; font-weight: bold;">bash</span>                <span style="color: #000000;">37</span> minutes ago      Up <span style="color: #000000;">37</span> minutes                                 boring_mccarthy</pre></div></div>

<p>Ensuite on commit ce conteneur pour obtenir une nouvele image. Pour faire simple, on va l&#8217;appeller &#8220;zerobin&#8221; :</p>

<div class="wp_syntax"><div class="code"><pre class="bash" style="font-family:monospace;">$ docker commit 8195e92a5e62 zerobin</pre></div></div>

<p>On peut alors tranquillement arrêter notre conteneur :</p>

<div class="wp_syntax"><div class="code"><pre class="bash" style="font-family:monospace;">$ docker stop 8195e92a5e62</pre></div></div>

<p>Maintenant on peut pusher notre image tout neuve sur un repo distant avec <code>docker push</code> pour pouvoir la puller plus tard. Il faut ouvrir un compte sur leur service ou créer son propre depot, je vous laisse trouver <a href="http://docs.docker.io/use/workingwithrepository/">comment faire</a>. Plus simplement, on peut juste dumper l&#8217;image avec <code>docker save id_image > nom_image.tar</code>, la copier sur une clé USB, et la recharger avec <code>docker load < nom_image.tar</code>.</p>
<p>Dans tous les cas, une fois sur le serveur, vous pouvez lancer votre image "zerobin", ici encore une fois avec la commande bash:</p>

<div class="wp_syntax"><div class="code"><pre class="bash" style="font-family:monospace;"><span style="color: #666666; font-style: italic;"># docker run -t -i -p 7777:8000 zerobin bash</span></pre></div></div>

<p>Cette fois, on rajoute une option de plus : <code>-p 7777:8000</code> dit à docker de relier le port <code>7777</code> de ma machine au port <code>8000</code> (port de 0bin par défaut) de mon conteneur. Ainsi, si une app va sur le port <code>7777</code> de ma machine, elle va en fait parler au port <code>8000</code> du conteneur sans s'en rendre compte.</p>
<p>Du coup, je peux lancer mon petit zerobin depuis mon contenur :</p>

<div class="wp_syntax"><div class="code"><pre class="bash" style="font-family:monospace;"><span style="color: #666666; font-style: italic;"># cd /home</span>
<span style="color: #666666; font-style: italic;"># zerobin --settings-file=settings.py</span></pre></div></div>

<p>Et voilà ! J'ai un zerobin qui marche, qui est préconfiguré, isolé et portable. Si je change de serveur, ou même d'OS, je peux juste reprendre le même conteneur et le relancer tel quel. Et je peux faire ça avec plein de process et les faire communiquer entre eux.</p>
<div id="attachment_10271" class="wp-caption aligncenter" style="width: 336px"><a href="media/73e7747e.Capture-du-2014-05-16-015832.png" data-original-source="http://sametmax.com/wp-content/uploads/2014/05/Capture-du-2014-05-16-015832.png"><img src="media/73e7747e.Capture-du-2014-05-16-015832.png" data-original-source="http://sametmax.com/wp-content/uploads/2014/05/Capture-du-2014-05-16-015832.png" alt="Capture d&#039;écran de zerobin" title="J&#039;accède à 7777 sur la machine, mais ça tape sur 8000 dans mon conteneur" width="326" height="179" class="size-full wp-image-10271" /></a><p class="wp-caption-text">J&#039;accède à 7777 sur la machine, mais ça tape sur 8000 dans mon conteneur</p></div>
<p>Docker est un outil très riche, et vous vous doutez bien que je ne vous ai montré que le début.</p>
<p>Par exemple, vous voulez sans doute ne pas avoir à lancer bash, puis un <code>cd</code>, puis zerobin. Ça peut s'automatiser avec un <a href="https://www.docker.io/learn/dockerfile/">dockerfile</a>. Vous voulez aussi peut être lancer le conteneur en arrière plan, ça se fait avec l'option <code>-d</code>. Ou peut être voulez-vous un dossier partagé entre tous les conteneurs ? C'est possible avec l'option <a href="http://docs.docker.io/use/working_with_volumes/">volume</a>.</p>
<p>Parmi les usages vraiment intéressants de dockers : packager les services très custos comme les nginx ou ffmpeg compilés avec des options cryptiques, deployer son app django avec toutes les libs Python et les settings qui vont bien, remplacer une app à chaud en redirigeant juste le load balancer sur le nouveau conteneur, ou encore fournir un env de test à un client. Perso, rien que pour tester des nouveaux logiciels sans pourir ma machine, je trouve ça pratique : on a bien moins peur de faire un <code>make install</code>.</p>
<p>Je vous laisse prendre en main le nouveau joujou, j'ai des serveurs à planter.</p>
 <p><a href="http://sametmax.com/?flattrss_redirect&amp;id=10270&amp;md5=8f433005660e2f39d8d2098a80d502ca" title="Flattr" target="_blank"><img src="media/fc229d00.flattr-badge-large.png" data-original-source="http://sametmax.com/wp-content/plugins/flattr/img/flattr-badge-large.png" alt="flattr this!"/></a></p>


feed_id: http://sametmax.com/?p=10245
title: Peut-on compiler un programme Python ?
uri: Peut-on-compiler-un-programme-Python
url: http://sametmax.com/peut-on-compiler-un-programme-python/
content:
<p>Sans répit, des hordes d&#8217;OP ont demandé sur Stackoverflow comment compiler un programme Python. La plupart du temps, pour des raisons d’obfuscation ou pour faire un joli .exe. Et inlassablement, les devs bienveillants leur répondaient que Python était un langage interprété, et donc non compilable.</p>
<p>Commença alors la lente fuite des impatients vers Go qui permettait de le faire si facilement.</p>
<p>Et puis arriva <a href="http://cython.org/">Cython</a>, qui promettait de permettre de transformer du Python en C, afin de l&#8217;embarquer dans un autre code C, ou permettre des appels de C vers Python et inversement plus facile.</p>
<p>Cela visait les perfs, l&#8217;intégration, faire des dll/so en Python, et du coup, dans l&#8217;excitation, tout le monde a loupé un petit détail minuscule.</p>
<p>Les mecs avaient implémenté un compilateur Python => C complet.</p>
<p>En fait, Cython permet &#8211; et c&#8217;est la que c&#8217;est fun car c&#8217;est un effet de bord presque involontaire &#8211; de créer un programme compilé <strong>autonome</strong> en Python. Et avec la toolchain classique en plus.</p>
<p>Ça peut gérer les dépendances, et ça marche même avec des trucs compilés balèzes du genre PyQT. </p>
<p>Je sens le chapiteau se dresser sous la braguette, alors voici la démo&#8230;</p>
<p>Pour montrer qu&#8217;on ne fait pas que du &#8220;Hello World&#8221;, je vais utiliser des dépendances assez complexes : <a href="http://lxml.de">lxml</a> (qui contient une extension C compilée), <a href="http://sametmax.com/sept-petites-libs-qui-changent-la-vie-dun-dev-python/">path.py, requests</a> et <a href="http://sametmax.com/la-plus-belle-maniere-de-parser-les-arguments-de-script-en-python/">docopt</a> :</p>

<div class="wp_syntax"><div class="code"><pre class="python" style="font-family:monospace;"><span style="color: #483d8b;">&quot;&quot;&quot;
    Download a file and save it to a location.
&nbsp;
    Usage:
        downloader.py &lt;target&gt; [&lt;location&gt;]
&quot;&quot;&quot;</span>
&nbsp;
<span style="color: #ff7700;font-weight:bold;">import</span> requests
<span style="color: #ff7700;font-weight:bold;">import</span> docopt
&nbsp;
<span style="color: #ff7700;font-weight:bold;">from</span> lxml.<span style="color: black;">html</span> <span style="color: #ff7700;font-weight:bold;">import</span> parse
<span style="color: #ff7700;font-weight:bold;">from</span> path <span style="color: #ff7700;font-weight:bold;">import</span> path
&nbsp;
args = docopt.<span style="color: black;">docopt</span><span style="color: black;">&#40;</span>__doc__<span style="color: black;">&#41;</span>
&nbsp;
p = path<span style="color: black;">&#40;</span>args<span style="color: black;">&#91;</span><span style="color: #483d8b;">'&lt;location&gt;'</span><span style="color: black;">&#93;</span><span style="color: black;">&#41;</span>.<span style="color: black;">realpath</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
&nbsp;
<span style="color: #ff7700;font-weight:bold;">if</span> p.<span style="color: black;">isdir</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>:
    p = p / path<span style="color: black;">&#40;</span>args<span style="color: black;">&#91;</span><span style="color: #483d8b;">'&lt;target&gt;'</span><span style="color: black;">&#93;</span><span style="color: black;">&#41;</span>.<span style="color: black;">namebase</span>
&nbsp;
p.<span style="color: black;">write_bytes</span><span style="color: black;">&#40;</span>requests.<span style="color: black;">get</span><span style="color: black;">&#40;</span>args<span style="color: black;">&#91;</span><span style="color: #483d8b;">'&lt;target&gt;'</span><span style="color: black;">&#93;</span><span style="color: black;">&#41;</span>.<span style="color: black;">content</span><span style="color: black;">&#41;</span>
&nbsp;
title = parse<span style="color: black;">&#40;</span>p<span style="color: black;">&#41;</span>.<span style="color: black;">getroot</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>.<span style="color: black;">find</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'head'</span><span style="color: black;">&#41;</span>.<span style="color: black;">find</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'title'</span><span style="color: black;">&#41;</span>.<span style="color: black;">text</span>
<span style="color: #ff7700;font-weight:bold;">print</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'Downloaded &quot;%s&quot;'</span> <span style="color: #66cc66;">%</span> title<span style="color: black;">&#41;</span></pre></div></div>

<p>Je ne m&#8217;encombre pas de gestion d&#8217;erreurs, juste suffisamment d&#8217;appels pour faire travailler les dépendances.</p>
<p>Qui dit compilation, dit environnement. Pour ma part, je suis sous Ubuntu 14.04 et je vais me compiler un petit programme en Python 3, avec lesquels il me faut installer Cython et <a href="http://sametmax.com/votre-python-aime-les-pip/">pip</a> pour Python 3, de quoi compiler du C, et les dépendances de notre script :</p>

<div class="wp_syntax"><div 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</span> <span style="color: #c20cb9; font-weight: bold;">install</span> <span style="color: #c20cb9; font-weight: bold;">gcc</span> cython3 python-pip3 python3-lxml
$ pip3 <span style="color: #c20cb9; font-weight: bold;">install</span> requests docopt path.py</pre></div></div>

<p>Je n&#8217;ai même pas eu besoin d&#8217;installer les headers de lxml. Vive le Dieu des geeks.</p>
<p>L&#8217;utilisation de Cython dans notre cas est assez simple : on lui dit de transformer notre module en module C. <code>--embed</code> lui dit d&#8217;inclure l&#8217;interpréteur Python dedans.</p>

<div class="wp_syntax"><div class="code"><pre class="bash" style="font-family:monospace;">$ cython3 downloader.py <span style="color: #660033;">-o</span> downloader.c <span style="color: #660033;">--embed</span></pre></div></div>

<p>On obtient un fichier C bien copieux :</p>

<div class="wp_syntax"><div class="code"><pre class="bash" style="font-family:monospace;">$ <span style="color: #c20cb9; font-weight: bold;">head</span> downloader.c
<span style="color: #000000; font-weight: bold;">/*</span> Generated by Cython 0.20.1post0 <span style="color: #7a0874; font-weight: bold;">&#40;</span>Debian 0.20.1+git90-g0e6e38e-1ubuntu2<span style="color: #7a0874; font-weight: bold;">&#41;</span> on Sun May <span style="color: #000000;">11</span> <span style="color: #000000;">22</span>:<span style="color: #000000;">53</span>:<span style="color: #000000;">18</span> <span style="color: #000000;">2014</span> <span style="color: #000000; font-weight: bold;">*/</span>
&nbsp;
<span style="color: #666666; font-style: italic;">#define PY_SSIZE_T_CLEAN</span>
<span style="color: #666666; font-style: italic;">#ifndef CYTHON_USE_PYLONG_INTERNALS</span>
<span style="color: #666666; font-style: italic;">#ifdef PYLONG_BITS_IN_DIGIT</span>
<span style="color: #666666; font-style: italic;">#define CYTHON_USE_PYLONG_INTERNALS 0</span>
<span style="color: #666666; font-style: italic;">#else</span>
<span style="color: #666666; font-style: italic;">#include &quot;pyconfig.h&quot;</span>
<span style="color: #666666; font-style: italic;">#ifdef PYLONG_BITS_IN_DIGIT</span>
<span style="color: #666666; font-style: italic;">#define CYTHON_USE_PYLONG_INTERNALS 1</span></pre></div></div>

<p>Il ne reste plus qu&#8217;à compiler ce dernier :</p>

<div class="wp_syntax"><div class="code"><pre class="bash" style="font-family:monospace;">$ <span style="color: #c20cb9; font-weight: bold;">gcc</span> <span style="color: #660033;">-Os</span> <span style="color: #660033;">-I</span> <span style="color: #000000; font-weight: bold;">/</span>usr<span style="color: #000000; font-weight: bold;">/</span>include<span style="color: #000000; font-weight: bold;">/</span>python3.4m  downloader.c <span style="color: #660033;">-o</span> download -lpython3.4m <span style="color: #660033;">-lpthread</span> <span style="color: #660033;">-lm</span> <span style="color: #660033;">-lutil</span> <span style="color: #660033;">-ldl</span></pre></div></div>

<p>J&#8217;ai mis <code>-I /usr/include/python3.4m</code> et <code>-lpython3.4m</code> car les headers de Python sont là dedans sur ma machine. Le reste, ce sont des options que j&#8217;ai copier / coller sur le Web car GCC a plus de flags qu&#8217;une ambassade américaine et que j&#8217;ai des choses plus importantes à retenir, comme par exemple la recette du guacamole.</p>
<p>On obtient un exécutable tout à fait exécutablatoire :</p>

<div class="wp_syntax"><div class="code"><pre class="bash" style="font-family:monospace;">$ <span style="color: #c20cb9; font-weight: bold;">chmod</span> u+x downloader
$ .<span style="color: #000000; font-weight: bold;">/</span>downloader <span style="color: #666666; font-style: italic;"># docopt marche :)</span>
Usage:
       downloader.py <span style="color: #000000; font-weight: bold;">&lt;</span>target<span style="color: #000000; font-weight: bold;">&gt;</span> <span style="color: #7a0874; font-weight: bold;">&#91;</span><span style="color: #000000; font-weight: bold;">&lt;</span>location<span style="color: #000000; font-weight: bold;">&gt;</span><span style="color: #7a0874; font-weight: bold;">&#93;</span></pre></div></div>

<p>Et ça télécharge comme prévu :</p>

<div class="wp_syntax"><div class="code"><pre class="bash" style="font-family:monospace;">$ .<span style="color: #000000; font-weight: bold;">/</span>downloader http:<span style="color: #000000; font-weight: bold;">//</span>sametmax.com index.html
Downloaded <span style="color: #ff0000;">&quot;Sam &amp; Max: Python, Django, Git et du cul | Deux développeurs en vadrouille qui se sortent les doigts du code&quot;</span></pre></div></div>

<p>Le script Python original fait 472 octets, le binaire obtenu 28,7 ko. Mais ce n&#8217;est pas standalone, puisque je n&#8217;ai pas demandé la compilation des dépendances (je viens de le tester sur une autre Ubuntu, il pleure que requests n&#8217;est pas installé). Je vous laisse vous faire chier à trouver comment faire pour répondre à votre cas de figure exact, mais ça implique d&#8217;utiliser <a href="https://github.com/cython/cython/blob/master/bin/cython_freeze">cython_freeze</a>.</p>
<p>Après tout, cet article est intitulé &#8220;Peut on compiler un programme Python ?&#8221; et non &#8220;Tuto pour rendre un script Python stand alone&#8221;. Je ne suis pas fou.</p>
<p>Apparemment ça marche sous Windows et Mac, même si je n&#8217;ai pas de quoi tester sous la main (bon, si j&#8217;ai une partition Windows, mais faut rebooter, tout réinstaller, merde quoi&#8230;).</p>
<p>Donc si vous voulez faire une application en Python et la rendre stand alone, l&#8217;obfusquer, pondre un exe pour rendre le téléchargement facile, rassurer les gens ou simplement ignorer les mises à jours des libs de l&#8217;OS, Cython est une bonne piste.</p>
<p>Petit bonus, votre programme sera plus rapide, car il saute l&#8217;étape d&#8217;interprétation. Bien entendu, vous récoltez les galères qui viennent avec la compilation, à savoir les différentes architectures, le linking vers les libs qui peuvent changer de place ou de versions, etc.</p>
 <p><a href="http://sametmax.com/?flattrss_redirect&amp;id=10245&amp;md5=aca3a675be00b8ed30202b7be62ec457" title="Flattr" target="_blank"><img src="media/fc229d00.flattr-badge-large.png" data-original-source="http://sametmax.com/wp-content/plugins/flattr/img/flattr-badge-large.png" alt="flattr this!"/></a></p>


feed_id: http://sametmax.com/?p=10392
title: Télécharger une page du blog en PDF
uri: Télécharger-une-page-du-blog-en-PDF
url: http://sametmax.com/telecharger-une-page-du-blog-en-pdf/
content:
<p>On m&#8217;a <a href="http://sametmax.com/le-potentiel-de-wamp-autobahn-et-crossbar-io/#comment-40031">demandé</a> de rendre possible téléchargement des articles en PDF.</p>
<p>Je suis paresseux, donc plutôt que d&#8217;installer un plugin, j&#8217;ai juste fait un lien vers un service externe en haut à droite de toutes les pages.</p>
<p>Si vous cliquez, ça vous génère le PDF pour la page complète, et ça marche pour toutes les URL, pas uniquement les articles.</p>
<p>De rien.</p>
 <p><a href="http://sametmax.com/?flattrss_redirect&amp;id=10392&amp;md5=e45b3b95cad9fbaa3dcac5b80957d55d" title="Flattr" target="_blank"><img src="media/fc229d00.flattr-badge-large.png" data-original-source="http://sametmax.com/wp-content/plugins/flattr/img/flattr-badge-large.png" alt="flattr this!"/></a></p>


feed_id: http://sametmax.com/?p=10398
title: Écrivez à l’arc, envoyez des sioux
uri: Écrivez-à-l-arc-envoyez-des-sioux
url: http://sametmax.com/ecrivez-a-larc-envoyez-des-sioux/
content:
<p>Les gens derrière le développement de Python sont extraordinaires : Python existe depuis 1990, cela fait donc plus de 20 ans qu&#8217;il est là, à évoluer, sans cesse.</p>
<p>Et cela a un coût. Un langage qui permet à Youtube, Dropbox et Instagram de tourner, ça doit douiller non ?</p>
<div id="attachment_10403" class="wp-caption aligncenter" style="width: 510px"><a href="media/4d6bba91.tumblr_n684vge7NJ1r539hzo1_500.jpg" data-original-source="http://sametmax.com/wp-content/uploads/2014/06/tumblr_n684vge7NJ1r539hzo1_500.jpg"><img src="media/4d6bba91.tumblr_n684vge7NJ1r539hzo1_500.jpg" data-original-source="http://sametmax.com/wp-content/uploads/2014/06/tumblr_n684vge7NJ1r539hzo1_500.jpg" alt="Photo de personnes déguisés en télétubies fumant et buvant" title="Youtube, Dropbox et instagram consomment toujours beaucoup" width="500" height="373" class="size-full wp-image-10403" /></a><p class="wp-caption-text">Youtube, Dropbox et instagram consomment toujours beaucoup</p></div>
<p>Et bien, la partie fun, c&#8217;est que le plus gros du boulot est fait par des volontaires. En fait, le budget total alloué par la Python Software Fundation pour <strong>payer</strong> des dev à travailler sur le langage est <a href="http://pyfound.blogspot.com/2012/01/psf-grants-over-37000-to-python.html">moins de 30 000 euros par an</a>. Même pas le salaire d&#8217;un ingé sénior. Parce qu&#8217;ils n&#8217;ont pas de pognon.</p>
<p>Alors, on peut se gargariser que, trop cool, la communauté Python contribue à mort et ça coute pas cher. Mais moi ce que je vois, ce sont des centaines de mecs qui bossent super dur, gratos, pour qu&#8217;on puisse en bénéficier d&#8217;un côté, et de l&#8217;autre, une fondation qui n&#8217;a pas de thune pour aller au bout de sa mission.</p>
<p>La honte, c&#8217;est bien entendu que des boites comme Google, Apple ou Sony, qui sont blindées, ne renvoient pas l’ascenseur alors qu&#8217;ils utilisent le langage en interne.</p>
<div id="attachment_10400" class="wp-caption aligncenter" style="width: 510px"><a href="media/8f7c1e9b.tumblr_n572kwK67E1r539hzo1_500.jpg" data-original-source="http://sametmax.com/wp-content/uploads/2014/06/tumblr_n572kwK67E1r539hzo1_500.jpg"><img src="media/8f7c1e9b.tumblr_n572kwK67E1r539hzo1_500.jpg" data-original-source="http://sametmax.com/wp-content/uploads/2014/06/tumblr_n572kwK67E1r539hzo1_500.jpg" alt="Photo d&#039;un homme assi sur un fauteuil luxueux avec un catana et une peau de tigre" title="Ouai c&#039;est Google. Ouai. Ouai. Nan rien à foutre." width="500" height="500" class="size-full wp-image-10400" /></a><p class="wp-caption-text">Ouai c&#039;est Google. Ouai. Ouai. Nan rien à foutre.</p></div>
<p>Mais ça, on peut rien y faire dans la seconde.</p>
<p>Ce qu&#8217;on peut faire tout de suite, en revanche, c&#8217;est filer 10€ &#8211; 100€, nous, en tant que dev, ou boite. Si vous utilisez professionnellement Python, je pense que sur toute une vie, c&#8217;est raisonnable.</p>
<p>Je hais la culpabilisation, donc l&#8217;idée n&#8217;est pas de vous dire &#8220;si vous ne donnez pas vous êtes des connards&#8221;. Simplement je donne régulièrement aux projets que j&#8217;utilise : VLC, Ubuntu, LibreOffice, etc. Et Le blog fait la promotion de Python, alors il est juste que je vous invite à envoyer des sous à la PSF.</p>
<div id="attachment_10399" class="wp-caption aligncenter" style="width: 510px"><a href="media/370288cf.tumblr_n55aed3WRL1r539hzo1_500.jpg" data-original-source="http://sametmax.com/wp-content/uploads/2014/06/tumblr_n55aed3WRL1r539hzo1_500.jpg"><img src="media/370288cf.tumblr_n55aed3WRL1r539hzo1_500.jpg" data-original-source="http://sametmax.com/wp-content/uploads/2014/06/tumblr_n55aed3WRL1r539hzo1_500.jpg" alt="Photo d&#039;un chat mençant un chien avec un couteau" title="Par contre le chantage ne me gène pas. Si vous ne donnez pas, je posterai une photo de chaton par jour dans la rubrique cul de sam et max. Ouai, nan, c&#039;est plus dur pour moi que pour vous en fait." width="500" height="427" class="size-full wp-image-10399" /></a><p class="wp-caption-text">Par contre le chantage ne me gène pas. Si vous ne donnez pas, je posterai une photo de chaton par jour dans la rubrique cul de sam et max. Ouai, nan, c&#039;est plus dur pour moi que pour vous en fait.</p></div>
<p>Ce n&#8217;est pas une question d&#8217;être une bonne personne. C&#8217;est juste un bon moyen d&#8217;aider au dev d&#8217;un outil qu&#8217;on aime, pour en bénéficier demain.</p>
<p style="text-align:center;  margin:3em">
<a style="font-size:3em;" href="https://www.python.org/psf/donations/">Faites pêter l&#8217;oseille</a>
  </p>
<p>Dommage, ils n&#8217;acceptent pas les bitcoins.</p>
 <p><a href="http://sametmax.com/?flattrss_redirect&amp;id=10398&amp;md5=ab455c5aa7df535daf2230b1eeecfc7c" title="Flattr" target="_blank"><img src="media/fc229d00.flattr-badge-large.png" data-original-source="http://sametmax.com/wp-content/plugins/flattr/img/flattr-badge-large.png" alt="flattr this!"/></a></p>


feed_id: http://sametmax.com/?p=10418
title: Deferred, Future et Promise : le pourquoi, le comment, et quand est-ce qu’on mange
uri: Deferred-Future-et-Promise-le-pourquoi-le-comment-et-quand-est-ce
url: http://sametmax.com/deferred-future-et-promise-le-pourquoi-le-comment-et-quand-est-ce-quon-mange/
content:
<p>Si vous avez plongé dans le monde de la programmation asynchrone non bloquante, vous avez du vous heurter aux <a href="http://sametmax.com/quest-ce-quun-callback/">callbacks</a>. Si ce n&#8217;est pas le cas, aller lire l&#8217;article, et faites vos armes sur jQuery, je vais m&#8217;en servir en exemple.</p>
<p>Signalement de rigueur que l&#8217;article est long :</p>

<!-- iframe plugin v:2.3 - wordpress.org/extend/plugins/iframe/ -->
<iframe width="560" height="315" src="//www.youtube.com/embed/E8b4xYbEugo" frameborder="0" scrolling="no" class="iframe-class"></iframe>
<p>Un callback, ça va.</p>
<p>Deux callbacks, pour un seul appel, ça commence à être chiant, mais c&#8217;est compréhensible.</p>
<p>Quand les callbacks appellent eux aussi des callbacks, ça donne des codes imbitables :</p>

<div class="wp_syntax"><div class="code"><pre class="javascript" style="font-family:monospace;">$<span style="color: #009900;">&#40;</span><span style="color: #003366; font-weight: bold;">function</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#123;</span>
  $.<span style="color: #660066;">post</span><span style="color: #009900;">&#40;</span><span style="color: #3366CC;">'/auth/token'</span><span style="color: #339933;">,</span> <span style="color: #003366; font-weight: bold;">function</span><span style="color: #009900;">&#40;</span>token<span style="color: #009900;">&#41;</span><span style="color: #009900;">&#123;</span>
    saveToken<span style="color: #009900;">&#40;</span>token<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
    $.<span style="color: #660066;">get</span><span style="color: #009900;">&#40;</span><span style="color: #3366CC;">'/sessions/last'</span><span style="color: #339933;">,</span> <span style="color: #003366; font-weight: bold;">function</span><span style="color: #009900;">&#40;</span>session<span style="color: #009900;">&#41;</span><span style="color: #009900;">&#123;</span>
      <span style="color: #000066; font-weight: bold;">if</span> <span style="color: #009900;">&#40;</span>session.<span style="color: #660066;">device</span> <span style="color: #339933;">!=</span> currentDevice<span style="color: #009900;">&#41;</span><span style="color: #009900;">&#123;</span>
        $.<span style="color: #660066;">get</span><span style="color: #009900;">&#40;</span><span style="color: #3366CC;">'/session/ '</span> <span style="color: #339933;">+</span> session.<span style="color: #660066;">id</span> <span style="color: #339933;">+</span> <span style="color: #3366CC;">'/context'</span><span style="color: #339933;">,</span> <span style="color: #003366; font-weight: bold;">function</span><span style="color: #009900;">&#40;</span>context<span style="color: #009900;">&#41;</span><span style="color: #009900;">&#123;</span>
          loadContext<span style="color: #009900;">&#40;</span><span style="color: #003366; font-weight: bold;">function</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#123;</span>
            startApp<span style="color: #009900;">&#40;</span><span style="color: #003366; font-weight: bold;">function</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#123;</span>
              initUi<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span>
            <span style="color: #009900;">&#125;</span><span style="color: #009900;">&#41;</span>
          <span style="color: #009900;">&#125;</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#125;</span>
        <span style="color: #009900;">&#41;</span><span style="color: #009900;">&#125;</span>
      <span style="color: #000066; font-weight: bold;">else</span> <span style="color: #009900;">&#123;</span>
        startApp<span style="color: #009900;">&#40;</span><span style="color: #003366; font-weight: bold;">function</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#123;</span>
          initUi<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span>
        <span style="color: #009900;">&#125;</span><span style="color: #009900;">&#41;</span>
      <span style="color: #009900;">&#125;</span><span style="color: #009900;">&#125;</span>
    <span style="color: #009900;">&#41;</span>
  <span style="color: #009900;">&#125;</span><span style="color: #009900;">&#41;</span>
<span style="color: #009900;">&#125;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span></pre></div></div>

<p>Il y a pire que de lire ce code : le modifier ! Retirez un bloc, pour voir. Oh, et histoire de vous faire partager l&#8217;expérience complète, j&#8217;ai volontairement déplacé l&#8217;indentation d&#8217;une parenthèse et de deux brackets.</p>
<p>Or les codes asynchrones ont besoin de callback afin d’enchainer certaines opérations dans le bon ordre, sinon on ne peut pas récupérer le résultat d&#8217;une fonction et l&#8217;utiliser dans une autre, puisqu&#8217;on ne sait pas quand l&#8217;opération se termine.</p>
<p>Dans notre exemple, <code>$.post</code> et <code>$.get</code> font des requêtes POST et GET, et comme on ne sait pas quand le serveur va répondre, il faut mettre un callback pour gérer la réponse quand elle arrive. C&#8217;est plus performant que de bloquer jusqu&#8217;à ce que la première requête soit terminée car pendant ce temps, notre programme peut faire autre chose. Mais c&#8217;est aussi super relou à écrire et comprendre.</p>
<p>Entre en jeu les promesses (promises). Ou les deferred. Ou les futures.</p>
<p>Typiquement, on retrouve des deferreds dans <a href="https://twistedmatrix.com/trac/">Twisted</a>, des promises pour <a href="http://api.jquery.com/jquery.ajax/">l&#8217;AJAX avec jQuery,</a> des futures pour <a href="http://docs.python.org/3.4/library/asyncio-protocol.html">asyncio</a>&#8230; Mais il y en a un peu partout de nos jours, et une lib peut utiliser plusieurs de ces concepts.</p>
<p>En fait c&#8217;est la même chose, un nom différent donné au même concept, par des gens qui l&#8217;ont réinventé dans leur coin. Les puristes vous diront qu&#8217;il y a des différences dans l&#8217;implémentation, ou alors que la promesse est l&#8217;interface tandis que le deferred est l&#8217;objet retourné, bla, bla, bla.</p>
<p>Fuck it, on va considérer que c&#8217;est tout pareil.</p>
<p>Les promesses sont une des manières de rendre un code asynchrone plus facile à gérer. On dit : ce groupe de fonctions doit s&#8217;exécuter dans un ordre car elles sont dépendantes les unes des autres.</p>
<p>Il y a d&#8217;autres moyens de gérer le problème de l&#8217;asynchrone: des événements, des queues, etc. L&#8217;avantage des promesses c&#8217;est que c&#8217;est assez simple, et ça marche là où on utilisait des callbacks avant, donc on a pu les rajouter aux libs qui étaient blindés de callbacks.</p>
<h2>Le principe</h2>
<p>La promesse est un moyen de dire que certaines fonctions, bien que non bloquantes et asynchrones, sont liées entre elles, et doivent s&#8217;exécuter les unes à la suite des autres. Cela permet de donner un ordre d&#8217;exécution à un groupe de fonctions, et surtout, que chaque fonction puisse accéder au résultat de la fonction précédente. Tout ceci sans bloquer le reste du système asynchrone.</p>
<p>En résumé, <strong>cela donne un gout de programmation synchrone, à quelque chose qui ne l&#8217;est pas.</strong></p>
<p>Cela se passe ainsi :</p>
<ul>
<li>La fonction asynchrone retourne un objet immédiatement : la promesse.</li>
<li>On ne passe pas de callback à la fonction. On rajoute un callback à la promesse.</li>
<li>Le callback prend en paramètre le résultat de la fonction asynchrone.</li>
<li>Le callback retourne le résultat de son traitement.</li>
<li>On peut rajouter autant de callbacks qu&#8217;on veut à la promesse, chacun devant accepter le résultat du callback précédent et retourner son propre résultat.</li>
<li>Si un des callbacks retourne une promesse, elle est fusionnée avec la promesse initiale, et c&#8217;est son résultat que le prochain callback va récupérer</li>
</ul>
<p>Voilà un exemple :</p>

<div class="wp_syntax"><div class="code"><pre class="javascript" style="font-family:monospace;"><span style="color: #006600; font-style: italic;">// $.get est asynchrone. On a pas le résultat tout de suite, mais en attendant</span>
<span style="color: #006600; font-style: italic;">// on a une promesse tout de suite.</span>
<span style="color: #003366; font-weight: bold;">var</span> $promesse <span style="color: #339933;">=</span> $.<span style="color: #660066;">get</span><span style="color: #009900;">&#40;</span><span style="color: #3366CC;">'/truc/machin'</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
&nbsp;
<span style="color: #006600; font-style: italic;">// premier callback. Il sera appelé quand $.get aura récupéré son</span>
<span style="color: #006600; font-style: italic;">// résultat</span>
$promesse.<span style="color: #660066;">then</span><span style="color: #009900;">&#40;</span><span style="color: #003366; font-weight: bold;">function</span><span style="color: #009900;">&#40;</span>resultat<span style="color: #009900;">&#41;</span><span style="color: #009900;">&#123;</span>
  <span style="color: #006600; font-style: italic;">// faire un truc avec le résultat</span>
  <span style="color: #006600; font-style: italic;">// puis on retourne le nouveau résultat</span>
  <span style="color: #000066; font-weight: bold;">return</span> nouveau_resultat<span style="color: #339933;">;</span>
<span style="color: #009900;">&#125;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
&nbsp;
<span style="color: #006600; font-style: italic;">// deuxième callback. Il sera appelé quand le premier callback</span>
<span style="color: #006600; font-style: italic;">// aura retourné son résultat.</span>
$promesse.<span style="color: #660066;">then</span><span style="color: #009900;">&#40;</span><span style="color: #003366; font-weight: bold;">function</span><span style="color: #009900;">&#40;</span>nouveau_resultat<span style="color: #009900;">&#41;</span><span style="color: #009900;">&#123;</span>
  <span style="color: #006600; font-style: italic;">// faire un truc</span>
<span style="color: #009900;">&#125;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span></pre></div></div>

<p>Notez bien que c&#8217;est TRES différent de ça (en Python):</p>

<div class="wp_syntax"><div class="code"><pre class="python" style="font-family:monospace;">resultat = request.<span style="color: black;">get</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'/truc/marchin'</span><span style="color: black;">&#41;</span>
&nbsp;
<span style="color: #ff7700;font-weight:bold;">def</span> function<span style="color: black;">&#40;</span>resultat<span style="color: black;">&#41;</span>:
  <span style="color: #808080; font-style: italic;"># faire un truc</span>
  <span style="color: #ff7700;font-weight:bold;">return</span> nouveau_resultat
nouveau_resultat = function<span style="color: black;">&#40;</span>resultat<span style="color: black;">&#41;</span>
&nbsp;
<span style="color: #ff7700;font-weight:bold;">def</span> autre_function<span style="color: black;">&#40;</span>nouveau_resultat<span style="color: black;">&#41;</span>:
  <span style="color: #808080; font-style: italic;"># faire un truc</span>
autre_function<span style="color: black;">&#40;</span>nouveau_resultat<span style="color: black;">&#41;</span></pre></div></div>

<p>En Python, le code est bloquant par défaut. Ça va marcher, mais pendant que le code attend la réponse du serveur, votre ordinateur est en pause et ne travaille pas.</p>
<h2>Un plus beau code</h2>
<p>On se retrouve avec un code asynchrone, mais qui s&#8217;exécute dans l&#8217;ordre de lecture. Et comme on peut chainer les <code>then()</code> et donc ne pas réécrire <code>$promesse</code> à chaque fois, on obtient quelque chose de beaucoup plus lisible :</p>

<div class="wp_syntax"><div class="code"><pre class="javascript" style="font-family:monospace;">$.<span style="color: #660066;">get</span><span style="color: #009900;">&#40;</span><span style="color: #3366CC;">'/truc/machin'</span><span style="color: #009900;">&#41;</span>
.<span style="color: #660066;">then</span><span style="color: #009900;">&#40;</span><span style="color: #003366; font-weight: bold;">function</span><span style="color: #009900;">&#40;</span>resultat<span style="color: #009900;">&#41;</span><span style="color: #009900;">&#123;</span>
  <span style="color: #006600; font-style: italic;">// faire un truc</span>
  <span style="color: #000066; font-weight: bold;">return</span> nouveau_resultat<span style="color: #339933;">;</span>
<span style="color: #009900;">&#125;</span><span style="color: #009900;">&#41;</span>
.<span style="color: #660066;">then</span><span style="color: #009900;">&#40;</span><span style="color: #003366; font-weight: bold;">function</span><span style="color: #009900;">&#40;</span>nouveau_resultat<span style="color: #009900;">&#41;</span><span style="color: #009900;">&#123;</span>
  <span style="color: #006600; font-style: italic;">// faire un truc</span>
<span style="color: #009900;">&#125;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span></pre></div></div>

<p>Si on reprend notre premier exemple, ça donne ça :</p>

<div class="wp_syntax"><div class="code"><pre class="javascript" style="font-family:monospace;">$<span style="color: #009900;">&#40;</span><span style="color: #003366; font-weight: bold;">function</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#123;</span>
&nbsp;
<span style="color: #006600; font-style: italic;">// create new token</span>
$.<span style="color: #660066;">post</span><span style="color: #009900;">&#40;</span><span style="color: #3366CC;">'/auth/token'</span><span style="color: #009900;">&#41;</span>
&nbsp;
<span style="color: #006600; font-style: italic;">// then save token and get last session</span>
.<span style="color: #660066;">then</span><span style="color: #009900;">&#40;</span><span style="color: #003366; font-weight: bold;">function</span><span style="color: #009900;">&#40;</span>token<span style="color: #009900;">&#41;</span><span style="color: #009900;">&#123;</span>
  saveToken<span style="color: #009900;">&#40;</span>token<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
  <span style="color: #000066; font-weight: bold;">return</span> $.<span style="color: #660066;">get</span><span style="color: #009900;">&#40;</span><span style="color: #3366CC;">'/sessions/last'</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
<span style="color: #009900;">&#125;</span><span style="color: #009900;">&#41;</span>
&nbsp;
<span style="color: #006600; font-style: italic;">// then init session</span>
.<span style="color: #660066;">then</span><span style="color: #009900;">&#40;</span><span style="color: #003366; font-weight: bold;">function</span><span style="color: #009900;">&#40;</span>session<span style="color: #009900;">&#41;</span><span style="color: #009900;">&#123;</span>
  <span style="color: #000066; font-weight: bold;">if</span> <span style="color: #009900;">&#40;</span>session.<span style="color: #660066;">device</span> <span style="color: #339933;">!=</span> currentDevice<span style="color: #009900;">&#41;</span><span style="color: #009900;">&#123;</span>
&nbsp;
    $.<span style="color: #660066;">get</span><span style="color: #009900;">&#40;</span><span style="color: #3366CC;">'/session/ '</span> <span style="color: #339933;">+</span> session.<span style="color: #660066;">id</span> <span style="color: #339933;">+</span> <span style="color: #3366CC;">'/context'</span><span style="color: #009900;">&#41;</span>
    .<span style="color: #660066;">then</span><span style="color: #009900;">&#40;</span><span style="color: #003366; font-weight: bold;">function</span><span style="color: #009900;">&#40;</span>context<span style="color: #009900;">&#41;</span><span style="color: #009900;">&#123;</span>
      loadContext<span style="color: #009900;">&#40;</span><span style="color: #003366; font-weight: bold;">function</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#123;</span>
        startApp<span style="color: #009900;">&#40;</span><span style="color: #003366; font-weight: bold;">function</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#123;</span>
          initUi<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span>
        <span style="color: #009900;">&#125;</span><span style="color: #009900;">&#41;</span>
      <span style="color: #009900;">&#125;</span><span style="color: #009900;">&#41;</span>
    <span style="color: #009900;">&#125;</span><span style="color: #009900;">&#41;</span>
&nbsp;
  <span style="color: #009900;">&#125;</span>
  <span style="color: #000066; font-weight: bold;">else</span> <span style="color: #009900;">&#123;</span>
    startApp<span style="color: #009900;">&#40;</span><span style="color: #003366; font-weight: bold;">function</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#123;</span>
      initUi<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span>
    <span style="color: #009900;">&#125;</span><span style="color: #009900;">&#41;</span>
  <span style="color: #009900;">&#125;</span><span style="color: #009900;">&#125;</span>
<span style="color: #009900;">&#125;</span><span style="color: #009900;">&#41;</span>
&nbsp;
<span style="color: #009900;">&#125;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span></pre></div></div>

<p>Tout ça s’exécute de manière non bloquante (d&#8217;autres fonctions ailleurs dans le programme peuvent s&#8217;exécuter pendant qu&#8217;on attend la réponse du serveur), mais dans l&#8217;ordre de lecture, donc on comprend bien ce qui se passe. Si on veut retirer un bloc, c&#8217;est beaucoup plus facile.</p>
<h2>Comment ça marche à l&#8217;intérieur ?</h2>
<p>Histoire d&#8217;avoir une idée de comment une promise marche, on va faire une implémentation, simpliste et naïve, mais compréhensible, d&#8217;une promesse en Python. Pour rendre l&#8217;API un peu sympa,je vais utiliser <a href="http://sametmax.com/comprendre-les-decorateurs-python-pas-a-pas-partie-1/">les décorateurs.</a></p>

<div class="wp_syntax"><div class="code"><pre class="python" style="font-family:monospace;"><span style="color: #ff7700;font-weight:bold;">class</span> Promise:
&nbsp;
    <span style="color: #808080; font-style: italic;"># La promesse contient une liste de callbacks, donc une liste de fonctions.</span>
    <span style="color: #808080; font-style: italic;"># Pas le résultat des fonctions, mais bien les fonctions elles mêmes,</span>
    <span style="color: #808080; font-style: italic;"># puisque les fonctions sont manipulables en Python.</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;">self</span>.<span style="color: black;">callbacks</span> = <span style="color: black;">&#91;</span><span style="color: black;">&#93;</span>
&nbsp;
    <span style="color: #808080; font-style: italic;"># Point d'entrée pour ajouter un callback à la promesse</span>
    <span style="color: #ff7700;font-weight:bold;">def</span> then<span style="color: black;">&#40;</span><span style="color: #008000;">self</span>, callback<span style="color: black;">&#41;</span>:
        <span style="color: #008000;">self</span>.<span style="color: black;">callbacks</span>.<span style="color: black;">append</span><span style="color: black;">&#40;</span>callback<span style="color: black;">&#41;</span>
&nbsp;
    <span style="color: #808080; font-style: italic;"># Cette méthode est celle qui sera appelée par le code asynchrone</span>
    <span style="color: #808080; font-style: italic;"># quand il reçoit son résultat.</span>
    <span style="color: #ff7700;font-weight:bold;">def</span> resolve<span style="color: black;">&#40;</span><span style="color: #008000;">self</span>, resultat<span style="color: black;">&#41;</span>:
&nbsp;
        <span style="color: #808080; font-style: italic;"># Ici, on obtient le résultat du code asycnhrone, donc on boucle</span>
        <span style="color: #808080; font-style: italic;"># sur les callbacks pour les appeler</span>
        <span style="color: #ff7700;font-weight:bold;">while</span> <span style="color: #008000;">self</span>.<span style="color: black;">callbacks</span>:
            <span style="color: #808080; font-style: italic;"># On retire le premier callback de la liste, et on l'appelle</span>
            <span style="color: #808080; font-style: italic;"># avec le résultat</span>
            resultat = <span style="color: #008000;">self</span>.<span style="color: black;">callbacks</span>.<span style="color: black;">pop</span><span style="color: black;">&#40;</span><span style="color: #ff4500;">0</span><span style="color: black;">&#41;</span><span style="color: black;">&#40;</span>resultat<span style="color: black;">&#41;</span>
&nbsp;
            <span style="color: #808080; font-style: italic;"># Si le resultat est une promesse, on dit à cette nouvelle promesse</span>
            <span style="color: #808080; font-style: italic;"># de nous rappeler quand a elle a reçu ses résultats à elle avant</span>
            <span style="color: #808080; font-style: italic;"># d'aller le reste de nos callbacks a nous : on fusionne les deux</span>
            <span style="color: #808080; font-style: italic;"># promesses :</span>
            <span style="color: #808080; font-style: italic;"># Promesse 1</span>
            <span style="color: #808080; font-style: italic;">#  - callback1</span>
            <span style="color: #808080; font-style: italic;">#  - callback2</span>
            <span style="color: #808080; font-style: italic;">#  - Promesse 2</span>
            <span style="color: #808080; font-style: italic;">#      * callback 1</span>
            <span style="color: #808080; font-style: italic;">#      * callback 2</span>
            <span style="color: #808080; font-style: italic;">#  - callback 3</span>
            <span style="color: #ff7700;font-weight:bold;">if</span> <span style="color: #008000;">isinstance</span><span style="color: black;">&#40;</span>resultat, Promise<span style="color: black;">&#41;</span>:
                resultat.<span style="color: black;">then</span><span style="color: black;">&#40;</span><span style="color: #008000;">self</span>.<span style="color: black;">resolve</span><span style="color: black;">&#41;</span>
                <span style="color: #ff7700;font-weight:bold;">break</span></pre></div></div>

<p>Maintenant, créons un code asynchrone:</p>

<div class="wp_syntax"><div class="code"><pre class="python" style="font-family:monospace;"><span style="color: #ff7700;font-weight:bold;">from</span> <span style="color: #dc143c;">threading</span> <span style="color: #ff7700;font-weight:bold;">import</span> Timer
&nbsp;
<span style="color: #ff7700;font-weight:bold;">def</span> func1<span style="color: black;">&#40;</span>v1<span style="color: black;">&#41;</span>:
    <span style="color: #808080; font-style: italic;"># On dit complètement artificiellement d'afficher le résultat</span>
    <span style="color: #808080; font-style: italic;"># de la fonction dans 3 secondes, sans bloquer histoire d'avoir</span>
    <span style="color: #808080; font-style: italic;"># un peu de nonbloquitude dans notre code et justifier l'asynchrone.</span>
    <span style="color: #ff7700;font-weight:bold;">def</span> callback1<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>v1<span style="color: black;">&#41;</span>
    t = Timer<span style="color: black;">&#40;</span><span style="color: #ff4500;">3</span>, callback1<span style="color: black;">&#41;</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> func2<span style="color: black;">&#40;</span>v2<span style="color: black;">&#41;</span>:
    <span style="color: #808080; font-style: italic;"># Le même, mais pour 2 secondes</span>
    <span style="color: #ff7700;font-weight:bold;">def</span> callback2<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>v2<span style="color: black;">&#41;</span>
    t = Timer<span style="color: black;">&#40;</span><span style="color: #ff4500;">2</span>, callback2<span style="color: black;">&#41;</span>
    t.<span style="color: black;">start</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
&nbsp;
<span style="color: #808080; font-style: italic;"># Deux fonctions normales</span>
<span style="color: #ff7700;font-weight:bold;">def</span> func3<span style="color: black;">&#40;</span>v3<span style="color: black;">&#41;</span>:
    <span style="color: #ff7700;font-weight:bold;">print</span><span style="color: black;">&#40;</span>v3<span style="color: black;">&#41;</span>
&nbsp;
<span style="color: #ff7700;font-weight:bold;">def</span> func4<span style="color: black;">&#40;</span>v4<span style="color: black;">&#41;</span>:
    <span style="color: #ff7700;font-weight:bold;">print</span><span style="color: black;">&#40;</span>v4<span style="color: black;">&#41;</span>
&nbsp;
<span style="color: #808080; font-style: italic;"># Et si on les enchaines...</span>
<span style="color: #ff7700;font-weight:bold;">print</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'Je commence'</span><span style="color: black;">&#41;</span>
func1<span style="color: black;">&#40;</span><span style="color: #ff4500;">1</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;">'Juste après'</span><span style="color: black;">&#41;</span>
func2<span style="color: black;">&#40;</span><span style="color: #ff4500;">2</span><span style="color: black;">&#41;</span>
func3<span style="color: black;">&#40;</span><span style="color: #ff4500;">3</span><span style="color: black;">&#41;</span>
func4<span style="color: black;">&#40;</span><span style="color: #ff4500;">4</span><span style="color: black;">&#41;</span>
&nbsp;
<span style="color: #808080; font-style: italic;"># ... le résultat est bien désordonné :</span>
&nbsp;
<span style="color: #808080; font-style: italic;">## Je commence</span>
<span style="color: #808080; font-style: italic;">## Juste après</span>
<span style="color: #808080; font-style: italic;">## 3</span>
<span style="color: #808080; font-style: italic;">## 4</span>
<span style="color: #808080; font-style: italic;">## 2</span>
<span style="color: #808080; font-style: italic;">## 1</span></pre></div></div>

<p>Parfois c&#8217;est ce que l&#8217;on veut, que les choses s’exécutent dans le désordre, sans bloquer.</p>
<p>Mais quand on a des fonctions qui dépendent les unes des autres, au milieu d&#8217;un code asynchrone, on veut qu&#8217;elles se transmettent le résultat les unes aux autres au bon moment. Pour cela, utilisons notre promesse :</p>

<div class="wp_syntax"><div class="code"><pre class="python" style="font-family:monospace;"><span style="color: #ff7700;font-weight:bold;">from</span> <span style="color: #dc143c;">threading</span> <span style="color: #ff7700;font-weight:bold;">import</span> Timer
&nbsp;
&nbsp;
<span style="color: #808080; font-style: italic;"># La mise en place de promesses suppose que le code </span>
<span style="color: #808080; font-style: italic;"># écrit en fasse explicitement usage. Notre code est</span>
<span style="color: #808080; font-style: italic;"># définitivement lié à cette manière de faire.</span>
&nbsp;
<span style="color: #ff7700;font-weight:bold;">def</span> func1<span style="color: black;">&#40;</span>v1<span style="color: black;">&#41;</span>:
    <span style="color: #808080; font-style: italic;"># Notre fonction doit créer la promesse et la retourner</span>
    p = Promise<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
    <span style="color: #ff7700;font-weight:bold;">def</span> callback1<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>v1<span style="color: black;">&#41;</span>
        <span style="color: #808080; font-style: italic;"># Dans le callback, elle doit dire quand la promesse est tenue</span>
        p.<span style="color: black;">resolve</span><span style="color: black;">&#40;</span>v1<span style="color: black;">&#41;</span>
    t = Timer<span style="color: black;">&#40;</span><span style="color: #ff4500;">3</span>, callback1<span style="color: black;">&#41;</span>
    t.<span style="color: black;">start</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
    <span style="color: #ff7700;font-weight:bold;">return</span> p
&nbsp;
<span style="color: #808080; font-style: italic;"># On lance la première fonction.</span>
<span style="color: #ff7700;font-weight:bold;">print</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'Je commence'</span><span style="color: black;">&#41;</span>
promise = func1<span style="color: black;">&#40;</span><span style="color: #ff4500;">1</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;">'Juste après'</span><span style="color: black;">&#41;</span>
&nbsp;
<span style="color: #808080; font-style: italic;"># On ajoute des callbacks à notre promesse.</span>
&nbsp;
@promise.<span style="color: black;">then</span>
<span style="color: #ff7700;font-weight:bold;">def</span> func2<span style="color: black;">&#40;</span>v2<span style="color: black;">&#41;</span>:
    p = Promise<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
    <span style="color: #ff7700;font-weight:bold;">def</span> callback2<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>:
        <span style="color: #808080; font-style: italic;"># Pour justifier l’enchainement des fonctions, on fait en sorte que</span>
        <span style="color: #808080; font-style: italic;"># chaque fonction attend le résultat de la précédente, et</span>
        <span style="color: #808080; font-style: italic;"># l'incrémente de 1.</span>
        <span style="color: #ff7700;font-weight:bold;">print</span><span style="color: black;">&#40;</span>v2 + <span style="color: #ff4500;">1</span><span style="color: black;">&#41;</span>
        p.<span style="color: black;">resolve</span><span style="color: black;">&#40;</span>v2 + <span style="color: #ff4500;">1</span><span style="color: black;">&#41;</span>
    t = Timer<span style="color: black;">&#40;</span><span style="color: #ff4500;">2</span>, callback2<span style="color: black;">&#41;</span>
    t.<span style="color: black;">start</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
    <span style="color: #808080; font-style: italic;"># Ce callback retourne lui-même une promesse, qui sera fusionnée</span>
    <span style="color: #ff7700;font-weight:bold;">return</span> p
&nbsp;
<span style="color: #808080; font-style: italic;"># Ces callbacks ne retournent pas de promesses, et seront chainés</span>
<span style="color: #808080; font-style: italic;"># normalement</span>
@promise.<span style="color: black;">then</span>
<span style="color: #ff7700;font-weight:bold;">def</span> func3<span style="color: black;">&#40;</span>v3<span style="color: black;">&#41;</span>:
    <span style="color: #ff7700;font-weight:bold;">print</span><span style="color: black;">&#40;</span>v3 + <span style="color: #ff4500;">1</span><span style="color: black;">&#41;</span>
    <span style="color: #ff7700;font-weight:bold;">return</span> v3 + <span style="color: #ff4500;">1</span>
&nbsp;
@promise.<span style="color: black;">then</span>
<span style="color: #ff7700;font-weight:bold;">def</span> func4<span style="color: black;">&#40;</span>v4<span style="color: black;">&#41;</span>:
    <span style="color: #ff7700;font-weight:bold;">print</span><span style="color: black;">&#40;</span>v4 + <span style="color: #ff4500;">1</span><span style="color: black;">&#41;</span>
&nbsp;
<span style="color: #808080; font-style: italic;"># Nos fonctions s'exécutent dans le bon ordre, mais bien de manière</span>
<span style="color: #808080; font-style: italic;"># asynchrone par rapport au reste du programme.</span>
&nbsp;
<span style="color: #808080; font-style: italic;">## Je commence</span>
<span style="color: #808080; font-style: italic;">## Juste après</span>
<span style="color: #808080; font-style: italic;">## 1</span>
<span style="color: #808080; font-style: italic;">## 2</span>
<span style="color: #808080; font-style: italic;">## 3</span>
<span style="color: #808080; font-style: italic;">## 4</span></pre></div></div>

<p>Notez bien :</p>
<ul>
<li>Le résultat &#8220;1&#8243; n&#8217;apparait que trois secondes après &#8220;Juste après&#8221;. Les fonctions sont donc bien non bloquantes.</li>
<li>Le resultat &#8220;2&#8243; apparait deux secondes après &#8220;1&#8243;: c&#8217;est aussi asynchrone, MAIS, n&#8217;est lancé que quand la première fonction a terminé son travail.</li>
<li>La deuxième fonction retourne une promesse, qui est fusionnée: tous ses callbacks vont s&#8217;exécuter en file avant que <code>func3</code> soit lancé. </li>
</ul>
<p>Évidement, n&#8217;utilisez pas cette implémentation de promise à la maison, c&#8217;est pédagogique. Ça ne gère pas les erreurs, ni le cas où le callback est enregistré après l&#8217;arrivée du résultat, et tout un tas d&#8217;autres cas tordus.</p>
<h2>Syntaxe alternative</h2>
<p>En Python, beaucoup de frameworks ont une approche plus agréable pour gérer les promesses à grand coup de <a href="http://sametmax.com/comment-utiliser-yield-et-les-generateurs-en-python/">yield</a>. Twisted fait ça avec son <code>@inlineCallback</code>, <code>asyncio</code> avec <code>@coroutine</code>. C&#8217;est juste du sucre syntaxique pour vous rendre la vie plus facile.</p>
<p>Il s&#8217;agit de transformer une fonction en générateur, et à chaque fois qu&#8217;on appelle <code>yield</code> sur une promesse, elle est fusionnée avec la précédente. Ça donne presque l&#8217;impression d&#8217;écrire un code bloquant normal :</p>

<div class="wp_syntax"><div class="code"><pre class="python" style="font-family:monospace;"><span style="color: #808080; font-style: italic;"># un appel de fonction asyncrone typique de twisted</span>
@inlineCallback
<span style="color: #ff7700;font-weight:bold;">def</span> une_fonction<span style="color: black;">&#40;</span>data<span style="color: black;">&#41;</span>:
  data = <span style="color: #ff7700;font-weight:bold;">yield</span> func1<span style="color: black;">&#40;</span>data<span style="color: black;">&#41;</span>
  data = <span style="color: #ff7700;font-weight:bold;">yield</span> func2<span style="color: black;">&#40;</span>data<span style="color: black;">&#41;</span>
  data = <span style="color: #ff7700;font-weight:bold;">yield</span> func3<span style="color: black;">&#40;</span>data<span style="color: black;">&#41;</span>
&nbsp;
une_fonction<span style="color: black;">&#40;</span>truc<span style="color: black;">&#41;</span></pre></div></div>

<p>Les fonctions 1, 2 et 3 vont ainsi être appelées de manière asynchrone par rapport au reste du programme, mais bien s’enchainer les unes à la suite des autres.</p>
<p>Ouai, tout ce bordel parce que l&#8217;asynchrone, c&#8217;est dur, donc on essaye de le faire ressembler à du code synchrone, qui lui est facile.</p>
 <p><a href="http://sametmax.com/?flattrss_redirect&amp;id=10418&amp;md5=adf0568366c6b0f00340cf1accd3fc68" title="Flattr" target="_blank"><img src="media/fc229d00.flattr-badge-large.png" data-original-source="http://sametmax.com/wp-content/plugins/flattr/img/flattr-badge-large.png" alt="flattr this!"/></a></p>


feed_id: http://sametmax.com/?p=10438
title: La protection CSRF de Django et les requêtes Ajax
uri: La-protection-CSRF-de-Django-et-les-requêtes-Ajax
url: http://sametmax.com/la-protection-csrf-de-django-et-les-requetes-ajax/
content:
<p>La protection contre les <a href="https://fr.wikipedia.org/wiki/CSRF">attaques CSRF</a> est dans le top 10 des erreurs les plus chiantes en Django, main dans la main avec les fichiers statiques qui ne marchent pas, les URL qui ne matchent pas et les CBV qui nheuuuu, juste pas.</p>
<p>Une fois qu&#8217;on a compris le principe, ça va pour la prog normal, mais un jour on a besoin de faire un POST en Ajax, et on se retrouve avec une erreur invisible. Après avoir dégainé Firebug, on comprend qu&#8217;on a une 403 forbidden, et votre cerveau finit (la durée galérienne est plus ou moins longue selon les profiles, les heures de sommeil et les phases de la lune) par réaliser qu&#8217;on n&#8217;a pas envoyé le token CSRF. Merde.</p>
<p>C&#8217;est là que généralement les gens sortent du <code>@csrf_exempt</code>, ou carrément, en finissent avec cette solution radicale :</p>

<div class="wp_syntax"><div class="code"><pre class="python" style="font-family:monospace;">MIDDLEWARE_CLASSES = <span style="color: black;">&#40;</span>
    <span style="color: #483d8b;">'django.middleware.gzip.GZipMiddleware'</span>,
    <span style="color: #483d8b;">'django.middleware.common.CommonMiddleware'</span>,
    <span style="color: #483d8b;">'django.contrib.sessions.middleware.SessionMiddleware'</span>,
    <span style="color: #483d8b;">'django.middleware.locale.LocaleMiddleware'</span>,
    <span style="color: #808080; font-style: italic;"># 'django.middleware.csrf.CsrfViewMiddleware',</span>
    <span style="color: #483d8b;">'django.contrib.auth.middleware.AuthenticationMiddleware'</span>,
    <span style="color: #483d8b;">'django.contrib.messages.middleware.MessageMiddleware'</span>,
<span style="color: black;">&#41;</span></pre></div></div>

<p>Mais c&#8217;est quand même dommage d&#8217;en arriver là alors qu&#8217;on peut le faire proprement.</p>
<p>D&#8217;abord, le token est sauvegardé dans un cookie. Il faut donc le récupérer.</p>

<div class="wp_syntax"><div class="code"><pre class="javascript" style="font-family:monospace;"><span style="color: #006600; font-style: italic;">// ue petite fonction pour récupérer la valeur d'un cookie,</span>
<span style="color: #006600; font-style: italic;">// puisque bien entendu, comme toutes les APIS javascripts,</span>
<span style="color: #006600; font-style: italic;">// les choses qu'on fait le plus souvent ne sont pas incluses</span>
<span style="color: #006600; font-style: italic;">// en natif. Oui je suis aigri.</span>
<span style="color: #003366; font-weight: bold;">function</span> getCookie<span style="color: #009900;">&#40;</span><span style="color: #000066;">name</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
    <span style="color: #000066; font-weight: bold;">if</span> <span style="color: #009900;">&#40;</span>document.<span style="color: #660066;">cookie</span> <span style="color: #339933;">&amp;&amp;</span> document.<span style="color: #660066;">cookie</span> <span style="color: #339933;">!=</span> <span style="color: #3366CC;">''</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
        <span style="color: #003366; font-weight: bold;">var</span> cookies <span style="color: #339933;">=</span> document.<span style="color: #660066;">cookie</span>.<span style="color: #660066;">split</span><span style="color: #009900;">&#40;</span><span style="color: #3366CC;">';'</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
        <span style="color: #000066; font-weight: bold;">for</span> <span style="color: #009900;">&#40;</span><span style="color: #003366; font-weight: bold;">var</span> i <span style="color: #339933;">=</span> <span style="color: #CC0000;">0</span><span style="color: #339933;">;</span> i <span style="color: #339933;">&lt;</span> cookies.<span style="color: #660066;">length</span><span style="color: #339933;">;</span> i<span style="color: #339933;">++</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
            <span style="color: #003366; font-weight: bold;">var</span> cookie <span style="color: #339933;">=</span> jQuery.<span style="color: #660066;">trim</span><span style="color: #009900;">&#40;</span>cookies<span style="color: #009900;">&#91;</span>i<span style="color: #009900;">&#93;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
            <span style="color: #000066; font-weight: bold;">if</span> <span style="color: #009900;">&#40;</span>cookie.<span style="color: #660066;">substring</span><span style="color: #009900;">&#40;</span><span style="color: #CC0000;">0</span><span style="color: #339933;">,</span> <span style="color: #000066;">name</span>.<span style="color: #660066;">length</span> <span style="color: #339933;">+</span> <span style="color: #CC0000;">1</span><span style="color: #009900;">&#41;</span> <span style="color: #339933;">==</span> <span style="color: #009900;">&#40;</span><span style="color: #000066;">name</span> <span style="color: #339933;">+</span> <span style="color: #3366CC;">'='</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
                <span style="color: #000066; font-weight: bold;">return</span> decodeURIComponent<span style="color: #009900;">&#40;</span>cookie.<span style="color: #660066;">substring</span><span style="color: #009900;">&#40;</span><span style="color: #000066;">name</span>.<span style="color: #660066;">length</span> <span style="color: #339933;">+</span> <span style="color: #CC0000;">1</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
                <span style="color: #000066; font-weight: bold;">break</span><span style="color: #339933;">;</span>
            <span style="color: #009900;">&#125;</span>
        <span style="color: #009900;">&#125;</span>
    <span style="color: #009900;">&#125;</span>
<span style="color: #009900;">&#125;</span></pre></div></div>

<p>Sinon, pour les fainéants, il y a y a un <a href="https://github.com/carhartl/jquery-cookie">plugin jquery</a> qui permet de faire <code>$.cookie('nom')</code>.</p>
<p>Ensuite, on attache cette valeur en header avec toutes les requêtes POST. Comme ça, plus besoin de l&#8217;inclure manuellement, on peut faire ses requêtes sans y penser.</p>
<p>Avec jQuery :</p>

<div class="wp_syntax"><div class="code"><pre class="javascript" style="font-family:monospace;">$.<span style="color: #660066;">ajaxSetup</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#123;</span>
    <span style="color: #006600; font-style: italic;">// fonction appelée avant d'envoyer une requête AJAX</span>
    beforeSend<span style="color: #339933;">:</span> <span style="color: #003366; font-weight: bold;">function</span><span style="color: #009900;">&#40;</span>xhr<span style="color: #339933;">,</span> settings<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
         <span style="color: #006600; font-style: italic;">// on ajoute le header que si la requête est pour le site en cours</span>
         <span style="color: #006600; font-style: italic;">// (URL relative) et est de type POST</span>
         <span style="color: #000066; font-weight: bold;">if</span> <span style="color: #009900;">&#40;</span><span style="color: #339933;">!/^</span>https<span style="color: #339933;">?:</span>.<span style="color: #339933;">*/</span>.<span style="color: #660066;">test</span><span style="color: #009900;">&#40;</span>settings.<span style="color: #660066;">url</span><span style="color: #009900;">&#41;</span>  <span style="color: #339933;">&amp;&amp;</span> settings.<span style="color: #660066;">type</span> <span style="color: #339933;">==</span> <span style="color: #3366CC;">&quot;POST&quot;</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
             <span style="color: #006600; font-style: italic;">// attachement du token dans le header</span>
             xhr.<span style="color: #660066;">setRequestHeader</span><span style="color: #009900;">&#40;</span><span style="color: #3366CC;">&quot;X-CSRFToken&quot;</span><span style="color: #339933;">,</span>  getCookie<span style="color: #009900;">&#40;</span><span style="color: #3366CC;">'csrftoken'</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
         <span style="color: #009900;">&#125;</span>
     <span style="color: #009900;">&#125;</span>
<span style="color: #009900;">&#125;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span></pre></div></div>

<p>Avec AngularJs :</p>

<div class="wp_syntax"><div class="code"><pre class="javascript" style="font-family:monospace;"><span style="color: #006600; font-style: italic;">// interception de la configuration du provider HTTP</span>
<span style="color: #006600; font-style: italic;">// qui possède un mécanisme déjà tout prêt pour ça</span>
votre_app.<span style="color: #660066;">config</span><span style="color: #009900;">&#40;</span><span style="color: #003366; font-weight: bold;">function</span><span style="color: #009900;">&#40;</span>$httpProvider<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
    $httpProvider.<span style="color: #660066;">defaults</span>.<span style="color: #660066;">xsrfCookieName</span> <span style="color: #339933;">=</span> <span style="color: #3366CC;">'csrftoken'</span><span style="color: #339933;">;</span>
    $httpProvider.<span style="color: #660066;">defaults</span>.<span style="color: #660066;">xsrfHeaderName</span> <span style="color: #339933;">=</span> <span style="color: #3366CC;">'X-CSRFToken'</span><span style="color: #339933;">;</span>
<span style="color: #009900;">&#125;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span></pre></div></div>

<p>Attention, si l&#8217;app n&#8217;est pas servie par Django (template statique avec uniquement des appels Ajax), il faut faire au moins un GET avant de faire son premier POST afin d&#8217;obtenir le cookie.</p>
 <p><a href="http://sametmax.com/?flattrss_redirect&amp;id=10438&amp;md5=322eb319a567675b1d6fb1e8aed01281" title="Flattr" target="_blank"><img src="media/fc229d00.flattr-badge-large.png" data-original-source="http://sametmax.com/wp-content/plugins/flattr/img/flattr-badge-large.png" alt="flattr this!"/></a></p>


feed_id: http://sametmax.com/?p=10433
title: assert “а” == “a” # lol
uri: assert-а-a-lol
url: http://sametmax.com/assert-%d0%b0-a-lol/
content:
<p>Python 3, le bonheur d&#8217;avoir UTF8 comme encoding par défaut !</p>
<p>En plus, ça ajoute un petit potentiel de lulz.</p>
<p>Par exemple, ceci marche très bien :</p>

<div class="wp_syntax"><div class="code"><pre class="python" style="font-family:monospace;"><span style="color: #ff7700;font-weight:bold;">def</span> <span style="color: #dc143c;">test</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;">'Youpi'</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;">'Youpi'</span><span style="color: black;">&#41;</span></pre></div></div>

<p>Et ceci&#8230;</p>

<div class="wp_syntax"><div class="code"><pre class="python" style="font-family:monospace;"><span style="color: #ff7700;font-weight:bold;">def</span> <span style="color: #dc143c;">test</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;">'Arg'</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;">'Arg'</span><span style="color: black;">&#41;</span></pre></div></div>

<p>&#8230; provoque une syntaxe error !</p>

<div class="wp_syntax"><div class="code"><pre class="python" style="font-family:monospace;">  File <span style="color: #483d8b;">&quot;&lt;stdin&gt;&quot;</span>, line <span style="color: #ff4500;">2</span>
       <span style="color: #ff7700;font-weight:bold;">print</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'Arg'</span><span style="color: black;">&#41;</span>
    ^
<span style="color: #008000;">SyntaxError</span>: invalid character <span style="color: #ff7700;font-weight:bold;">in</span> identifier</pre></div></div>

<p>La raison est que la première ligne print(&#8216;Arg&#8217;) contient le caractère unicode U+0020, qui est un espace inimprimable, mais pas le même que l&#8217;ascii :)</p>
<p>Bon, vous allez-me dire, on pouvait déjà mélanger les tabs et les espaces, s&#8217;amuser avec les espaces insécables, ou simplement déclarer manuellement l&#8217;encoding et faire pareil&#8230;</p>
<p>Allons plus loin. Saviez-vous qu&#8217;on pouvait utiliser des caractères non-ASCII <strong>dans les identifiants</strong> en Python 3 ?</p>
<p>Ceci est donc parfaitement valide :</p>

<div class="wp_syntax"><div class="code"><pre class="python" style="font-family:monospace;">éôà = <span style="color: #ff4500;">1</span></pre></div></div>

<p>Ce qui invite bien entendu a des choses tout à fait amusantes comme :</p>

<div class="wp_syntax"><div class="code"><pre class="python" style="font-family:monospace;"><span style="color: #ff7700;font-weight:bold;">def</span> аttention<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;">'!'</span><span style="color: black;">&#41;</span>
&nbsp;
<span style="color: #66cc66;">&gt;&gt;&gt;</span> аttention<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
<span style="color: #66cc66;">!</span>
<span style="color: #66cc66;">&gt;&gt;&gt;</span> attention<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
Traceback <span style="color: black;">&#40;</span>most recent call last<span style="color: black;">&#41;</span>:
  File <span style="color: #483d8b;">&quot;&lt;stdin&gt;&quot;</span>, line <span style="color: #ff4500;">1</span>, <span style="color: #ff7700;font-weight:bold;">in</span> <span style="color: #66cc66;">&lt;</span>module<span style="color: #66cc66;">&gt;</span>
<span style="color: #008000;">NameError</span>: name <span style="color: #483d8b;">'attention'</span> <span style="color: #ff7700;font-weight:bold;">is</span> <span style="color: #ff7700;font-weight:bold;">not</span> defined</pre></div></div>

<p>En effet, j&#8217;ai utilisé le caractère &#8220;а&#8221; cyrilique comme première lettre, et non le &#8220;a&#8221; ASCII. Ils n&#8217;ont pas le même code :</p>

<div class="wp_syntax"><div class="code"><pre class="python" style="font-family:monospace;"><span style="color: #66cc66;">&gt;&gt;&gt;</span> <span style="color: #008000;">ord</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">&quot;а&quot;</span><span style="color: black;">&#41;</span>, <span style="color: #008000;">ord</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">&quot;a&quot;</span><span style="color: black;">&#41;</span>
<span style="color: black;">&#40;</span><span style="color: #ff4500;">1072</span>, <span style="color: #ff4500;">97</span><span style="color: black;">&#41;</span>
<span style="color: #66cc66;">&gt;&gt;&gt;</span> <span style="color: #483d8b;">&quot;а&quot;</span> == <span style="color: #483d8b;">&quot;a&quot;</span>
<span style="color: #008000;">False</span></pre></div></div>

<p>Quand j&#8217;étais au lycée, une bonne blague qu&#8217;on faisait aux profs était de faire une capture d&#8217;écran du bureau de leur ordi, la mettre en fond d&#8217;écran, virer les icônes et régler la barrer des tâches pour se cacher automatiquement. Ils pensaient que leur machine était freezée, et un reboot ne changeait rien. Des heures à s&#8217;arracher les cheveux.</p>
<p>Avec les identifiants unicodes je pense qu&#8217;on peut retrouver cette merveilleuse créativité avec ses collègues.</p>
 <p><a href="http://sametmax.com/?flattrss_redirect&amp;id=10433&amp;md5=e35a47ba5a1bb5cc1a1857a2f4294990" title="Flattr" target="_blank"><img src="media/fc229d00.flattr-badge-large.png" data-original-source="http://sametmax.com/wp-content/plugins/flattr/img/flattr-badge-large.png" alt="flattr this!"/></a></p>


feed_id: http://sametmax.com/?p=10455
title: Les graves problèmes de la vie en Thaïlande
uri: Les-graves-problèmes-de-la-vie-en-Thaïlande
url: http://sametmax.com/les-graves-problemes-de-la-vie-en-thailande/
content:
<p><em>Quelques morceaux choisis de ces dernières semaines. Les > sont des monologues, les &#8211; sont des dialogues.</em></p>
<p>> ce soir une amie de ma cop veut tourner une scène meuf/meuf pour son pigeon, je suis chargé de trouver une meuf partante, j&#8217;ai un budget de 1500 TBH payé par elle et j&#8217;ai le droit de me faire sucer par la meuf<br />
> puis je dois filmer<br />
> elle se fait 20 000 TBH en vendant sa scène lesbienne<br />
> faut que je trouve une bouffeuse de minou<br />
> je me demande si c&#8217;est pas mieux de passer un coup de fil à requin<br />
> elle a fait ses preuves<br />
> t&#8217;as son num?</p>
<hr />
<p>- Hi, I need to change my mirror glass and wheel<br />
- Ok, 600 BHT (14€).<br />
- I&#8217;ll be back tomorrow  ?<br />
- No. Ready. 20 minutes.</p>
<hr />
<p>> les thais dans les restos, c&#8217;est toujours leur premier jour. Ils peuvent avoir travaillé 10 ans, ça change rien. Ils vont se coucher, le lendemain, tu as un thai tout neuf.</p>
<hr />
<p>- Bon alors tu vas tout droit, vers la colline, et quand tu atteins un scouter rouge garé, tu prends à gauche.<br />
- &#8230;</p>
<hr />
<p>> Bon les photos du DJ qui mixe avec sa bite, on les mets dans &#8220;musique&#8221; ou dans &#8220;amateur&#8221; ?</p>
<hr />
<p>- Non, ma nouvelle meuf m&#8217;a racheté à l&#8217;ancienne.<br />
- T&#8217;a racheté ?<br />
- Oui, elle lui a donné un paquet de drogue pour ne plus me voir.<br />
- Ta nouvelle meuf c&#8217;est pas celle en taule pour trafique de drogue et du coup tu dois garder ses clebs ?<br />
- Yep. Son avocat me demande 350 000 BHT pour la faire sortir ou elle reste 10 ans.</p>
<hr />
<p>> Donc la grosse, elle se pointe dès qu&#8217;elle voit les photos du bbq sur facebook, elle bouffe tout, elle se met la race avec notre vodka, et une fois qu&#8217;elle voit que sa copine se fait chauffer dans la piscine, elle l&#8217;embarque et se casse.<br />
> Et on la connaissais même pas.</p>
<hr />
<p>- Le mec de ce resto, il fait de la haute cuisine française.<br />
- Mais comment il s&#8217;en sort ?<br />
- L&#8217;astuce, c&#8217;est qu&#8217;il a deux cartes. Une pour les connards de touristes. Dès qu&#8217;il voit un australien ou un russe, il lui sort le menu frite, hamburger et bière. Et quand les compatriotes arrivent, il nous file le coq au vin et la pana-cota maison.</p>
<hr />
<p>17h > Bon parlons des choses importantes : ou-est-ce qu&#8217;on bouffe ? Fuji ? Batcave ? 101 ? BBQ ? Marché ? Da Moreno ? Buffet sushi ? Samsha ? Suédois ? Coffee mania ? Halal ?<br />
20h> Bon, ça fait 3 heures qu&#8217;on débat, je tranche. Batcave. Ou peut être 101. Quoique je me ferais bien une pizza. C&#8217;est con que le mec des buritos ait fermé. L&#8217;indien c&#8217;est trop lourd, et au 101 ils ont une choucroute&#8230;</p>
<hr />
<p>> Ils ont ouvert un Hooters en centre ville ? C&#8217;est idiot, c&#8217;est comme tenir un stand de pêche aux canards en face de Disneyland.</p>
 <p><a href="http://sametmax.com/?flattrss_redirect&amp;id=10455&amp;md5=23c767a98c963afd2991bb33033fa7b6" title="Flattr" target="_blank"><img src="media/fc229d00.flattr-badge-large.png" data-original-source="http://sametmax.com/wp-content/plugins/flattr/img/flattr-badge-large.png" alt="flattr this!"/></a></p>


feed_id: http://sametmax.com/?p=10445
title: Comment trouve-t-on des clients en freelance ?
uri: Comment-trouve-t-on-des-clients-en-freelance
url: http://sametmax.com/comment-trouve-t-on-des-clients-en-freelance/
content:
<p>Ça faisait longtemps que je n&#8217;avais pas publié le courrier des lecteurs. Voici un mail que j&#8217;ai reçu et la réponse envoyée (avec une ou deux corrections).</p>
<blockquote><p>Comment trouve-t-on des clients en freelance ? Vous avez surfé sur le réseau du job précédent, ou la demande est telle qu&#8217;il suffit de s&#8217;inscrire en tant que dev python dans les pages jaunes ?</p></blockquote>
<p><em>Hello,</p>
<p>Je depop les emails datant de mars en juin :)</p>
<p>Pour trouver des clients en tant que freelance :</p>
<p>- utiliser le carnet d&#8217;adresse de sa boîte précédente. Ça suppose un travail régulier à garder le contact avec les interlocuteurs qu&#8217;on a avec les clients externes, demander les numéros/emails directes, les postes, etc. L&#8217;important est de bien être transparent et de ne pas essayer de choper des contrats qui auraient été donnés à son ancienne boîte. Il faut être un complément sur les projets en cours, ou prendre les projets que les autres ne prennent pas. Ça ne peut pas se faire a posteriori, ça se prépare pendant son séjour dans la boîte, et ça suppose une démarche semi-commerciale une fois qu&#8217;on est dehors.</p>
<p>- aller aux événements/lieux sociaux tech. Tout ce qui est conférences (type la cantine, NUMA), salons (type PyConFR, Djangocong), soirées à thème (on peut les organiser soi-même), petit dej entrepreneur (type open coffee), associations (type hackerspace, fablab). Faire sa review, découvrir de nouvelles technos et les partager avec les autres (proposer une présentation, un tuto, un workshop, etc), ça créé des liens.</p>
<p>- créer un site, avec son CV, un tweeter, et scanner régulièrement la recherche tweeter pour des demandes. Il y en a une chiée.</p>
<p>- faire toutes les semaines un tour des annonces sur les dizaines de sites d&#8217;offre d&#8217;emploi. Même si une offre ne demande pas un freelance, proposer quand même. Même si l&#8217;offre est à l&#8217;étranger (proposer de bouger, ou télétravail). On finit toujours pas trouver un truc, mais même quand on en trouve pas, ça fait des contacts. Google est ton ami. Les RSS aussi. Tu es dev, dev un script qui ratisse large et récupère pour toi toutes ces infos et en fait la synthèse.</p>
<p>- prétendre qu&#8217;on est freelance depuis longtemps. Demander à des amis de mentir pour soit en ce faisant passé pour un référent dans un boîte et donner son numéro. Je suis très sérieux.</p>
<p>- ton compte github peut être un bon CV, le mettre en avant.</p>
<p>- on est pas obligé de quitter sa boîte pour être freelance. Le status auto entrepreneur est cumulable avec un travail à plein temps (prendre des missions courtes et travailler le we) ou mi-temps (pour se lancer). On est pas obliger de faire le grand saut d&#8217;un coup.</p>
<p>@+</p>
<p>Sam</em></p>
<p>On entend souvent dire &#8220;construisez votre réseau&#8221;, &#8220;faites marcher votre réseau&#8221;, mais la réalité c&#8217;est que la plupart des gens n&#8217;en ont pas et ne savent pas comment en construire un. C&#8217;est pour ça qu&#8217;il faut attaquer sur deux tableaux : les annonces en ligne (assez facile, contrats moins sympas, mais permet de ternir à court terme et boucher les trous) et les événements sociaux (plus dur, mais meilleurs résultats sur le long terme).</p>
<p>Si vous arrivez à travailler pour une niche (informatique pour pharmaceutique, gestion de projet industriel automobile, booking, etc), il est beaucoup plus facile de démarcher ensuite dans cette niche. N&#8217;hésitez donc pas au début à vous spécialiser dedans.</p>
<p>Ça fait écho à l&#8217;article <a href="http://sametmax.com/ou-trouver-un-job-ou-une-mission-python-django/">Où trouver un Job ou une mission Python / Django ?</a>, même si il faudrait que je fasse un dossier complet là dessus un jour avec :</p>
<ul>
<li>Où et comment chercher les annonces.</li>
<li>Comment créer un CV.</li>
<li>Comment établir des contacts commerciaux aux événements sociaux.</li>
<li>Contacter un client potentiel/</li>
<li>Comment mener le premier entretient avec un client, puis le second.</li>
<li>Comment conclure avec sa proposition commerciale</li>
<li>Comment faire sa veille techno et rendre son profile toujours plus intéressant.</li>
<li>Comment se mettre en avant en général et donner envie en tant que dev.</li>
<li>Comment établir un devis, facturer, combien, pour quoi, etc.</li>
<li>Comment recruter un dev.</li>
<li>Comment gérer un dev.</li>
<li>Comment gérer un projet : côté dev et côté client.</li>
</ul>
<p>Comme d&#8217;hab, tout ceci est prévu pour après 2038, un jeudi, vers 16h47.</p>
<p>Mais ça serait intéressant, surtout que sur le Web ont dit à tout le monde de jouer franc jeu, d&#8217;être un bon garçon, etc, alors que clairement c&#8217;est un jeu de dupe et vous serez gagnant en trichant. Pas en bossant pas, attention, faut fournir le taff pour lequel on est payé, mais &#8220;soyez-vous même&#8221; est le conseil le plus débile que le cinéma et votre maman vous ait donné quand il s&#8217;agit de maximiser ses chances de plaire (dans le job et dans d&#8217;autres domaines).</p>
 <p><a href="http://sametmax.com/?flattrss_redirect&amp;id=10445&amp;md5=07c1866cb583b5a7f1076cf47b267137" title="Flattr" target="_blank"><img src="media/fc229d00.flattr-badge-large.png" data-original-source="http://sametmax.com/wp-content/plugins/flattr/img/flattr-badge-large.png" alt="flattr this!"/></a></p>


feed_id: http://sametmax.com/?p=10476
title: CLOC it
uri: CLOC-it
url: http://sametmax.com/cloc-it/
content:
<p>CLOC est un petit prog perl qui retourne des stats sur le code qu&#8217;on a dans un dossier.</p>
<p>Sur ubuntu, ça s&#8217;installe avec un <code>sudo apt-get install cloc</code>, et un <code>cloc /home/sam/work</code> plus tard, on obtient un rapport sur le nombre de fichiers / lignes de code par langage :</p>
<pre>
---------------------------------------------------------
Language            files      blank    comment      code
---------------------------------------------------------
HTML                10585     392921      49870   2909814
Javascript           1497     118180     220836    656645
Python               3132     104173     129888    517995
CSS                   399      23324      10117    164944
XML                  2860       7435      16148     89942
Java                  194       7364      34214     30960
XSLT                   83       3902       1992     25842
Bourne Shell           91       1998       2511     16263
C                      48       2824       4020     11810
C/C++ Header          167       4256       7775     10015
SQL                    59       1640       1347      8961
LESS                   50       1371       1308      5884
C++                    35       1244        782      5772
make                   41        806        283      3691
QML                    56        413        906      2458
Visualforce Page       17        175         22      1253
Bourne Again Shell     17        109        145       883
m4                      2         99         22       707
SASS                   20        127         41       596
ActionScript            1         71        121       553
DOS Batch               8         63         19       359
DTD                     5        159        280       355
PHP                     2         58        250       315
D                       2         32         -2       171
CMake                   4         28         11       124
Ruby                    5         37          9       120
CoffeeScript            1         30         10        95
Teamcenter def          1          0          0        91
XSD                     3         10          0        78
YAML                    5          7          0        49
Arduino Sketch          1         17         27        41
IDL                     2         12          0        38
MATLAB                  1         21         62        35
C Shell                 1          8          6        18
Visual Basic            1          0          0        18
ASP.Net                 1          4          0         9
---------------------------------------------------------
SUM:                19397     672918     483020   4466904
---------------------------------------------------------
</pre>
<p>Pour le HTML, on a compris que j&#8217;étais un dev très orienté Web. Mais, horreur, coderai-je plus de Javascript que de Python ?</p>
<p>Non, je vous rassure. C&#8217;est juste que mon dossier de travail contient moult documentation Web dumpées pour être consultées hors ligne, de nombreuses libs JS dupliquées (j&#8217;aime pas les CDN) et aucun de mes virtualenv. </p>
<p>Rien que la doc d&#8217;angular :</p>

<div class="wp_syntax"><div class="code"><pre class="bash" style="font-family:monospace;"><span style="color: #c20cb9; font-weight: bold;">find</span> <span style="color: #660033;">-iname</span> <span style="color: #ff0000;">&quot;*.js&quot;</span>  <span style="color: #000000; font-weight: bold;">|</span> <span style="color: #c20cb9; font-weight: bold;">grep</span> angular <span style="color: #000000; font-weight: bold;">|</span> <span style="color: #c20cb9; font-weight: bold;">wc</span>
<span style="color: #000000;">632</span></pre></div></div>

<p>On notera qu&#8217;il y a deux fois plus de fichiers Python (3132 c&#8217;est pas énorme quand on y pense) que de fichiers JS, mais un peu moins de lignes malgré tout, ce qui reflète bien la tendance à tasser beaucoup de code dans un seul gros fichier en JS.</p>
<p>2860 fichiers XML ? Bordel, mais d&#8217;où viennent-ils ?</p>

<div class="wp_syntax"><div class="code"><pre class="bash" style="font-family:monospace;"><span style="color: #c20cb9; font-weight: bold;">find</span> <span style="color: #660033;">-iname</span> <span style="color: #ff0000;">&quot;*.xml&quot;</span> <span style="color: #000000; font-weight: bold;">|</span> <span style="color: #c20cb9; font-weight: bold;">grep</span> docker <span style="color: #000000; font-weight: bold;">|</span> <span style="color: #c20cb9; font-weight: bold;">wc</span>
<span style="color: #000000;">2790</span></pre></div></div>

<p>Ah, ok.</p>
<p>Je serais curieux de savoir ce que ça donne pour un dev scientifique, un sys admin, un débutant, un chevronné, etc.</p>
 <p><a href="http://sametmax.com/?flattrss_redirect&amp;id=10476&amp;md5=0a259e199cab84c95179e7c5fcc52470" title="Flattr" target="_blank"><img src="media/fc229d00.flattr-badge-large.png" data-original-source="http://sametmax.com/wp-content/plugins/flattr/img/flattr-badge-large.png" alt="flattr this!"/></a></p>


feed_id: http://sametmax.com/?p=10575
title: Redirection 307 et 308
uri: Redirection-307-et-308
url: http://sametmax.com/redirection-307-et-308/
content:
<p>Le standard HTTP 1.1 vient de <a href="http://evertpot.com/http-11-updated/">subir une mise à jour</a>.</p>
<p>Oui, LE standard HTTP 1.1. La fameuse <a href="http://tools.ietf.org/html/rfc2616">RFC2616</a>, qui n&#8217;a pas été touchée depuis l&#8217;année de sortie de The Matrix (coïncidence ? Je ne pense pas&#8230;).</p>
<p>Bref, qu&#8217;est-ce qui change ?</p>
<p>Essentiellement la forme, qui permettra à la RFC de HTTP 2.0 de faire référence à des parties de celle de la HTTP 1.1, maintenant divisée en 8, et évitera de se répéter.</p>
<p>Le web ne va donc pas casser.</p>
<p>Mais il y a quand même intronisation officielle de la redirection 308, qui est à la 301 ce que la 307 est à la 302.</p>
<p>Je me doute que ça vous parle autant qu&#8217;un personnage de film de Charlie Chaplin en fond d&#8217;écran d&#8217;un Windows avec driver realtek.</p>
<p>Pour rappel, quand un serveur HTTP répond à un client, il lui renvoie un code pour indiquer la nature de sa réponse. Quand tout va bien, c&#8217;est 200, quand la page n&#8217;est pas trouvée, c&#8217;est le fameux 404, quand le serveur est en erreur, c&#8217;est une 50x, etc.</p>
<p>Quand le serveur renvoie un code 30x, c&#8217;est généralement une redirection : on dit au client, ce que tu cherches est ailleurs, et le client, recevant ce code, fait donc une seconde requête, sur la nouvelle adresse.</p>
<p>Le code 301 indique une redirection permanente, ce qui signifie que le client peut considérer que c&#8217;est la nouvelle adresse qui remplace l&#8217;ancienne, pour toujours.</p>
<p>Le code 302 indique une redirection temporaire, généralement on utilise ça pour orienter le client sur le site à la suite d&#8217;une action, comme la saisie d&#8217;un formulaire.</p>
<p>Mais ces deux codes ont une particularité : le client effectue une simple requête GET vers la nouvelle adresse. Si vous avez fait une requête complexe avant, par exemple une requête POST avec plein de paramètres, le client va se rediriger avec une nouvelle requête GET basique, toute l&#8217;information précédente est perdue.</p>
<p>Cela peut être ce que l&#8217;on veut. Mais parfois, on souhaite que le client fasse la redirection avec une copie exacte de la requête initiale.</p>
<p>C&#8217;est ce que fait le code 307 : une redirection temporaire, mais avec la même requêtes. C&#8217;est comme la 302, donc, mais répète la même requête deux fois.</p>
<p>Le code 308 demande aussi une redirection en répétant la requête, mais une redirection permanente, comme la 301.</p>
<p><del datetime="2014-06-16T07:31:44+00:00">C&#8217;est ce dernier qui a été ajouté officiellement au protocole, puisqu&#8217;il est déjà supporté par la plupart des navigateurs : même <a href="http://blogs.msdn.com/b/ieinternals/archive/2012/03/29/http-308-permanent-redirect-pushing-the-web-forward-by-breaking-unwanted-forward-compatibility.aspx">IE le comprends depuis 2008</a>.</del> </p>
<p>(le support est vraiment aléatoire, voir comments)</p>
<p>Par curiosité, vous pouvez tester la redirection <a href="http://webdbg.com/test/308/">ici</a>.</p>
 <p><a href="http://sametmax.com/?flattrss_redirect&amp;id=10575&amp;md5=e5a69778e77419a03796548cfa36702d" title="Flattr" target="_blank"><img src="media/fc229d00.flattr-badge-large.png" data-original-source="http://sametmax.com/wp-content/plugins/flattr/img/flattr-badge-large.png" alt="flattr this!"/></a></p>


feed_id: http://sametmax.com/?p=10530
title: Encore une raison d’aimer WAMP
uri: Encore-une-raison-d-aimer-WAMP
url: http://sametmax.com/encore-une-raison-daimer-wamp/
content:
<p>RPC et Pub/Sub sont des modes de communications dont on a besoin tous les jours.</p>
<p>La preuve en est que :</p>
<ul>
<li>On utilise massivement HTTP, qui n&#8217;est jamais qu&#8217;un RPC du pauvre.</li>
<li>La plupart des frameworks expose un modèle d’évènements, slots ou de signaux, ce qui n&#8217;est jamais qu&#8217;un Pub/Sub mais uniquement en local.</li>
</ul>
<p>En fait, on les utilise, mais en bricolé, et limité. On s&#8217;en tient à ça parce que ça marche facilement et partout. Jusqu&#8217;ici, l&#8217;alternative, c&#8217;était la solution propre type SOAP, CORBA, etc., qui est ni simple, ni fun à utiliser. Massivement chiant serait plutôt le bon terme, en fait.</p>
<p>Mais ça n&#8217;a pas empêché les gens de tenter de faire leurs propres solutions :</p>
<ul>
<li><a href="https://t.co/aSFkhCYVCG">GO</a></li>
<li><a href="https://github.com/capaj/socket.io-rpc">JS</a></li>
<li><a href="http://pubsub.sourceforge.net/installation.html#system-requirements">Python</a></li>
<li><a href="http://www.rabbitmq.com/tutorials/tutorial-six-ruby.html">Ruby</a></li>
<li><a href="http://support.microsoft.com/kb/833401">Exchange</a></li>
<li><a href="http://docs.closure-library.googlecode.com/git/class_goog_pubsub_PubSub.html">Clojure</a></li>
</ul>
<p>Si vous avez suivi le compte twitter, vous avez du voir que j&#8217;en trouve régulièrement.</p>
<p>Bien entendu, aucun de ces projets n&#8217;implémente à la fois RPC _et_ Pub/Sub. Aucun ne se base sur un standard. Et aucun ne permet à plusieurs langages / stack technos de parler entre eux.</p>
<p><a href="http://sametmax.com/crossbar-le-futur-des-applications-web-python/">WAMP</a> résout tout ça, en permettant RPC et Pub/Sub, en temps réel, à travers le réseau, entre composants hétérogènes, le tout basé sur un standard.</p>
<p>Au passage, l&#8217;API flaskesque a été acceptée par l&#8217;équipe de Tavendo, et on a une branche qui est bien avancée sur le sujet. Après je m&#8217;attaquerai à faire un bon gros tuto sur la question.</p>
 <p><a href="http://sametmax.com/?flattrss_redirect&amp;id=10530&amp;md5=b7d9f04b7ff9e1eeabc21ced44cbdffd" title="Flattr" target="_blank"><img src="media/fc229d00.flattr-badge-large.png" data-original-source="http://sametmax.com/wp-content/plugins/flattr/img/flattr-badge-large.png" alt="flattr this!"/></a></p>


feed_id: http://sametmax.com/?p=10520
title: Ma boîte à outils Python, mise à jour
uri: Ma-boîte-à-outils-Python-mise-à-jour
url: http://sametmax.com/ma-boite-a-outils-python-mise-a-jour/
content:
<p>Il y a bien longtemps je vous avais parlé de <a href="http://sametmax.com/sept-petites-libs-qui-changent-la-vie-dun-dev-python/">7 libs Python </a> vraiment chouettes, puis quelques articles pour vous signaler des remplaçants, ici et là.</p>
<p>Comme je ne m&#8217;attends pas à ce que vous suiviez le blog article par article, voici une petit synthèse, la conclusion de l&#8217;évolution de ma boîte à outils Python.</p>
<h2>D&#8217;abord, ce qui n&#8217;a pas changé</h2>
<p><a href="http://sametmax.com/debugger-en-python-les-bases-de-pdb/">Ipdb</a>, est toujours de la partie. Je m&#8217;essaye à <a href="https://pypi.python.org/pypi/pudb">pudb</a> de temps à autre, mais vraiment pour casser l&#8217;habitude. Ipdb reste la référence pour tout debugger. C&#8217;est simple, c&#8217;est puissant, c&#8217;est fantastique.</p>
<p><a href="https://github.com/jaraco/path.py">path.py</a>, inébranlable remplacement de os.path et shutil, malgré l&#8217;intégration de <a href="https://docs.python.org/3/library/pathlib.html?highlight=pathlib#module-pathlib">pathlib</a> à python 3.4 qui est loin d&#8217;être aussi bon. J&#8217;ai même fait quelques contributions sur le projet, notamment l&#8217;écriture de fichier &#8220;inplace&#8221;.</p>
<p><a href="http://docs.python-requests.org/">requests</a>, la fantastique lib HTTP, qui n&#8217;évolue plus beaucoup, car qui a besoin d&#8217;améliorer la perfection ? On regrettera l&#8217;absence de version asynchrone, bien que certains projets alternatifs, moins bons, s&#8217;y collent.</p>
<p><a href="http://bottlepy.org/">bottle</a>, toujours pour bricoler des sites rapides. Mais de moins en moins, maintenant que <a href="https://github.com/sametmax/django-quicky">django_quicky</a> est bien rodé, si je sais que le site va déboucher sur un truc, c&#8217;est plus productif d&#8217;utiliser ce dernier.</p>
<p><a href="http://peewee.readthedocs.org/">peewee</a>, surtout pour introduire les gens à la programmation avec base de données durant les formations. SQLAlchemy c&#8217;est bien trop dur, et l&#8217;ORM Django se ramène avec tout le framework. Mais pour mes besoins rapides, j&#8217;avoue que je tape de plus en plus dans Redis ou du flat file. C&#8217;est souvent du jetable de toute façon et je manipule itertools mieux que SQL ou toute couche ORM au dessus.</p>
<p><a href="http://sametmax.com/grin-le-grep-python-sous-steroid/">grin</a> est toujours là. Parce que je ne me souviens jamais des millions de flags de grep pour obtenir ce que grin donne gratos, comme la coloration, le contexte, la récursivité et l&#8217;exclusion du dossier <em>.git</em>. Parce qu&#8217;il prend les regex Python en compte. Parce qu&#8217;il marche sous n&#8217;importe quelle plateforme supportée par Python de la même manière, y compris Windows, et s&#8217;installe avec un <code>pip install grin --user</code> sans avoir besoin des droits root.</p>
<h2>Ce qui a changé</h2>
<p>Adios dateutil, bonjour <a href="http://sametmax.com/les-time-zones-en-python/">arrow</a>. Ok, je triche un peu, car arrow utilise dateutil sous le capot, mais je n&#8217;en vois jamais la couleur. Arrow est plus simple, plus beau, plus productif, plus mieux. Les dates, telles que ça devrait être dans la lib standard.</p>
<p>Ensuite, au revoir clize, place à <a href="http://sametmax.com/la-plus-belle-maniere-de-parser-les-arguments-de-script-en-python/">docopt</a> pour parser toutes les lignes de commandes. Les fonctionnalités avancées de <a href="http://click.pocoo.org/">click</a> ne m&#8217;ont pas convaincu de migrer, et docopt est tellement intuitif, tellement merveilleux dans son fonctionnement&#8230;</p>
<p><a href="https://github.com/sametmax/minibelt">minibelt</a> fait maintenant partie de tous mes fichiers requirements. Surtout pour son sorted <code>set</code>, son <code>dmerge</code>, ses <code>get</code>, <code>attr</code> et <code>iget</code>. Parfois pour <code>chunk</code> et <code>windows</code>. C&#8217;est tellement rageant de devoir recoder ces petites fonctions pour chaque projet.</p>
<p><a href="http://sametmax.com/remplacer-sed-awk-cut-et-perl-par-python-orgasme-pour-sysadmin/">pyped</a> est de plus en plus utile chaque jour. Depuis l&#8217;article du blog, il a bien évolué et embarque un tas de goodies comme le spliting via options, l&#8217;auto filter ou encore le parsing JSON. Bref, je n&#8217;ai plus touché à <code>sed</code> depuis des mois.</p>
<p><a href="http://sametmax.com/se-simplifier-les-tests-python-avec-pytest/">pytest</a> partout. Je ne touche plus au module unittest de la stdlib ni celui de Django. pytest est la réponse à tout et même plus, c&#8217;est 42 + 1, c&#8217;est la seule raison pour laquelle je fais encore des tests.</p>
<p><a href="https://pypi.python.org/pypi/six/1.7.2">six</a>. Pas parce que j&#8217;aime cette lib, mais parce que je me tape pas mal de conversion Python 2/3 en ce moment, et six est le socle sur lequel repose toutes mes migrations. Pourvue que je n&#8217;ai bientôt plus besoin de l&#8217;utiliser. Oui, oui, je sais, on doit migrer <a href="http://0bin.net/">0bin</a> et tous les autres projets. Un jour.</p>
<p>Pas directement lié à Python, mais pour faire des UI au dessus de mes APIs en Python. jQuery est petit à petit en train d&#8217;être remplacé par <a href="http://angular-ui.github.io/">angularjs</a> que je commence à bien maitriser. Ça mériterait un paquet d&#8217;articles rien que pour le bestiau mais je vais m&#8217;en tenir au 180 drafts qu&#8217;on a déjà en stock :) Et puis jQuery a encore de beaux jours devant lui pour tous les sites qui ont besoin de ref, les sites legacy, les petits sites qui n&#8217;ont pas besoin d&#8217;un bazooka pour l&#8217;UI&#8230;</p>
<p>Un autre truc que j&#8217;utilise tous les jours, c&#8217;est la <a href="http://sametmax.com/cours-et-tutos/">liste des cours</a> du blog avec une recherche type &#8220;site:http://sametmax.com/cours-et-tutos/ mes mots clés&#8221; car le moteur de recherche de wordpress est à chier. Et franchement, je me félicite régulièrement du <a href="https://github.com/sametmax/miroir-du-blog">dump du blog</a> parce qu&#8217;Internet est pas stable ici et la consultation hors ligne devient vite indispensable. Pour cette même raison, j&#8217;ai installé <a href="http://zealdocs.org/">zeal</a> avec la doc de python en local.</p>
<h2>Ce qui est resté au placard</h2>
<p>Je pensais que j&#8217;utiliserais beaucoup <a href="https://github.com/sametmax/templite">templite</a>, mais en fait pas du tout. Comme minibelt contient mes fonctions favorites, <a href="http://sametmax.com/batbelt-la-lib-des-petits-outils-python-qui-vont-bien/">batbelt</a>, sa grande soeur, s’ennuie, surtout qu&#8217;elle n&#8217;est toujours pas compatible Python 3.</p>
<p>Plus de Beautiful soup. Je ne parse plus de HTML, il y a des APIs partout&#8230; Plus de Fabric. J&#8217;utilise docker et je déploie mes instances à la main (j&#8217;ai pas 500 serveurs à gérer).</p>
<p>J&#8217;ai aussi fait un long essai du shell fish, mais je ne suis pas convaincu et je vais retourner à ce bon vieux bash.</p>
 <p><a href="http://sametmax.com/?flattrss_redirect&amp;id=10520&amp;md5=f204afcb23124a9b49cb408dcefa1064" title="Flattr" target="_blank"><img src="media/fc229d00.flattr-badge-large.png" data-original-source="http://sametmax.com/wp-content/plugins/flattr/img/flattr-badge-large.png" alt="flattr this!"/></a></p>


feed_id: http://sametmax.com/?p=10596
title: Comment fonctionne HTTP ?
uri: Comment-fonctionne-HTTP
url: http://sametmax.com/comment-fonctionne-http/
content:
<p>Plus je fais du dev Web, plus je m&#8217;aperçois que beaucoup de mes collègues n&#8217;ont aucune idée de comment fonctionne HTTP sous le capot. Comme vous le savez, ma règle numéro 1 c&#8217;est qu&#8217;<a href="http://sametmax.com/bien-expliquer-quelque-chose-les-regles-de-base/">il n&#8217;y a rien d&#8217;évident</a>, donc petit tuto pour expliquer les bases.</p>
<p>On ne va pas rentrer dans les petits détails, juste une petite intro histoire de savoir ce qui se passe derrière ce script PHP ou cette application bottle.</p>
<p>Article long, vous connaissez la chanson.</p>

<!-- iframe plugin v:2.3 - wordpress.org/extend/plugins/iframe/ -->
<iframe width="420" height="315" src="//www.youtube.com/embed/mDarUeXPFco" frameborder="0" scrolling="no" class="iframe-class"></iframe>
<p>Et puis c&#8217;est dans le ton de l&#8217;actu ^^</p>
<h2>La logique de client / serveur</h2>
<p>(<a href="http://sametmax.com/bien-expliquer-quelque-chose-les-regles-de-base/">Je me repompe moi-même</a>)</p>
<p>Se balader sur le Web, c&#8217;est comme aller au resto. On est un client, on demande quelque chose au serveur, le serveur va voir en cuisine, et revient avec la bouffe, ou une explication sur l&#8217;absence de la bouffe (mais jamais pourquoi la bouffe est dégueulasse, allez comprendre):</p>
<div id="attachment_965" class="wp-caption aligncenter" style="width: 533px"><a href="media/fba86e68.http.png" data-original-source="http://sametmax.com/wp-content/uploads/2012/06/http.png"><img class=" wp-image-965 " title="Le protocole HTTP, en gros" src="media/fba86e68.http.png" data-original-source="http://sametmax.com/wp-content/uploads/2012/06/http.png" alt="Schéma du protocole HTTP, en gros" width="523" height="666" /></a><p class="wp-caption-text">Le protocole HTTP, en (très) gros</p></div>
<p>Ce cycle requête/réponse se déroule des centaines de fois quand on parcours un site Web. Chaque lien cliqué déclenche une requête GET, et la page suivante ne s&#8217;affiche que parce qu&#8217;on a reçu la réponse contenant le HTML de celle-ci. Les formulaires, les requêtes AJAX, la mise à jour de vos Tweets sur votre téléphone, tout ça fonctionne sur le même principe.</p>
<p>Donc, sur votre site Web, Firefox, Chrome et les autres vont faire une requête à une machine, votre machine contient un code (si vous êtes dev, votre code :) qui va recupérer cette requête et générer une réponse.</p>
<h2>Tout est texte</h2>
<p>Généralement, les développeurs utilisent des outils sophistiqués pour écrire des sites Web. Si bien que quand on écrit du code, on ne voit pas vraiment la requête et la réponse, mais des fonctions, des objets, du HTML&#8230; Comment ça arrive et comment ça repart est géré automatiquement.</p>
<p>Regardons ce qui se passe quand ce n&#8217;est PAS géré automatiquement.</p>
<p>Voici le code d&#8217;un petit serveur HTTP écrit en Python 3. Il accepte n&#8217;importe quelle requête sur le port 7777 et retourne toujours une page avec marqué &#8220;Coucou&#8221;.</p>

<div class="wp_syntax"><div class="code"><pre class="python" style="font-family:monospace;"><span style="color: #ff7700;font-weight:bold;">import</span> asyncio
&nbsp;
<span style="color: #808080; font-style: italic;"># Le texte de la réponse qu'on va renvoyer</span>
COUCOU = b<span style="color: #483d8b;">&quot;&quot;&quot;HTTP/1.1 200 OK
Date: Fri, 16 Jun 2014 23:59:59 UTC
Content-Type: text/html
&nbsp;
&lt;html&gt;
&lt;body&gt;
&lt;h1&gt;Coucou&lt;/h1&gt;
&lt;/body&gt;
&lt;/html&gt;&quot;&quot;&quot;</span>
&nbsp;
<span style="color: #808080; font-style: italic;"># La fonction qui gère chaque requête et qui renvoie une réponse</span>
<span style="color: #808080; font-style: italic;"># pour chacune d'entre elles. La même réponse à chaque fois pour cet</span>
<span style="color: #808080; font-style: italic;"># exemple, mais on peut fabriquer une réponse différente si on veut.</span>
<span style="color: #ff7700;font-weight:bold;">def</span> handle_request<span style="color: black;">&#40;</span>request, response<span style="color: black;">&#41;</span>:
    <span style="color: #808080; font-style: italic;"># On lit la requête</span>
    data = <span style="color: #ff7700;font-weight:bold;">yield</span> <span style="color: #ff7700;font-weight:bold;">from</span> request.<span style="color: black;">read</span><span style="color: black;">&#40;</span><span style="color: #ff4500;">1000000</span><span style="color: black;">&#41;</span>
    <span style="color: #808080; font-style: italic;"># On affiche son contenu. Surprise, c'est que du texte !</span>
    <span style="color: #ff7700;font-weight:bold;">print</span><span style="color: black;">&#40;</span>data.<span style="color: black;">decode</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'ascii'</span><span style="color: black;">&#41;</span><span style="color: black;">&#41;</span>
    <span style="color: #808080; font-style: italic;"># On écrit notre réponse, que du texte aussi !</span>
    response.<span style="color: black;">write</span><span style="color: black;">&#40;</span>COUCOU<span style="color: black;">&#41;</span>
    <span style="color: #808080; font-style: italic;"># On ferme la connexion : le protocole HTTP est stateless,</span>
    <span style="color: #808080; font-style: italic;"># c'est à dire qu'il n'y a pas de maintien d'un état</span>
    <span style="color: #808080; font-style: italic;"># côté client ou serveur et chaque requête est indépendante</span>
    <span style="color: #808080; font-style: italic;"># de toutes les autres.</span>
    response.<span style="color: black;">close</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
&nbsp;
<span style="color: #ff7700;font-weight:bold;">if</span> __name__ == <span style="color: #483d8b;">'__main__'</span>:
    <span style="color: #808080; font-style: italic;"># Machinerie pour faire tourner le serveur :</span>
    <span style="color: #808080; font-style: italic;"># Récupération de la boucle d'événements.</span>
    loop = asyncio.<span style="color: black;">get_event_loop</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
    <span style="color: #808080; font-style: italic;"># Création du serveur qui écoute sur le port 7777</span>
    <span style="color: #808080; font-style: italic;"># et qui va appeler notre fonction quand il reçoit</span>
    <span style="color: #808080; font-style: italic;"># une requête.</span>
    f = asyncio.<span style="color: black;">start_server</span><span style="color: black;">&#40;</span>handle_request, port=<span style="color: #ff4500;">7777</span><span style="color: black;">&#41;</span>
    <span style="color: #808080; font-style: italic;"># Installation du serveur dans la boucle d'événements.</span>
    loop.<span style="color: black;">run_until_complete</span><span style="color: black;">&#40;</span>f<span style="color: black;">&#41;</span>
    <span style="color: #808080; font-style: italic;"># On démarre la boucle d'événement.</span>
    <span style="color: #ff7700;font-weight:bold;">print</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">&quot;Serving on localhost:7777&quot;</span><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>
&nbsp;
<span style="color: #808080; font-style: italic;"># Si vous êtes dev, vous commencez à comprendre combien</span>
<span style="color: #808080; font-style: italic;"># les libs et frameworks qui gèrent tout ce bordel pour</span>
<span style="color: #808080; font-style: italic;"># vous sont fantastiques.</span></pre></div></div>

<p>Si on lance le serveur et qu&#8217;on visite la page <code>http://localhost:7777</code> sur un navigateur, on va alors voir ceci dans le terminal où tourne le serveur :</p>
<pre>$ python3 server.py
Serving on localhost:7777
GET / HTTP/1.1
Host: 127.0.0.1:7777
User-Agent: Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:30.0) Gecko/20100101 Firefox/30.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: fr,fr-fr;q=0.8,en-us;q=0.5,en;q=0.3
Accept-Encoding: gzip, deflate
DNT: 1
Connection: keep-alive
Cache-Control: max-age=0
</pre>
<p>C&#8217;est la requête envoyée par notre client, et reçue par notre serveur. &#8220;Aller&#8221; sur l&#8217;adresse veut dire en fait que votre navigateur envoie ce morceau de texte.</p>
<p>Juste après, mon serveur renvoie aussi du texte. Toujours le même dans notre cas :</p>

<div class="wp_syntax"><div class="code"><pre class="html" style="font-family:monospace;">HTTP/1.1 200 OK
Date: Fri, 16 Jun 2014 23:59:59 UTC
Content-Type: text/html
&nbsp;
&lt;html&gt;
&lt;body&gt;
&lt;h1&gt;Coucou&lt;/h1&gt;
&lt;/body&gt;
&lt;/html&gt;</pre></div></div>

<p>Le navigateur l&#8217;interprète, et vous fabrique cette page :</p>
<div id="attachment_10597" class="wp-caption aligncenter" style="width: 335px"><a href="media/552cfa12.get.png" data-original-source="http://sametmax.com/wp-content/uploads/2014/06/get.png"><img src="media/552cfa12.get.png" data-original-source="http://sametmax.com/wp-content/uploads/2014/06/get.png" alt="" title="Vous contemplez le Web de 1995" width="325" height="186" class="size-full wp-image-10597" /></a><p class="wp-caption-text">Vous contemplez le Web de 1995</p></div>
<p>Comme vous pouvez le voir, ce n&#8217;est que du texte tout simple qui est reçu et envoyé.</p>
<p>Quand quelqu&#8217;un développe un site Web, ce qu&#8217;il fait vraiment, c&#8217;est ça. La plupart du temps il ne le voit pas car ses outils lui facilitent la tâche en analysant le texte et en lui donnant des fonctions et des objets pour le manipuler. Mais derrière, c&#8217;est juste du texte.</p>
<p>Quand quelqu&#8217;un surf le Web, ce qu&#8217;il fait vraiment, c&#8217;est ça. Mais le navigateur se charge de le cacher derrière des jolis contrôles, et affiche un résultat bien plus agréable à regarder.</p>
<h2>Structure des requêtes</h2>
<p>Le texte d&#8217;une requête est divisé en 3 parties :</p>
<ul>
<li>L&#8217;action demandée sur la ressource.</li>
<li>Les headers.</li>
<li>Le corps de la requête</li>
</ul>
<p>Prenons notre précédente requête d&#8217;exemple :</p>
<pre>
GET / HTTP/1.1
Host: 127.0.0.1:7777
User-Agent: Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:30.0) Gecko/20100101 Firefox/30.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: fr,fr-fr;q=0.8,en-us;q=0.5,en;q=0.3
Accept-Encoding: gzip, deflate
DNT: 1
Connection: keep-alive
Cache-Control: max-age=0</pre>
<p>L&#8217;action demandée sur la ressource est toujours sur la première ligne :</p>
<p><code>GET / HTTP/1.1</code></p>
<p>Elle se divise en 3 parties :</p>
<ul>
<li>Le verbe d&#8217;action : GET, POST, PUT, OPTION, HEAD, etc. C&#8217;est ce qu&#8217;on veut faire sur les données qu&#8217;on demande. Le plus courant sur le Web, c&#8217;est GET (lire la donnée) et POST (créer la donnée, qu&#8217;on utilise souvent pour les formulaires).</li>
<li>Le chemin de la ressource. Où se trouve la ressource à laquelle on souhaite accéder. Ici c&#8217;est &#8220;/&#8221;, la racine, mais ça peut être &#8220;/user/monique/profile&#8221; par exemple.</li>
<li>La version du protocole utilisé. Aujourd&#8217;hui tout le monde utilise HTTP en version 1.1, donc ça ne change pas vraiment.</li>
</ul>
<p>Notez bien que le chemin de la ressource n&#8217;a pas à correspondre à un chemin réel d&#8217;un fichier sur l&#8217;ordinateur. Une ressource est quelque chose de complètement virtuel, quelque chose que mon programme met à disposition selon mes désirs. Si c&#8217;est un fichier, très bien, mais ce n&#8217;est pas obligatoire, et je peux générer n&#8217;importe quelle réponse qui me plait.</p>
<p>En effet, si je vais sur l&#8217;adresse <code>http://127.0.0.1:7777/user/monique/profile/</code>, vous notez que mon serveur continue de marcher. Il reçoit simplement la requête :</p>
<pre>GET /user/monique/profile/ HTTP/1.1
...
</pre>
<p>C&#8217;est à mon serveur de décider ce qu&#8217;il choisit de faire avec le chemin <code>/user/monique/profile/</code>. Ici il est un peu con et continue de renvoyer &#8220;coucou&#8221;.</p>
<p>Bien, on vient de voir &#8220;L&#8217;action demandée sur la ressource&#8221;, voyons maintenant les headers :</p>
<pre>Host: 127.0.0.1:7777
User-Agent: Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:30.0) Gecko/20100101 Firefox/30.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: fr,fr-fr;q=0.8,en-us;q=0.5,en;q=0.3
Accept-Encoding: gzip, deflate
DNT: 1
Connection: keep-alive
Cache-Control: max-age=0</pre>
<p>Les headers sont des informations supplémentaires que le client fourni à mon serveur sous la forme :</p>
<pre>Nom-De-L-Information: contenu</pre>
<p><strong>Les sauts de ligne sont très importants.</strong> La première ligne de la requête est &#8220;L&#8217;action demandée sur la ressource&#8221;, puis on met un saut de ligne, ensuite vient un header, puis un saut de ligne, puis un header&#8230; Chaque header doit tenir sur une ligne.</p>
<p>En HTTP/1.1, seul le header <code>Host</code> est obligatoire, mais comme aucun header n&#8217;était obligatoire en HTTP/1.0, beaucoup de serveurs acceptent l&#8217;absence de tout header.</p>
<p>Les headers contiennent généralement des informations sur le client (ex: <code>Accept-Language</code> vous dit quelles langues le client accepte), le contenu de la requête (ex: <code>Content-Length</code> indique la taille de la requête) ou demande un comportement du server (<code>Cache-Control</code> précise comment gérer les ressources qu&#8217;on peut mettre en cache).</p>
<p>Pour &#8220;Le corps de la requête&#8221;, il nous faut ajouter du contenu à notre requête.</p>
<p>Pour cela, faisons une page avec un petit formulaire HTML :</p>

<div class="wp_syntax"><div class="code"><pre class="html" style="font-family:monospace;">&lt;html&gt;
&lt;head&gt;
    &lt;title&gt;Test post&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
&lt;form method=&quot;post&quot; action=&quot;http://127.0.0.1:7777/&quot;&gt;
&lt;p&gt;&lt;input type=&quot;test&quot; value=&quot;coucou, tu veux voir mon POST ?&quot; name=&quot;salut&quot;&gt;
&lt;input type=&quot;submit&quot;&gt;&lt;/p&gt;
&lt;/form&gt;
&lt;/body&gt;
&lt;/html&gt;</pre></div></div>

<p>Ce qui nous donne cette page :</p>
<div id="attachment_10598" class="wp-caption aligncenter" style="width: 335px"><a href="media/7dfeb501.post.png" data-original-source="http://sametmax.com/wp-content/uploads/2014/06/post.png"><img src="media/7dfeb501.post.png" data-original-source="http://sametmax.com/wp-content/uploads/2014/06/post.png" alt="Capture d&#039;écran d&#039;un simple formulaire web sous firefox" title="Techniquement, on peut poster un formulaire avec une requête GET, mais shut..." width="325" height="186" class="size-full wp-image-10598" /></a><p class="wp-caption-text">Techniquement, on peut poster un formulaire avec une requête GET, mais shut...</p></div>
<p>Si on active le formulaire, notre serveur affiche cette requête :</p>
<pre>POST / HTTP/1.1
Host: 127.0.0.1:7777
User-Agent: Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:30.0) Gecko/20100101 Firefox/30.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: fr,fr-fr;q=0.8,en-us;q=0.5,en;q=0.3
Accept-Encoding: gzip, deflate
DNT: 1
Connection: keep-alive
Content-Type: application/x-www-form-urlencoded
Content-Length: 41

salut=coucou%2C+tu+veux+voir+mon+POST+%3F</pre>
<p>Déjà, vous notez que le verbe d&#8217;action a changé : <code>POST / HTTP/1.1</code>.</p>
<p>Ensuite, à la fin de la requête, on laisse une ligne vide, puis on a de nouveau du texte : </p>
<pre>salut=coucou%2C+tu+veux+voir+mon+POST+%3F</pre>
<p>C&#8217;est le corps de la requête.</p>
<p>C&#8217;est comme ça que le serveur reçoit le contenu des données des formulaires, et c&#8217;est ce qu&#8217;on retrouve dans la variable <code>$_POST</code> en PHP ou <code>request.POST</code> en Django</p>
<p>Encore une fois, les outils de programmation évitent au codeur de travailler avec du charabia, et le transforme en quelque chose de facile à comprendre et à manipuler.</p>
<h2>Structure des réponses</h2>
<p>C&#8217;est pareil, ma bonne dame. Reprenons notre exemple de réponse :</p>

<div class="wp_syntax"><div class="code"><pre class="html" style="font-family:monospace;">HTTP/1.1 200 OK
Date: Fri, 16 Jun 2014 23:59:59 UTC
Content-Type: text/html
&nbsp;
&lt;html&gt;
&lt;body&gt;
&lt;h1&gt;Coucou&lt;/h1&gt;
&lt;/body&gt;
&lt;/html&gt;</pre></div></div>

<p>Première ligne, on précise le protocole, puis le <a href="http://fr.wikipedia.org/wiki/Liste_des_codes_HTTP">code de réponse</a>, qui stipule la nature de votre réponse (tout va bien, une erreur, une redirection, la page n&#8217;existe pas, etc).</p>
<p>Ensuite les headers, puis on saute une ligne, et on met le corps de la réponse. Ici, le code HTML qui va donner notre jolie page &#8220;Coucou&#8221;.</p>
<h2>Tout tient là dedans</h2>
<p>Tout le reste, toutes les fonctionnalités fantastiques du Web (les liens hypertextes, les fichiers statiques JS et CSS inclus, les cookies, les redirections, le cache, la compression&#8230;) ont pour point d&#8217;entrée un de ces 3 éléments de la requête ou de la réponse. C&#8217;est que c&#8217;est un protocole bien foutu. </p>
<p>Enfin, disons, <a href="http://sametmax.com/le-web-nest-plus-http/">presque tout</a> ? :)</p>
<hr />
<p><a href="https://github.com/sametmax/codes-des-articles/tree/master/2014/juin">Télécharger le code de l&#8217;article</a></p>
 <p><a href="http://sametmax.com/?flattrss_redirect&amp;id=10596&amp;md5=cb865fe145abb491eb081cf7de0d006b" title="Flattr" target="_blank"><img src="media/fc229d00.flattr-badge-large.png" data-original-source="http://sametmax.com/wp-content/plugins/flattr/img/flattr-badge-large.png" alt="flattr this!"/></a></p>


feed_id: http://sametmax.com/?p=10807
title: Alzheimer
uri: Alzheimer
url: http://sametmax.com/alzheimer/
content:
<p><em>Visiblement j&#8217;ai raté mon coup. Les gens voient l&#8217;article comme anti-microsoft alors que je voulais juste souligner la faciliter à oublier qui sont les ordures en prenant un exemple criant.</em></p>
<p>Si il y a bien un truc qui me fait rager, c&#8217;est la capacité de l&#8217;homme  à oublier à quel point les gens ont été des connards.</p>
<p>Ça marche en politique, en commerce, dans le monde de l&#8217;art, et même dans son entourage proche, avec les relations amicales et amoureuses.</p>
<p>Partout en fait. Si quelqu&#8217;un fait une grosse crasse, il pourra toujours attendre, et revenir plus tard. Pouf, lavé. En fait, <strong>il vaut mieux faire une grosse saloperie de temps en temps, et avoir une bonne image, que d&#8217;être quelqu&#8217;un de bien sans communiquer dessus.</strong> Pour les boites, mais pour vous en tant que personne aussi évidement.</p>
<p>Prenez par exemple cette <a href="http://imgur.com/gallery/DTDsH0P">interview de Bill Gates</a>. </p>
<p>Je prends Bill Gates comme exemple, hein, il y en a beaucoup comme lui.</p>
<p>Lisez les commentaires sous l&#8217;interview, vous allez-voir, c&#8217;est atterrant.</p>
<p>Déjà, dois-je souligner le fait que le gars utilise une technique de rhétorique de comptoir pour se mettre de la pommade et que ça passe sans problème.</p>
<p>Dès le début, je trouve ça irritant que le public soit aussi con.</p>
<p>Quand on se traine les casseroles qu&#8217;il a et qu&#8217;à la question &#8220;c&#8217;est quoi le mythe le plus ennuyeux à votre propos&#8221; il répond &#8220;qu&#8217;on dise que je suis trop bien, lol&#8221;, et que personne ne tique&#8230; Merde !</p>
<p>C&#8217;est dans tous les livres les plus pourris sur les entretiens d&#8217;embauche, la première technique qu&#8217;on enseigne pour répondre à la question &#8220;quel est votre plus gros point faible&#8221;.</p>
<p>Mais passons, ce n&#8217;est pas le sujet de l&#8217;article.</p>
<p>Le sujet, c&#8217;est qu&#8217;au cours des années, Microsoft, sous la direction de Billy, nous a <a href="http://en.wikipedia.org/wiki/Criticism_of_Microsoft">pourri la vie</a> avec :</p>
<ul>
<li>L&#8217;enfermement de ses utilisateurs.</li>
<li>Le troll de copyright.</li>
<li>Des licences abusives.</li>
<li>Le mauvais traitement de ses employés.</li>
<li>Céder à la censure.</li>
<li>Collaboration avec la NSA.</li>
<li>IE6.</li>
</ul>
<p>Chacun de ces points mérite à lui seul de ranger Gates dans la catégorie connard, et ne plus lui accorder de crédit. Tous ces points ensembles&#8230;</p>
<p>Mais non, apparemment ce n&#8217;est pas grave. On peut mépriser ses clients, ses employés, les lois, et avoir l&#8217;aval du public. Les faits, tout le monde s&#8217;en bat les steaks.</p>
<p>En fait, les gens n&#8217;associent pas du tout la compagnie avec l&#8217;homme. Deux entités distinctes. Il vient d&#8217;où son pognon à William, d&#8217;après vous ?</p>
<p>Enfin je dis ça, les gens ont pardonné à Microsoft aussi. Comme ils pardonnent à <a href="http://sebsauvage.net/rhaa/index.php?q=thecriclinks">Apple</a> et comme il pardonneront à <a href="http://doonesbury.washingtonpost.com/strip/archive/2014/06/01">Google</a>.</p>
<p>Pourtant, ce n&#8217;est pas comme si c&#8217;était un petit scandale et puis terminé. Non, ils en remettent une couche régulièrement:</p>
<ul>
<li><a href="http://www.forbes.com/sites/alexandrawrage/2013/03/20/microsoft-whistleblower-allegations-highlight-global-corruption-risks/">Corruption</a></li>
<li><a href="http://www.tomshardware.fr/articles/pots-de-vin-microsoft,1-45570.html">Pot de vin</a></li>
<li><a href="http://www.sebsauvage.net/rhaa/?2005/03/02">Plagiat</a></li>
<li><a href="http://www.networkworld.com/community/blog/blast-past-microsoft-accused-monopolistic-pra">Abus de position dominante</a></li>
<li><a href="http://en.wikipedia.org/wiki/United_States_v._Microsoft_Corp.">Établissement d&#8217;un monopole</a></li>
<li><a href="http://arstechnica.com/information-technology/2013/03/microsoft-accused-of-locking-out-linux-in-eu-antitrust-complaint/">Entrave à la libre concurrence</a></li>
<li><a href="http://lesmoutonsenrages.fr/2012/01/12/le-mariage-gates-et-monsanto-attention-danger-pour-la-planete/">Financement des pires lobbies légaux de la planète</a></li>
<li><a href="http://www.zdnet.com/blog/security/one-year-old-unpatched-windows-token-kidnapping-under-attack/2894">Mépris de la sécurité de ses utilisateurs.</a></li>
<li><a href="http://sebsauvage.net/rhaa/index.php?2009/05/09/15/48/27-microsoft-sabote-odf">Sabotage</a></li>
<li><a href="http://sebsauvage.net/rhaa/index.php?2007/05/23/09/34/59-microsoft-relance-sa-campagne-quot-get-the-facts-quot-">Publicité mensongère</a></li>
<li><a href="http://appft1.uspto.gov/netacgi/nph-Parser?Sect1=PTO1&#038;Sect2=HITOFF&#038;d=PG01&#038;p=1&#038;u=%2Fnetahtml%2FPTO%2Fsrchnum.html&#038;r=1&#038;f=G&#038;l=50&#038;s1=%2220040230959%22.PGNR.&#038;OS=DN/20040230959&#038;RS=DN/20040230959">Brevets abusifs</a></li>
<li ><a href="http://www.microsoft-watch.com/content/vista/ballmers_bold_vista_admission.html?kc=MWRSS02129TX1K0000535">Vente de produits ouvertement pas finis au prix d&#8217;un produit de haute technologie</a></li>
<li><a href="http://www.asymco.com/2011/05/27/microsoft-has-received-five-times-more-income-from-android-than-from-windows-phone/">Business model basé sur l&#8217;extorsion</a></li>
<li><a href="http://www.salon.com/2011/09/06/wikileaks_microsoft_tunisia">Soutien de dictateurs</a></li>
<li><a href="http://sebsauvage.net/rhaa/?2010/01/06/13/21/41-microsoft-peine-de-mort-par-flashage">Destruction de votre matos à distance</a></li>
</ul>
<p>J&#8217;arrête là car la liste n&#8217;en finit pas, des dizaines de scandales (en plus, là je ne prends qu&#8217;un exemple par type de scandale ici, sinon&#8230;), plus ou moins gros, par an. Tellement que c&#8217;en est lassant.</p>
<p>Pourtant, un seul d&#8217;entre eux devrait suffire à les renvoyer dans les cordes. Là, il y a de quoi rempir un manuel d&#8217;exercice de conclusions pénales, et ça ne fait rien.</p>
<p>Je crois que passée une certaine quantité de magouilles, y a un switch qui s&#8217;active dans le cervau qui amorce un <a href="https://en.wikipedia.org/wiki/SEP_field">SEP field</a>.</p>
<p>Ça me fait penser à cette blague, où Bush discute avec Bin Laden, et un conseiller en com leur demande ce qu&#8217;ils foutent :</p>
<blockquote>
<ul>
<li>On fait nos plans. On a décidé de tuer 30 000 américains, 140 000 irakiens et un homosexuel séropositif.</li>
<li>Pourquoi un homo ?</li>
<li> Tu vois Ousama, je te l&#8217;avais dit, tout le monde s&#8217;en branle des autres !</li>
</ul>
</blockquote>
<p>Après il y a des gens qui disent &#8220;<em>oui mais Microsoft est trop gros, il ne peut pas contrôler tout ça</em>&#8220;.</p>
<p>MS a la taille choisie par Bill Gates, il est responsable de ce fait, il l&#8217;a décidé, chaque jour. S&#8217;il était, à l&#8217;époque, assez compétent pour être à la tête de la boite, il est responsable de chacune de ces merdes. S&#8217;il n&#8217;était pas compétent, on avait un abruti aux commandes d&#8217;un tank dans un jardin d&#8217;enfants. Dans tous les cas, c&#8217;est grave.</p>
<p>Il y a aussi le &#8220;<em>oui, mais il a fait beaucoup pour l&#8217;informatique</em>&#8220;. </p>
<p>Déjà, ça reste à démontrer. Qui me dit que si on supprimais M$ à sa naissance, l&#8217;informatique ne se porterait pas mieux ? Improuvable.</p>
<p>Ensuite, <a href="http://www.reuters.com/article/2013/11/04/us-jnj-settlement-idUSBRE9A30MM20131104">Johnson &amp; Johnson</a> ont aussi &#8220;fait beaucoup&#8221; pour la pharmaceutique, mais je ne leur confierais pas <a href="http://www.framablog.org/index.php/post/2014/05/21/microsoft-academie-de-paris">l&#8217;hébergement d&#8217;un colloque du ministère de la santé</a>. (J&#8217;étais parti pour un exemple de rétablissement économique de l&#8217;Allemagne en 40, mais j&#8217;ai reçu un email de Godwin).</p>
<p>Tout ça pour dire que parce que quelqu&#8217;un a (peut-être) fait un peu de bien, ça ne l&#8217;absout pas de toutes les merdes qu&#8217;il a faites. Sinon, je vous mets une baffe dans la gueule, je vous fais un bisou, et on est quitte ?</p>
<p>Ensuite, il y a l&#8217;aspect moral du simple fait qu&#8217;il a concentré, pour sa petite personne, pendant des dizaines d&#8217;années, des milliards de dollars. Il en donne la moitié aujourd&#8217;hui, mais les gens semblent oublier qu&#8217;ils les a retirés de l&#8217;économie et bloqués. Pendant toute sa vie.</p>
<p>Je ne parle pas du flux d&#8217;argent qu&#8217;il a fait circuler de l&#8217;économie à ses comptes, et de ses comptes à l&#8217;économie. Non. Je parle de ce qu&#8217;il a accumulé dessus, c&#8217;est à dire ce qui est resté là, à attendre.</p>
<p>Et oui, c&#8217;est ce que personne ne dit quand on parle des ultra-riches : la monnaie, ce n&#8217;est pas juste un nombre ou du papier, c&#8217;est la représentation d&#8217;une ressource, en grande majorité le travail humain. Quand ce sont quelques millions, l&#8217;impact n&#8217;est pas énorme. Quand ce sont quelques milliards, <strong>ce sont des milliers d&#8217;années de travail de personnes qui restent en suspens, pour le plaisir d&#8217;un seul homme.</strong></p>
<p>Enfin le plaisir : si il a plusieurs milliards en banque, c&#8217;est qu&#8217;il n&#8217;en dépense pas la majorité. C&#8217;est une décoration. Un blason. Et après il a redonné, excusez-moi, la moitié ! On l&#8217;admire pour ça ? Il annonce qu&#8217;à sa mort, une fois qu&#8217;il n&#8217;aura plus besoin de son aura sociale (putain, le pin&#8217;s à $100 milliards les gars), il va tout refiler, et on en parle comme d&#8217;un grand homme.</p>
<p>Donc le gars a créé une boite qui, dans notre système économico-social, lui a permis d&#8217;immobiliser l&#8217;équivalent du PIB d&#8217;un petit pays. Pour rien au final, car cela représente la somme de ce qu&#8217;il n&#8217;a PAS utilisé pendant toutes ces années. Il a gardé ce pognon pour la déco.</p>
<p>Et on l&#8217;applaudit pour ça ?</p>
<p>Quand quelqu&#8217;un est riche, il n&#8217;a pas de responsabilité aux yeux du public. Les gens l&#8217;envient. Ou le respectent. Mais personne ne pense qu&#8217;il a le devoir d&#8217;assumer les conséquences de ce qu&#8217;il fait (ou ne fait pas) avec son argent.</p>
<p>En plus, la grosse blague, c&#8217;est que les organisations philanthropiques aux USA ont le droit de gérer un portefeuille d&#8217;investissement. Ce qui donne <a href="http://www.agoravox.fr/tribune-libre/article/bill-gates-philanthrope-oui-mais-19525">ceci</a> :</p>
<blockquote><p>Comme la plupart des organisations philanthropiques, la Gates Foundation donne chaque année au moins 5% de son capital pour payer un minimum d’impôts. [...] Les 95% restants sont investis. [...] Et là, on en arrive au point soulevé par les journalistes du LA Times qui se sont aperçus que les entreprises dans lesquelles la fondation possédait des parts et dont les objectifs allaient à l’encontre de ses buts caritatifs représentaient 41 % de ses actifs, soit 8,7 milliards de dollars. </p></blockquote>
<p>En gros le gars fait du pognon en finançant des boites qui sont responsables de la destruction contre laquelle luttent les associations qu&#8217;il prétend aider.</p>
<p>Au passage, en donnant son pognon à la fondation, Bilou s&#8217;évite aussi de payer pas mal d&#8217;impôts. Argent qu&#8217;il réinvestit dans cette structure payant elle-même peu d&#8217;impôts qui peut alors investir dans ce qui arrange notre bonhomme. <a href="http://www.framablog.org/index.php/post/2009/03/05/fondation-bill-et-melinda-gates">C&#8217;est pour aider le monde</a>, ma bonne dame.</p>
<p>Donc Bill Gates est une ordure, et il y a des gens pour le défendre, parce que c&#8217;est une ordure socialement acceptable, et très visible. C&#8217;est vrai qu&#8217;en photo il a l&#8217;air super sympa.</p>
<p><strong>Mais ce n&#8217;est pas Bill Gates qui m&#8217;énerve le plus. Ce n&#8217;est qu&#8217;un exemple. Il y en a des milliers comme lui.</p>
<p>Ce qui m&#8217;énerve le plus, c&#8217;est chaque abruti qui oublie.</strong></p>
 <p><a href="http://sametmax.com/?flattrss_redirect&amp;id=10807&amp;md5=8d6d192f10b31fbf9c6327fc315d056c" title="Flattr" target="_blank"><img src="media/fc229d00.flattr-badge-large.png" data-original-source="http://sametmax.com/wp-content/plugins/flattr/img/flattr-badge-large.png" alt="flattr this!"/></a></p>


feed_id: http://sametmax.com/?p=11050
title: Attributs privés en Python
uri: Attributs-privés-en-Python
url: http://sametmax.com/variables-privees-en-python/
content:
<p><strong>Tout est accessible en Python. Il n&#8217;y a pas de variables privées.</strong></p>
<p>Quand on veut une variable à usage interne, la convention est de la nommer avec un underscore devant :</p>

<div class="wp_syntax"><div class="code"><pre class="python" style="font-family:monospace;"><span style="color: #ff7700;font-weight:bold;">class</span> Monique:
    <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;">self</span>._private = <span style="color: #483d8b;">&quot;lessons&quot;</span></pre></div></div>

<p>Ça n&#8217;empêche rien, mais les devs savent qu&#8217;il faut éviter d&#8217;utiliser cette variable car elle ne fait pas partie de l&#8217;API publique de la classe, et l&#8217;implémentation pourrait changer plus tard.</p>
<p>Comme <code>self</code>, c&#8217;est une convention forte puisque la plupart des libs de completion du code la prennent en compte.</p>
<p>Il existe une fonctionalité moins connue, qui fait que quand on utilise <strong>deux</strong> underscores, accéder directement à la variable lève un <code>AttributeError</code>:</p>

<div class="wp_syntax"><div class="code"><pre class="python" style="font-family:monospace;"><span style="color: #ff7700;font-weight:bold;">class</span> Monique:
    <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;">self</span>.__private = <span style="color: #483d8b;">&quot;lessons&quot;</span>
&nbsp;
m = Monique<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>m.__private<span style="color: black;">&#41;</span>
<span style="color: #008000;">AttributeError</span>: <span style="color: #483d8b;">'Monique'</span> <span style="color: #008000;">object</span> has no attribute <span style="color: #483d8b;">'__private'</span></pre></div></div>

<p>Du coup, quelques rares personnes ont utilisé cette feature pour émuler des attributs privés, ignorant le fait qu&#8217;on vous dit partout sur la toile que tout est accessible en Python.</p>
<p>C&#8217;est une mauvaise idée.</p>
<p>C&#8217;est une mauvaise idée car si votre objectif est la sécurité, ça ne sert à rien puisque votre variable est accessible de tas de manières détournées différentes. Par exemple :</p>

<div class="wp_syntax"><div class="code"><pre class="python" style="font-family:monospace;"><span style="color: #ff7700;font-weight:bold;">print</span><span style="color: black;">&#40;</span>m.<span style="color: #0000cd;">__dict__</span><span style="color: black;">&#91;</span><span style="color: #483d8b;">'_Monique__private'</span><span style="color: black;">&#93;</span><span style="color: black;">&#41;</span>
<span style="color: #808080; font-style: italic;"># lessons</span>
m.<span style="color: #0000cd;">__dict__</span><span style="color: black;">&#91;</span><span style="color: #483d8b;">'_Monique__private'</span><span style="color: black;">&#93;</span> = <span style="color: #008000;">None</span>
<span style="color: #ff7700;font-weight:bold;">print</span><span style="color: black;">&#40;</span>m.<span style="color: #0000cd;">__dict__</span><span style="color: black;">&#91;</span><span style="color: #483d8b;">'_Monique__private'</span><span style="color: black;">&#93;</span><span style="color: black;">&#41;</span>
<span style="color: #808080; font-style: italic;"># None</span></pre></div></div>

<p>C&#8217;est une mauvaise idée car si votre objectif est de créer une API publique, un seul underscore suffit.</p>
<p>Enfin c&#8217;est une mauvaise idée car si vous fournissez une lib qui ne correspond pas à un besoin, quelqu&#8217;un peut toujours contourner le problème en monkey patchant votre code le temps que vous trouviez une solution plus propre. En rendant la variable &#8220;privée&#8221;, vous rendez ceci plus difficile, sans le rendre impossible. Tout le monde y perd.</p>
 <p><a href="http://sametmax.com/?flattrss_redirect&amp;id=11050&amp;md5=5d90c65ec47a7409c17731671c438759" title="Flattr" target="_blank"><img src="media/fc229d00.flattr-badge-large.png" data-original-source="http://sametmax.com/wp-content/plugins/flattr/img/flattr-badge-large.png" alt="flattr this!"/></a></p>


feed_id: http://sametmax.com/?p=11013
title: Mais qui donc hérite de ma classe ?
uri: Mais-qui-donc-hérite-de-ma-classe
url: http://sametmax.com/mais-qui-donc-herite-de-ma-classe/
content:
<p>Pas une question que l&#8217;on se pose souvent, à moins d&#8217;avoir une lib qui expose une classe avec beaucoup d&#8217;introspection.</p>
<p>Ceci dit pour la culture G, je me suis dit que ça pourrait intéresser quelques geeks de savoir comment lister les enfants d&#8217;une classe, récursivement.</p>

<div class="wp_syntax"><div class="code"><pre class="python" style="font-family:monospace;"><span style="color: #ff7700;font-weight:bold;">def</span> subclasses<span style="color: black;">&#40;</span>cls, _found=<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span><span style="color: black;">&#41;</span>:
    <span style="color: #483d8b;">&quot;&quot;&quot; Retourne toutes les classes héritant d'une classe &quot;&quot;&quot;</span>
&nbsp;
    <span style="color: #808080; font-style: italic;"># On check que la classe est hérite bien de &quot;object&quot;.</span>
    <span style="color: #808080; font-style: italic;"># Utile uniquement pour un code 2.x qui utiliserait</span>
    <span style="color: #808080; font-style: italic;"># des old style classes, qui n'ont pas &quot;__subclasses__&quot;.</span>
    <span style="color: #ff7700;font-weight:bold;">if</span> <span style="color: #ff7700;font-weight:bold;">not</span> <span style="color: #008000;">isinstance</span><span style="color: black;">&#40;</span>cls, <span style="color: #008000;">type</span><span style="color: black;">&#41;</span>:
      <span style="color: #ff7700;font-weight:bold;">raise</span> <span style="color: #008000;">TypeError</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'subclasses works only with new-style classes'</span>
                            <span style="color: #483d8b;">', not %s'</span> <span style="color: #66cc66;">%</span> cls<span style="color: black;">&#41;</span>
&nbsp;
    <span style="color: #808080; font-style: italic;"># On va stocker les classes rencontrées dans un set</span>
    <span style="color: #808080; font-style: italic;"># pour éviter les doublons car on peut tomber sur</span>
    <span style="color: #808080; font-style: italic;"># un héritage en forme de diamant.</span>
    _found = _found <span style="color: #ff7700;font-weight:bold;">or</span> <span style="color: #008000;">set</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
&nbsp;
    <span style="color: #ff7700;font-weight:bold;">try</span>:
      subs = cls.__subclasses__<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
    <span style="color: #ff7700;font-weight:bold;">except</span> <span style="color: #008000;">TypeError</span>:  <span style="color: #808080; font-style: italic;"># Erreur levée si cls == type, petits vicieux</span>
      subs = cls.__subclasses__<span style="color: black;">&#40;</span>cls<span style="color: black;">&#41;</span>
&nbsp;
    <span style="color: #ff7700;font-weight:bold;">for</span> sub <span style="color: #ff7700;font-weight:bold;">in</span> subs:
        <span style="color: #ff7700;font-weight:bold;">if</span> sub <span style="color: #ff7700;font-weight:bold;">not</span> <span style="color: #ff7700;font-weight:bold;">in</span> _found:
            _found.<span style="color: black;">add</span><span style="color: black;">&#40;</span>sub<span style="color: black;">&#41;</span>
            <span style="color: #808080; font-style: italic;"># Un appel récursif comme on les aimes</span>
            subclasses<span style="color: black;">&#40;</span>sub, _found<span style="color: black;">&#41;</span>
&nbsp;
    <span style="color: #ff7700;font-weight:bold;">return</span> _found</pre></div></div>

<p>Pas très compliqué, donc, si on connait les piègeounets : ça ne marche pas sur les old syle classes, et il y a un traitement spécial pour <code>type</code>. Celà dit, qui utilise encore des old styles classes ? Et franchement lister les héritiers de <code>type</code>, à par pour satisfaire sa curiosité&#8230;</p>
<p>Aller, juste pour le fun, sur un virtualenv vierge:</p>

<div class="wp_syntax"><div class="code"><pre class="python" style="font-family:monospace;"><span style="color: #ff7700;font-weight:bold;">for</span> x <span style="color: #ff7700;font-weight:bold;">in</span> subclasses<span style="color: black;">&#40;</span><span style="color: #008000;">object</span><span style="color: black;">&#41;</span>:
    <span style="color: #ff7700;font-weight:bold;">print</span> x
<span style="color: #808080; font-style: italic;">## &lt;type 'callable-iterator'&gt;</span>
<span style="color: #808080; font-style: italic;">## &lt;class '_abcoll.Sized'&gt;</span>
<span style="color: #808080; font-style: italic;">## &lt;type 'exceptions.EOFError'&gt;</span>
<span style="color: #808080; font-style: italic;">## &lt;class '_abcoll.Mapping'&gt;</span>
<span style="color: #808080; font-style: italic;">## &lt;type 'exceptions.UnicodeWarning'&gt;</span>
<span style="color: #808080; font-style: italic;">## &lt;type 'exceptions.IOError'&gt;</span>
<span style="color: #808080; font-style: italic;">## &lt;type 'memoryview'&gt;</span>
<span style="color: #808080; font-style: italic;">## &lt;type 'code'&gt;</span>
<span style="color: #808080; font-style: italic;">## &lt;type 'exceptions.ImportWarning'&gt;</span>
<span style="color: #808080; font-style: italic;">## &lt;type 'sys.flags'&gt;</span>
<span style="color: #808080; font-style: italic;">## &lt;type 'iterator'&gt;</span>
<span style="color: #808080; font-style: italic;">## &lt;class 'warnings.catch_warnings'&gt;</span>
<span style="color: #808080; font-style: italic;">## &lt;type 'exceptions.Warning'&gt;</span>
<span style="color: #808080; font-style: italic;">## &lt;type 'exceptions.EnvironmentError'&gt;</span>
<span style="color: #808080; font-style: italic;">## &lt;type 'dict'&gt;</span>
<span style="color: #808080; font-style: italic;">## &lt;class 'encodings.utf_8.IncrementalEncoder'&gt;</span>
<span style="color: #808080; font-style: italic;">## &lt;type 'weakproxy'&gt;</span>
<span style="color: #808080; font-style: italic;">## &lt;class '_abcoll.Iterator'&gt;</span>
<span style="color: #808080; font-style: italic;">## &lt;type 'sys.version_info'&gt;</span>
<span style="color: #808080; font-style: italic;">## &lt;type 'posix.stat_result'&gt;</span>
<span style="color: #808080; font-style: italic;">## &lt;type 'frozenset'&gt;</span>
<span style="color: #808080; font-style: italic;">## &lt;class 'codecs.CodecInfo'&gt;</span>
<span style="color: #808080; font-style: italic;">## &lt;class 'zipimport.ZipImportError'&gt;</span>
<span style="color: #808080; font-style: italic;">## &lt;type 'exceptions.StopIteration'&gt;</span>
<span style="color: #808080; font-style: italic;">## &lt;type 'exceptions.ImportError'&gt;</span>
<span style="color: #808080; font-style: italic;">## &lt;class 'site._Printer'&gt;</span>
<span style="color: #808080; font-style: italic;">## &lt;type 'weakref'&gt;</span>
<span style="color: #808080; font-style: italic;">## &lt;class '_abcoll.Sequence'&gt;</span>
<span style="color: #808080; font-style: italic;">## &lt;type 'property'&gt;</span>
<span style="color: #808080; font-style: italic;">## &lt;class 'encodings.utf_8.IncrementalDecoder'&gt;</span>
<span style="color: #808080; font-style: italic;">## &lt;type 'imp.NullImporter'&gt;</span>
<span style="color: #808080; font-style: italic;">## &lt;type 'exceptions.TypeError'&gt;</span>
<span style="color: #808080; font-style: italic;">## &lt;type 'bytearray'&gt;</span>
<span style="color: #808080; font-style: italic;">## &lt;type 'int'&gt;</span>
<span style="color: #808080; font-style: italic;">## &lt;type 'exceptions.UnicodeTranslateError'&gt;</span>
<span style="color: #808080; font-style: italic;">## &lt;type 'instance'&gt;</span>
<span style="color: #808080; font-style: italic;">## &lt;type 'exceptions.BaseException'&gt;</span>
<span style="color: #808080; font-style: italic;">## &lt;type 'exceptions.SyntaxWarning'&gt;</span>
<span style="color: #808080; font-style: italic;">## &lt;type 'file'&gt;</span>
<span style="color: #808080; font-style: italic;">## &lt;class '_abcoll.MappingView'&gt;</span>
<span style="color: #808080; font-style: italic;">## &lt;class '_weakrefset._IterationGuard'&gt;</span>
<span style="color: #808080; font-style: italic;">## &lt;type 'exceptions.UnicodeDecodeError'&gt;</span>
<span style="color: #808080; font-style: italic;">## &lt;type 'classobj'&gt;</span>
<span style="color: #808080; font-style: italic;">## &lt;type 'exceptions.UnicodeError'&gt;</span>
<span style="color: #808080; font-style: italic;">## &lt;class 'abc.abstractproperty'&gt;</span>
<span style="color: #808080; font-style: italic;">## &lt;type 'exceptions.DeprecationWarning'&gt;</span>
<span style="color: #808080; font-style: italic;">## &lt;type 'complex'&gt;</span>
<span style="color: #808080; font-style: italic;">## &lt;type 'set'&gt;</span>
<span style="color: #808080; font-style: italic;">## &lt;type 'buffer'&gt;</span>
<span style="color: #808080; font-style: italic;">## &lt;type 'generator'&gt;</span>
<span style="color: #808080; font-style: italic;">## &lt;type 'exceptions.Exception'&gt;</span>
<span style="color: #808080; font-style: italic;">## &lt;class 'codecs.IncrementalDecoder'&gt;</span>
<span style="color: #808080; font-style: italic;">## &lt;type 'exceptions.UserWarning'&gt;</span>
<span style="color: #808080; font-style: italic;">## &lt;type 'staticmethod'&gt;</span>
<span style="color: #808080; font-style: italic;">## &lt;type 'exceptions.UnicodeEncodeError'&gt;</span>
<span style="color: #808080; font-style: italic;">## &lt;class 'encodings.CodecRegistryError'&gt;</span>
<span style="color: #808080; font-style: italic;">## &lt;class '_abcoll.MutableSequence'&gt;</span>
<span style="color: #808080; font-style: italic;">## &lt;class '_abcoll.KeysView'&gt;</span>
<span style="color: #808080; font-style: italic;">## &lt;type 'cell'&gt;</span>
<span style="color: #808080; font-style: italic;">## &lt;type 'exceptions.StandardError'&gt;</span>
<span style="color: #808080; font-style: italic;">## &lt;type 'enumerate'&gt;</span>
<span style="color: #808080; font-style: italic;">## &lt;type 'exceptions.KeyboardInterrupt'&gt;</span>
<span style="color: #808080; font-style: italic;">## &lt;type 'exceptions.BufferError'&gt;</span>
<span style="color: #808080; font-style: italic;">## &lt;class 'warnings.WarningMessage'&gt;</span>
<span style="color: #808080; font-style: italic;">## &lt;type 'EncodingMap'&gt;</span>
<span style="color: #808080; font-style: italic;">## &lt;class 'codecs.BufferedIncrementalDecoder'&gt;</span>
<span style="color: #808080; font-style: italic;">## &lt;type 'exceptions.ValueError'&gt;</span>
<span style="color: #808080; font-style: italic;">## &lt;class '_abcoll.Set'&gt;</span>
<span style="color: #808080; font-style: italic;">## &lt;type 'exceptions.MemoryError'&gt;</span>
<span style="color: #808080; font-style: italic;">## &lt;type 'exceptions.SystemExit'&gt;</span>
<span style="color: #808080; font-style: italic;">## &lt;type 'reversed'&gt;</span>
<span style="color: #808080; font-style: italic;">## &lt;class '_abcoll.Hashable'&gt;</span>
<span style="color: #808080; font-style: italic;">## &lt;type 'bool'&gt;</span>
<span style="color: #808080; font-style: italic;">## &lt;type 'classmethod'&gt;</span>
<span style="color: #808080; font-style: italic;">## &lt;type 'exceptions.ReferenceError'&gt;</span>
<span style="color: #808080; font-style: italic;">## &lt;type 'exceptions.GeneratorExit'&gt;</span>
<span style="color: #808080; font-style: italic;">## &lt;class '_abcoll.Container'&gt;</span>
<span style="color: #808080; font-style: italic;">## &lt;class '_abcoll.ItemsView'&gt;</span>
<span style="color: #808080; font-style: italic;">## &lt;type 'xrange'&gt;</span>
<span style="color: #808080; font-style: italic;">## &lt;type 'exceptions.PendingDeprecationWarning'&gt;</span>
<span style="color: #808080; font-style: italic;">## &lt;type 'basestring'&gt;</span>
<span style="color: #808080; font-style: italic;">## &lt;type 'exceptions.SystemError'&gt;</span>
<span style="color: #808080; font-style: italic;">## &lt;type 'exceptions.ZeroDivisionError'&gt;</span>
<span style="color: #808080; font-style: italic;">## &lt;class 'site.Quitter'&gt;</span>
<span style="color: #808080; font-style: italic;">## &lt;type 'long'&gt;</span>
<span style="color: #808080; font-style: italic;">## &lt;type 'NotImplementedType'&gt;</span>
<span style="color: #808080; font-style: italic;">## &lt;type 'super'&gt;</span>
<span style="color: #808080; font-style: italic;">## &lt;type 'fieldnameiterator'&gt;</span>
<span style="color: #808080; font-style: italic;">## &lt;class '_abcoll.Callable'&gt;</span>
<span style="color: #808080; font-style: italic;">## &lt;class '_abcoll.MutableSet'&gt;</span>
<span style="color: #808080; font-style: italic;">## &lt;type 'exceptions.ArithmeticError'&gt;</span>
<span style="color: #808080; font-style: italic;">## &lt;class 'abc.ABCMeta'&gt;</span>
<span style="color: #808080; font-style: italic;">## &lt;type 'exceptions.OverflowError'&gt;</span>
<span style="color: #808080; font-style: italic;">## &lt;type 'exceptions.FutureWarning'&gt;</span>
<span style="color: #808080; font-style: italic;">## &lt;type 'sys.float_info'&gt;</span>
<span style="color: #808080; font-style: italic;">## &lt;type 'formatteriterator'&gt;</span>
<span style="color: #808080; font-style: italic;">## &lt;type 'type'&gt;</span>
<span style="color: #808080; font-style: italic;">## &lt;type 'exceptions.AssertionError'&gt;</span>
<span style="color: #808080; font-style: italic;">## &lt;type 'traceback'&gt;</span>
<span style="color: #808080; font-style: italic;">## &lt;type 'exceptions.FloatingPointError'&gt;</span>
<span style="color: #808080; font-style: italic;">## &lt;type 'function'&gt;</span>
<span style="color: #808080; font-style: italic;">## &lt;type 'instancemethod'&gt;</span>
<span style="color: #808080; font-style: italic;">## &lt;type 'sys.long_info'&gt;</span>
<span style="color: #808080; font-style: italic;">## &lt;type 'builtin_function_or_method'&gt;</span>
<span style="color: #808080; font-style: italic;">## &lt;class 'signal.ItimerError'&gt;</span>
<span style="color: #808080; font-style: italic;">## &lt;class 'site._Helper'&gt;</span>
<span style="color: #808080; font-style: italic;">## &lt;type 'exceptions.LookupError'&gt;</span>
<span style="color: #808080; font-style: italic;">## &lt;type 'wrapper_descriptor'&gt;</span>
<span style="color: #808080; font-style: italic;">## &lt;type 'exceptions.KeyError'&gt;</span>
<span style="color: #808080; font-style: italic;">## &lt;class '_abcoll.ValuesView'&gt;</span>
<span style="color: #808080; font-style: italic;">## &lt;type 'slice'&gt;</span>
<span style="color: #808080; font-style: italic;">## &lt;class '_weakrefset.WeakSet'&gt;</span>
<span style="color: #808080; font-style: italic;">## &lt;type 'list'&gt;</span>
<span style="color: #808080; font-style: italic;">## &lt;type 'exceptions.IndexError'&gt;</span>
<span style="color: #808080; font-style: italic;">## &lt;type 'getset_descriptor'&gt;</span>
<span style="color: #808080; font-style: italic;">## &lt;type 'posix.statvfs_result'&gt;</span>
<span style="color: #808080; font-style: italic;">## &lt;type 'exceptions.SyntaxError'&gt;</span>
<span style="color: #808080; font-style: italic;">## &lt;type 'frame'&gt;</span>
<span style="color: #808080; font-style: italic;">## &lt;class 'codecs.BufferedIncrementalEncoder'&gt;</span>
<span style="color: #808080; font-style: italic;">## &lt;type 'PyCapsule'&gt;</span>
<span style="color: #808080; font-style: italic;">## &lt;type 'exceptions.IndentationError'&gt;</span>
<span style="color: #808080; font-style: italic;">## &lt;type 'NoneType'&gt;</span>
<span style="color: #808080; font-style: italic;">## &lt;type 'zipimport.zipimporter'&gt;</span>
<span style="color: #808080; font-style: italic;">## &lt;type 'member_descriptor'&gt;</span>
<span style="color: #808080; font-style: italic;">## &lt;type 'exceptions.AttributeError'&gt;</span>
<span style="color: #808080; font-style: italic;">## &lt;type 'ellipsis'&gt;</span>
<span style="color: #808080; font-style: italic;">## &lt;type 'exceptions.UnboundLocalError'&gt;</span>
<span style="color: #808080; font-style: italic;">## &lt;type 'unicode'&gt;</span>
<span style="color: #808080; font-style: italic;">## &lt;class '_abcoll.Iterable'&gt;</span>
<span style="color: #808080; font-style: italic;">## &lt;type 'exceptions.TabError'&gt;</span>
<span style="color: #808080; font-style: italic;">## &lt;type 'exceptions.NameError'&gt;</span>
<span style="color: #808080; font-style: italic;">## &lt;type 'tuple'&gt;</span>
<span style="color: #808080; font-style: italic;">## &lt;class 'warnings._OptionError'&gt;</span>
<span style="color: #808080; font-style: italic;">## &lt;type 'exceptions.NotImplementedError'&gt;</span>
<span style="color: #808080; font-style: italic;">## &lt;type 'exceptions.RuntimeWarning'&gt;</span>
<span style="color: #808080; font-style: italic;">## &lt;type 'str'&gt;</span>
<span style="color: #808080; font-style: italic;">## &lt;class '_abcoll.MutableMapping'&gt;</span>
<span style="color: #808080; font-style: italic;">## &lt;type 'exceptions.BytesWarning'&gt;</span>
<span style="color: #808080; font-style: italic;">## &lt;type 'module'&gt;</span>
<span style="color: #808080; font-style: italic;">## &lt;type 'exceptions.RuntimeError'&gt;</span>
<span style="color: #808080; font-style: italic;">## &lt;class 'codecs.IncrementalEncoder'&gt;</span>
<span style="color: #808080; font-style: italic;">## &lt;type 'float'&gt;</span>
<span style="color: #808080; font-style: italic;">## &lt;type 'exceptions.OSError'&gt;</span>
<span style="color: #808080; font-style: italic;">## &lt;type 'dictproxy'&gt;</span>
<span style="color: #808080; font-style: italic;">## &lt;type 'weakcallableproxy'&gt;</span></pre></div></div>

<p>Putain, ça c&#8217;est du remplissage ! 20minutes serait fier de moi.</p>
 <p><a href="http://sametmax.com/?flattrss_redirect&amp;id=11013&amp;md5=9198e16f2ea13b4d062b93aecc44825c" title="Flattr" target="_blank"><img src="media/fc229d00.flattr-badge-large.png" data-original-source="http://sametmax.com/wp-content/plugins/flattr/img/flattr-badge-large.png" alt="flattr this!"/></a></p>


feed_id: http://sametmax.com/?p=10535
title: Où il est présenté une méthode en Python pour afficher de la vidéo 3-bit dans son terminal.
uri: Où-il-est-présenté-une-méthode-en-Python-pour-afficher-de-la-vidé
url:
http://sametmax.com/ou-il-est-presente-une-methode-en-python-pour-afficher-de-la-video-3-bit-dans-son-terminal/

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>Il est des choses inutiles plus passionnantes que d&#8217;autres.<br />
Il semblerait que j&#8217;ai un petit faible pour les choses inutiles que je fais moi-même.</p>
<p>Car, afficher le contenu de sa webcam dans une console, ça ne sert clairement pas à grand chose mais j&#8217;ai pourtant crié très fort en serrant les poings quand j&#8217;y suis arrivé.</p>
<h2>Capturer de la vidéo en Python.</h2>
<p>Sous Nunux, il existe un joli petit paquet tout beau tout chaud qui permet de gérer du flux vidéo en Python : <strong>python-opencv</strong>.</p>
<p>Alors, zou :</p>

<div class="wp_syntax"><div 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</span> <span style="color: #c20cb9; font-weight: bold;">install</span> python-opencv</pre></div></div>

<p>Et pour afficher sa webcam, seules quelques lignes suffisent :</p>

<div class="wp_syntax"><div class="code"><pre class="python" style="font-family:monospace;"><span style="color: #808080; font-style: italic;">#-*- coding: utf-8 -*-</span>
&nbsp;
<span style="color: #ff7700;font-weight:bold;">import</span> cv2
&nbsp;
<span style="color: #808080; font-style: italic;"># Choix du périphérique de capture. 0 pour /dev/video0.</span>
cap = cv2.<span style="color: black;">VideoCapture</span><span style="color: black;">&#40;</span><span style="color: #ff4500;">0</span><span style="color: black;">&#41;</span>
&nbsp;
<span style="color: #ff7700;font-weight:bold;">while</span> <span style="color: #008000;">True</span>:
&nbsp;
    <span style="color: #808080; font-style: italic;"># On capture l'image.</span>
    ret,im = cap.<span style="color: black;">read</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
&nbsp;
    <span style="color: #808080; font-style: italic;"># On l'affiche.</span>
    cv2.<span style="color: black;">imshow</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'Ma Webcam à moi'</span>,im<span style="color: black;">&#41;</span>
&nbsp;
    <span style="color: #808080; font-style: italic;"># Et on attend 40 millisecondes pour avoir du 25 images par seconde.</span>
    key = cv2.<span style="color: black;">waitKey</span><span style="color: black;">&#40;</span><span style="color: #ff4500;">40</span><span style="color: black;">&#41;</span></pre></div></div>

<p>GO !!</p>
<p><a href="http://sametmax.com/ou-il-est-presente-une-methode-en-python-pour-afficher-de-la-video-3-bit-dans-son-terminal/mawebcam-2/" rel="attachment wp-att-10677"><img src="media/df3b550b.MaWebCam.jpg" data-original-source="http://sametmax.com/wp-content/uploads/2014/06/MaWebCam.jpg" alt="" title="MaWebCam" width="600" height="337" class="aligncenter size-full wp-image-10677" /></a></p>
<p>Je vais bien me garder de vous faire un tuto sur OpenCV tant cette librairie est puissante (comprendre : j&#8217;y panne que dalle). Si vous êtes curieux, vous pouvez aller faire un tour <a target="_blank" href="http://docs.opencv.org/trunk/doc/py_tutorials/py_tutorials.html">ici</a>.</p>
<p>Et, parce que j&#8217;avais bien d&#8217;autres choses plus intéressantes à faire que d&#8217;aller visiter le lien ci-dessus, j&#8217;ai fait un petit <em>print</em> du <em>im</em> précédent et découvert une liste toute mignonne dont voici la structure:</p>

<div class="wp_syntax"><div class="code"><pre class="python" style="font-family:monospace;"><span style="color: #808080; font-style: italic;"># Avec des valeurs pour les niveaux comprises entre 0 et 255.</span>
im<span style="color: black;">&#91;</span>n° de ligne<span style="color: black;">&#93;</span><span style="color: black;">&#91;</span>n° de colonne<span style="color: black;">&#93;</span><span style="color: black;">&#91;</span>niveau de bleu, niveau de vert, niveau de rouge<span style="color: black;">&#93;</span></pre></div></div>

<p>J&#8217;étais content car j&#8217;allais pouvoir récupérer les valeurs de chaque pixel de cette façon:</p>

<div class="wp_syntax"><div class="code"><pre class="python" style="font-family:monospace;"><span style="color: #ff7700;font-weight:bold;">for</span> ligne <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> colonne <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;
        niv_bleu = im<span style="color: black;">&#91;</span>ligne<span style="color: black;">&#93;</span><span style="color: black;">&#91;</span>colonne<span style="color: black;">&#93;</span><span style="color: black;">&#91;</span><span style="color: #ff4500;">0</span><span style="color: black;">&#93;</span>
        niv_vert = im<span style="color: black;">&#91;</span>ligne<span style="color: black;">&#93;</span><span style="color: black;">&#91;</span>colonne<span style="color: black;">&#93;</span><span style="color: black;">&#91;</span><span style="color: #ff4500;">1</span><span style="color: black;">&#93;</span>
        niv_rouge = im<span style="color: black;">&#91;</span>ligne<span style="color: black;">&#93;</span><span style="color: black;">&#91;</span>colonne<span style="color: black;">&#93;</span><span style="color: black;">&#91;</span><span style="color: #ff4500;">2</span><span style="color: black;">&#93;</span></pre></div></div>

<p>Si, pour un autre projet formidable, je n&#8217;avais pas eu à travailler avec une RasberryPi et ses petites cuisses de bébé, je pense que j&#8217;utiliserai encore cette méthode de bourrin.</p>
<p>Mais voilà, c&#8217;est juste ridicule quand on sait que la liste en question est en fait un array <a target="_blank" href="http://sametmax.com/les-bases-de-numpy/">numpy</a> et qu&#8217;il est 10, 100 fois plus rapide de faire:</p>

<div class="wp_syntax"><div class="code"><pre class="python" style="font-family:monospace;"><span style="color: #ff7700;font-weight:bold;">for</span> ligne <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> colonne <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;
        niv_bleu = im.<span style="color: black;">item</span><span style="color: black;">&#40;</span>ligne, colonne, <span style="color: #ff4500;">0</span><span style="color: black;">&#41;</span>
        niv_vert = im.<span style="color: black;">item</span><span style="color: black;">&#40;</span>ligne, colonne, <span style="color: #ff4500;">1</span><span style="color: black;">&#41;</span>
        niv_rouge = im.<span style="color: black;">item</span><span style="color: black;">&#40;</span>ligne, colonne, <span style="color: #ff4500;">2</span><span style="color: black;">&#41;</span></pre></div></div>

<p>Je n&#8217;en suis pas à me dire que la prochaine fois que j&#8217;achèterai un grille-pain, je lirai la notice avant de l&#8217;utiliser, mais presque&#8230;</p>
<p>Petite remarque en passant : les valeurs de rouge, de vert et de bleu étant comprise entre 0 et 255, cela nous donne 256 valeurs pour chaque couleur.<br />
Soit 256 x 256 x 256. Soit 2⁸ x 2⁸ x 2⁸. Soit 2²⁴ ==> les couleurs de notre flux sont codées par défaut sur 24 bits.</p>
<p>À noter qu&#8217;il est tout à fait possible d&#8217;analyser une image sans avoir à l&#8217;afficher, ce qui permet d&#8217;utiliser OpenCV dans un environnement sans gestionnaire de fenêtre.</p>
<h2>Afficher de la couleur dans la console.</h2>
<p>Bon. J&#8217;avais mes niveaux RVB pour chaque pixel. Il me fallait désormais afficher de la couleur dans la console.</p>
<p>Quelques requêtes Duck Duck Go plus tard, je découvre <a target="_blank" href="https://pypi.python.org/pypi/termcolor">termcolor</a> qui fait très bien le job mais dont on peut se passer en regardant <a target="_blank" href="http://www.developpez.net/forums/d448416/autres-langages/python-zope/general-python/mode-console-couleur-shell/#post2706250">les codes ANSI</a> de plus près.</p>
<p>Bien entendu, la grande majorité des consoles étant limitées à 8 couleurs, soit 2³, soit 3-bit je me suis restreint à cette qualité.</p>
<p>Démonstration :</p>
<p><a href="http://sametmax.com/ou-il-est-presente-une-methode-en-python-pour-afficher-de-la-video-3-bit-dans-son-terminal/color-python-console/" rel="attachment wp-att-10569"><img src="media/6fd02b9f.color-python-console.png" data-original-source="http://sametmax.com/wp-content/uploads/2014/06/color-python-console.png" alt="" title="" width="436" height="301" class="aligncenter size-full wp-image-10569" /></a></p>
<p>Il est possible d&#8217;obtenir beaucoup plus de couleurs en combinant les fonds, les caractères et les intensités comme le fait la <a target="_blank" href="http://caca.zoy.org/wiki/libcaca">libcaca</a>, mais on ne joue pas vraiment dans la même cour.</p>
<p>Perso, quand ça s&#8217;est affiché en rouge pour la première fois, j&#8217;ai eu des frissons partout. Parce qu&#8217;il faut bien comprendre que je n&#8217;ai toujours aucune idée de ce que &#8220;\033[&#8221; et autres &#8220;m&#8221; veulent dire. J&#8217;ai copié/collé, c&#8217;est tout. Et dans ces cas là, quand ça marche, c&#8217;est toujours la fête.</p>
<p>Ce qui pourrait être un problème, on le voit à l&#8217;image, c&#8217;est qu&#8217;une fois que j&#8217;ai écrit TATA YOYO en rouge, le prompt devient lui aussi rouge, et ainsi de suite à chaque changement de couleur. Pour remédier à ça, il faut ajouter <strong>\033[0m</strong> à la fin du texte à afficher pour que le reste soit écrit avec la couleur par défaut du terminal.</p>
<p>Démonstration :</p>
<p><a href="http://sametmax.com/ou-il-est-presente-une-methode-en-python-pour-afficher-de-la-video-3-bit-dans-son-terminal/color-python-console-2/" rel="attachment wp-att-10570"><img src="media/e409c858.color-python-console-2.png" data-original-source="http://sametmax.com/wp-content/uploads/2014/06/color-python-console-2.png" alt="" title="color-python-console-2" width="436" height="301" class="aligncenter size-full wp-image-10570" /></a></p>
<p>C&#8217;est d&#8217;ailleurs ce que fait <em>termcolor</em>, sauf que, dans notre cas, nous n&#8217;avons pas besoin de revenir à cette valeur par défaut à chaque affichage de pixel vu que le suivant sera lui aussi coloré.</p>
<p>Je vous en parle seulement parce que vous avez l&#8217;air sympa.</p>
<p>J&#8217;ajouterai qu&#8217;après avoir effectué un benchmark de folie exploitant brillamment les deux points qui clignotent à chaque seconde sur mon radio-réveil, il s&#8217;est avéré que la solution &#8220;maison&#8221; était plus performante que <em>termcolor</em> : mon choix était fait.</p>
<h2>Du pixel au █</h2>
<p>J&#8217;ai renoué contact avec le █ il n&#8217;y a pas si longtemps. Aussi étonnant que cela puisse paraître, alors que je baigne quasi quotidiennement dans l&#8217;informatique depuis 30 ans, il n&#8217;est pas impossible que notre dernière rencontre remonte à 1986 sur le Commodore 64 familial.</p>
<p>Pour vous donner une idée de l&#8217;émotion qui m&#8217;a traversé quand j&#8217;ai revu le █, vous pourriez très clairement user de l&#8217;expression &#8220;le █ d&#8217;Olivier&#8221; en lieu et place de &#8220;la madeleine de Proust&#8221; dans vos discussions. Mais, à l&#8217;oral, le █ passe mal, et c&#8217;est bien dommage.</p>
<p>Pour info, le pseudo unicode de █ c&#8217;est \u2588. </p>
<p>Et, pour afficher un █ en couleur, il suffit de faire comme vu au dessus.</p>
<p><a href="http://sametmax.com/ou-il-est-presente-une-methode-en-python-pour-afficher-de-la-video-3-bit-dans-son-terminal/python-color-console-3-1/" rel="attachment wp-att-10695"><img src="media/6acc808a.python-color-console-3-1.png" data-original-source="http://sametmax.com/wp-content/uploads/2014/06/python-color-console-3-1.png" alt="" title="python-color-console-3-1" width="436" height="301" class="aligncenter size-full wp-image-10695" /></a></p>
<p>Reste à trouver un moyen de passer des millions de couleurs potentielles de notre vidéo aux huit de notre console.</p>
<p>C&#8217;est là que vous allez comprendre pourquoi je me suis acharné avec mes captures d&#8217;écrans. C&#8217;était pour bien vous faire intégrer l&#8217;association entre les couleurs et la valeur qui les code. À savoir :</p>
<p><span style="color:#990000">1 : rouge</span><br />
<span style="color:#009900">2 : vert</span><br />
<span style="color:#999900">3 : jaune</span><br />
<span style="color:#000099">4 : bleu</span><br />
<span style="color:#990099">5 : violet</span><br />
<span style="color:#009999">6 : turquoise</span><br />
<span style="color:#999999">7 : blanc</span></p>
<p>Et là qu&#8217;est qu&#8217;on remarque ?<br />
Que cela respecte la synthèse additive si on attribue 1 au rouge, 2 au vert et 4 au bleu, bien entendu !</p>
<p>1 + 2 = 3 et en synthèse additive rouge + vert = jaune<br />
1 + 4 = 5 et en synthèse additive rouge + bleu = violet<br />
2 + 4 = 6 et en synthèse additive vert + bleu = turquoise<br />
1 + 2 + 4 = 7 et en synthèse additive rouge + vert + bleu = blanc</p>
<p>Mettez-vous à ma place: je venais de découvrir l&#8217;Amérique !</p>
<p>Bon, rétrospectivement, cela ne constitue vraiment rien d’extraordinaire en soi dans la mesure où c&#8217;est ce qui découle logiquement d&#8217;un codage sur 3 bits mis en place par un être humain qui a juste envie de faire simple plutôt que de faire compliqué.</p>
<p>Mais tout de même, sur le moment&#8230;<br />
&#8230; L&#8217;AMÉRIQUE BORDEL ! L&#8217;AMÉRIQUE !</p>
<p>Il devenait alors facile d&#8217;évaluer le degré de présence de chaque composante RVB d&#8217;un pixel puis de déterminer laquelle des 8 couleurs lui correspondait le plus.</p>
<p>Voilà comment je m&#8217;y suis pris :</p>

<div class="wp_syntax"><div class="code"><pre class="python" style="font-family:monospace;"><span style="color: #808080; font-style: italic;"># On initialise à 0 l'indice du pixel analysé </span>
indice_couleur = <span style="color: #ff4500;">0</span>
&nbsp;
<span style="color: #808080; font-style: italic;"># On analyse le niveau de Bleu du pixel.</span>
<span style="color: #808080; font-style: italic;"># S'il est au dessus du seuil...</span>
<span style="color: #ff7700;font-weight:bold;">if</span> img.<span style="color: black;">item</span><span style="color: black;">&#40;</span>ligne, colonne, <span style="color: #ff4500;">0</span><span style="color: black;">&#41;</span> <span style="color: #66cc66;">&gt;</span> seuil :
&nbsp;
    <span style="color: #808080; font-style: italic;">#...on ajoute 4 à l'indice.</span>
    indice_couleur += <span style="color: #ff4500;">4</span>
&nbsp;
<span style="color: #808080; font-style: italic;"># On analyse le niveau de Vert du pixel.</span>
<span style="color: #808080; font-style: italic;"># S'il est au dessus du seuil...</span>
<span style="color: #ff7700;font-weight:bold;">if</span> img.<span style="color: black;">item</span><span style="color: black;">&#40;</span>ligne, colonne, <span style="color: #ff4500;">1</span><span style="color: black;">&#41;</span> <span style="color: #66cc66;">&gt;</span> seuil :
&nbsp;
    <span style="color: #808080; font-style: italic;">#...on ajoute 2 à l'indice.</span>
    indice_couleur += <span style="color: #ff4500;">2</span>
&nbsp;
<span style="color: #808080; font-style: italic;"># On analyse le niveau de Rouge du pixel.</span>
<span style="color: #808080; font-style: italic;"># S'il est au dessus du seuil...</span>
<span style="color: #ff7700;font-weight:bold;">if</span> img.<span style="color: black;">item</span><span style="color: black;">&#40;</span>ligne, colonne, <span style="color: #ff4500;">2</span><span style="color: black;">&#41;</span> <span style="color: #66cc66;">&gt;</span> seuil :
&nbsp;
    <span style="color: #808080; font-style: italic;"># ...on ajoute 1 à l'indice.</span>
    indice_couleur += <span style="color: #ff4500;">1</span></pre></div></div>

<p>L&#8217;indice obtenu correspond alors au code couleur ANSI à utiliser !!</p>
<p>Je veux dire.</p>
<p>Tout de même.</p>
<p>C&#8217;est super, non ?</p>
<p>&#8230;</p>
<p>Hum&#8230;</p>
<p>&#8230;</p>
<p>Bien, bien&#8230;<br />
C&#8217;est bientôt fini, il me reste juste&#8230;</p>
<h2>Quelques remarques supplémentaires.</h2>
<p><strong>1)</strong> Le noir ANSI est en fait du gris, et c&#8217;est bien moche, j&#8217;ai donc préféré partir du principe que <strong>la console aurait un fond noir</strong> et afficher un &#8220;espace&#8221; pour chaque pixel noir.</p>
<p><strong>2)</strong> print(&#8220;\033[H\033[2J&#8221;) permet d&#8217;effacer la console comme le fait os.system(&#8216;clear&#8217;).<br />
Mais, j&#8217;imagine que ça devait faire trop de <em>033</em> dans le script pour moi, parce que, psychologiquement, ça ne passait pas.<br />
J&#8217;ai un peu discuté avec moi-même et on a fini par décider d&#8217;utiliser le <em>clear</em>.</p>
<p><strong>3)</strong> J&#8217;ai commencé par utiliser la concaténation pour ajouter mes █ colorés à mon <em>texte_image</em> final :</p>

<div class="wp_syntax"><div class="code"><pre class="python" style="font-family:monospace;">texte_image += u<span style="color: #483d8b;">&quot;<span style="color: #000099; font-weight: bold;">\0</span>33[3{0}m█&quot;</span>.<span style="color: black;">format</span><span style="color: black;">&#40;</span>indice_couleur<span style="color: black;">&#41;</span><span style="color: black;">&#41;</span></pre></div></div>

<p>Mais, Stack Overflow a tapé à la fenêtre et il m&#8217;a dit qu&#8217;il était beaucoup plus rapide de créer une liste puis d&#8217;en joindre les éléments.<br />
J&#8217;ai benchmarké avec mon radio-réveil.<br />
Stack Overflow avait raison.</p>
<p><strong>4)</strong> Par contre, ce que Stack Overflow s&#8217;était bien gardé de me dire, c&#8217;est que l&#8217;affichage en console avait ses propres limites internes.<br />
Bilan, après avoir optimisé mon code du mieux que je le pouvais, j&#8217;ai constaté que le script calculait de toutes façon les <em>texte_image</em> plus vite que la console ne pouvait les afficher.</p>
<p>Ce qui relève un peu du FAIL quand on y pense.</p>
<p>Donc, si vous avez une idée pour que ça ne scintille plus au delà de 25 lignes de hauteur, je suis preneur, sachant que je suis tout à fait à même d&#8217;entendre que j&#8217;ai fait n&#8217;importe quoi dès le début.</p>
<p><em>ÉDIT: Dans les commentaires, Tmonjalo a proposé une solution qui résout le problème du scintillement en faisant revenir le curseur en haut à gauche plutôt que d&#8217;effacer la console. J&#8217;ai donc édité le code en conséquence. Merci à lui.</em></p>
<h2>La totale.</h2>
<p>Voici le script final. Il est diffusé sous les termes de la très sérieuse <a target="_blank" href="http://fr.wikipedia.org/wiki/WTFPL">WTFPL</a>.</p>
<p>Les variables à modifier pour faire des tests sont le <em>seuil</em>, la <em>largeurOut</em> et la <em>hauteurOut</em>.</p>
<p>À noter aussi que si vous faite un petit&#8230;</p>

<div class="wp_syntax"><div class="code"><pre class="python" style="font-family:monospace;">cap = cv2.<span style="color: black;">VideoCapture</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">&quot;VotreFilm.avi&quot;</span><span style="color: black;">&#41;</span></pre></div></div>

<p>&#8230; au lieu d&#8217;ouvrir la webcam en <em>/dev/video0</em>, et bien vous allez voir <em>VotreFilm.avi</em> dans la console. Super génial !</p>

<div class="wp_syntax"><div class="code"><pre class="python" style="font-family:monospace;"><span style="color: #808080; font-style: italic;">#-*- coding: utf-8 -*-</span>
&nbsp;
<span style="color: #ff7700;font-weight:bold;">import</span> cv2
<span style="color: #ff7700;font-weight:bold;">import</span> <span style="color: #dc143c;">os</span>
&nbsp;
<span style="color: #808080; font-style: italic;"># Définition du flux capturé.</span>
<span style="color: #808080; font-style: italic;"># Comme elle sera, de toutes façons, retaillée à la baisse,</span>
<span style="color: #808080; font-style: italic;"># elle est fixée à la valeur la plus petite supportée par la webcam.</span>
<span style="color: #808080; font-style: italic;"># À noter que cette valeur minimale peut varier en fonction de votre cam.</span>
largeurIn = <span style="color: #ff4500;">160</span>
hauteurIn = <span style="color: #ff4500;">120</span>
&nbsp;
<span style="color: #808080; font-style: italic;"># Définition du flux qui s'affichera en console.</span>
<span style="color: #808080; font-style: italic;"># À savoir le nombre de caractères en largeur et en hauteur.</span>
largeurOut = <span style="color: #ff4500;">60</span>
hauteurOut = <span style="color: #ff4500;">20</span>
&nbsp;
<span style="color: #808080; font-style: italic;"># Seuil de présence des couleurs rouge, vert, bleu dans un pixel. </span>
<span style="color: #808080; font-style: italic;"># Entre 0 et 255.</span>
seuil = <span style="color: #ff4500;">120</span>
&nbsp;
<span style="color: #808080; font-style: italic;"># Choix du périphérique de capture.</span>
<span style="color: #808080; font-style: italic;"># Ici /dev/video0</span>
cap = cv2.<span style="color: black;">VideoCapture</span><span style="color: black;">&#40;</span><span style="color: #ff4500;">0</span><span style="color: black;">&#41;</span>
&nbsp;
<span style="color: #808080; font-style: italic;"># Configuration de la définition du flux.</span>
cap.<span style="color: #008000;">set</span><span style="color: black;">&#40;</span><span style="color: #ff4500;">3</span>, largeurIn<span style="color: black;">&#41;</span>
cap.<span style="color: #008000;">set</span><span style="color: black;">&#40;</span><span style="color: #ff4500;">4</span>, hauteurIn<span style="color: black;">&#41;</span>
&nbsp;
<span style="color: #808080; font-style: italic;"># On efface la console.</span>
<span style="color: #dc143c;">os</span>.<span style="color: black;">system</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'clear'</span><span style="color: black;">&#41;</span>
&nbsp;
<span style="color: #808080; font-style: italic;"># On définit une position de référence pour le curseur.</span>
<span style="color: #808080; font-style: italic;"># En haut à gauche, donc, puisqu'on vient juste d'effacer la console.</span>
<span style="color: #ff7700;font-weight:bold;">print</span> <span style="color: black;">&#40;</span><span style="color: #483d8b;">'<span style="color: #000099; font-weight: bold;">\0</span>33[s'</span><span style="color: black;">&#41;</span>
&nbsp;
<span style="color: #808080; font-style: italic;"># Pendant... tout le temps...</span>
<span style="color: #ff7700;font-weight:bold;">while</span> <span style="color: #008000;">True</span>:
&nbsp;
    <span style="color: #808080; font-style: italic;"># On capture une image.</span>
    ret, img = cap.<span style="color: black;">read</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
&nbsp;
    <span style="color: #808080; font-style: italic;"># On retaille l'image capturée.</span>
    img = cv2.<span style="color: black;">resize</span><span style="color: black;">&#40;</span>img,<span style="color: black;">&#40;</span>largeurOut, hauteurOut<span style="color: black;">&#41;</span><span style="color: black;">&#41;</span>
&nbsp;
    <span style="color: #808080; font-style: italic;"># On initialise une liste qui contiendra tous les éléments de l'image</span>
    liste_image = <span style="color: black;">&#91;</span><span style="color: black;">&#93;</span>
&nbsp;
    <span style="color: #808080; font-style: italic;"># Pour chaque ligne de l'image.</span>
    <span style="color: #ff7700;font-weight:bold;">for</span> ligne <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: #808080; font-style: italic;"># Pour chaque colonne de chaque ligne.</span>
        <span style="color: #ff7700;font-weight:bold;">for</span> colonne <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;
            <span style="color: #808080; font-style: italic;"># On initialise à 0 l'indice du pixel analysé </span>
            indice_couleur = <span style="color: #ff4500;">0</span>
&nbsp;
            <span style="color: #808080; font-style: italic;"># On analyse le niveau de bleu du pixel.</span>
            <span style="color: #808080; font-style: italic;"># S'il est au dessus du seuil...</span>
            <span style="color: #ff7700;font-weight:bold;">if</span> img.<span style="color: black;">item</span><span style="color: black;">&#40;</span>ligne, colonne, <span style="color: #ff4500;">0</span><span style="color: black;">&#41;</span> <span style="color: #66cc66;">&gt;</span> seuil :
&nbsp;
                <span style="color: #808080; font-style: italic;">#...on ajoute 4 à l'indice.</span>
                indice_couleur += <span style="color: #ff4500;">4</span>
&nbsp;
            <span style="color: #808080; font-style: italic;"># On analyse le niveau de bleu du pixel.</span>
            <span style="color: #808080; font-style: italic;"># S'il est au dessus du seuil...</span>
            <span style="color: #ff7700;font-weight:bold;">if</span> img.<span style="color: black;">item</span><span style="color: black;">&#40;</span>ligne, colonne, <span style="color: #ff4500;">1</span><span style="color: black;">&#41;</span> <span style="color: #66cc66;">&gt;</span> seuil :
&nbsp;
                <span style="color: #808080; font-style: italic;">#...on ajoute 2 à l'indice.</span>
                indice_couleur += <span style="color: #ff4500;">2</span>
&nbsp;
            <span style="color: #808080; font-style: italic;"># On analyse le niveau de bleu du pixel.</span>
            <span style="color: #808080; font-style: italic;"># S'il est au dessus du seuil...</span>
            <span style="color: #ff7700;font-weight:bold;">if</span> img.<span style="color: black;">item</span><span style="color: black;">&#40;</span>ligne, colonne, <span style="color: #ff4500;">2</span><span style="color: black;">&#41;</span> <span style="color: #66cc66;">&gt;</span> seuil :
&nbsp;
                <span style="color: #808080; font-style: italic;"># ...on ajoute 1 à l'indice.</span>
                indice_couleur += <span style="color: #ff4500;">1</span>
&nbsp;
            <span style="color: #808080; font-style: italic;"># Si l'indice obtenu est différent de 0...</span>
            <span style="color: #ff7700;font-weight:bold;">if</span> indice_couleur:
&nbsp;
                <span style="color: #808080; font-style: italic;"># ...on ajoute un █ coloré à la liste.</span>
                liste_image.<span style="color: black;">append</span><span style="color: black;">&#40;</span>u<span style="color: #483d8b;">&quot;<span style="color: #000099; font-weight: bold;">\0</span>33[3{0}m█&quot;</span>.<span style="color: black;">format</span><span style="color: black;">&#40;</span>indice_couleur<span style="color: black;">&#41;</span><span style="color: black;">&#41;</span>
&nbsp;
            <span style="color: #808080; font-style: italic;"># Si l'indice est égal à 0...</span>
            <span style="color: #ff7700;font-weight:bold;">if</span> <span style="color: #ff7700;font-weight:bold;">not</span> indice_couleur:
&nbsp;
                <span style="color: #808080; font-style: italic;"># ...on ajoute un espace (noir ?) à la liste.</span>
                liste_image.<span style="color: black;">append</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">&quot; &quot;</span><span style="color: black;">&#41;</span>
&nbsp;
        <span style="color: #808080; font-style: italic;"># On fait en sorte que le terminal retrouve sa couleur initiale</span>
        liste_image.<span style="color: black;">append</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">&quot;<span style="color: #000099; font-weight: bold;">\n</span><span style="color: #000099; font-weight: bold;">\0</span>33[00m&quot;</span><span style="color: black;">&#41;</span>
&nbsp;
    <span style="color: #808080; font-style: italic;"># On produit un string en mettant bout à bout tous les éléments de la liste</span>
    texte_image = <span style="color: #483d8b;">''</span>.<span style="color: black;">join</span><span style="color: black;">&#40;</span>liste_image<span style="color: black;">&#41;</span>
&nbsp;
    <span style="color: #808080; font-style: italic;"># On affiche l'image.</span>
    <span style="color: #ff7700;font-weight:bold;">print</span><span style="color: black;">&#40;</span>texte_image<span style="color: black;">&#41;</span>
&nbsp;
    <span style="color: #808080; font-style: italic;"># On replace le curseur à la position de référence.</span>
    <span style="color: #ff7700;font-weight:bold;">print</span> <span style="color: black;">&#40;</span><span style="color: #483d8b;">'<span style="color: #000099; font-weight: bold;">\0</span>33[u'</span><span style="color: black;">&#41;</span>
&nbsp;
    <span style="color: #808080; font-style: italic;"># On attend 40 millisecondes pour obtenir du 25 images par seconde.</span>
    key = cv2.<span style="color: black;">waitKey</span><span style="color: black;">&#40;</span><span style="color: #ff4500;">40</span><span style="color: black;">&#41;</span></pre></div></div>

<h2> Des vidéos ! Des vidéos !</h2>
<p>Voici une petite vidéo réalisée pour promouvoir un événement à nous. À partir de la 44ème seconde, on peut m&#8217;y voir coiffé d&#8217;un masque de soudeur en train de faire tenir au plafond un donut géant au moyen d&#8217;une batte de base-ball en aluminium :</p>
<p><iframe src="//player.vimeo.com/video/98458658" width="500" height="414" frameborder="0" webkitallowfullscreen mozallowfullscreen allowfullscreen></iframe></p>
<p>Pour plus d&#8217;information sur cet événement vous pouvez allez voir <a target="_blank" href="http://labomedia.org/du-21-au-26-juillet-2014-open-atelier-version-longue-n4/">ici</a> et admirer, par la même occasion, notre magnifique affiche réalisée en pur Python.</p>
<p>Enfin, compte tenu des tauliers du site, je ne pouvais passer à côté de la figure imposée.<br />
Je vous propose donc un extrait de Deep 3-bit, hommage appuyé à <a target="_blank" href="http://www.ljudmila.org/~vuk/">Vuk Cosic</a> et son légendaire <a target="_blank" href="http://www.youtube.com/watch?v=DGktK6OxfJI">Deep ASCII</a>.</p>
<p><iframe src="//player.vimeo.com/video/98451297" width="500" height="418" frameborder="0" webkitallowfullscreen mozallowfullscreen allowfullscreen></iframe></p>
 <p><a href="http://sametmax.com/?flattrss_redirect&amp;id=10535&amp;md5=475e855a6e9fa095b06e8ec82241c132" title="Flattr" target="_blank"><img src="media/fc229d00.flattr-badge-large.png" data-original-source="http://sametmax.com/wp-content/plugins/flattr/img/flattr-badge-large.png" alt="flattr this!"/></a></p>


feed_id: http://sametmax.com/?p=11061
title: Un petit tour des frameworks Web Python
uri: Un-petit-tour-des-frameworks-Web-Python
url: http://sametmax.com/un-petit-tours-des-frameworks-web-python/
content:
<p><a href="http://bottlepy.org/">Bottle</a> : le plus petit disponible (tient dans un fichier). Génial pour du code jetable ou des petits sites, etc. Fantastique outil d&#8217;enseignement et d&#8217;apprentissage. En plus, ça tient plutôt bien la charge compte tenu de la taille.</p>
<p><a href="https://www.djangoproject.com/">django</a> : le framework Python le plus connu. On peut tout faire avec, l’écosystème est fantastique (il y a des apps djangos tierces partout pour tout et n&#8217;importe quoi, c&#8217;est un truc de fou). Mais le maîtriser prend du temps. Efficace pour construire un site web avec beaucoup de logique personnalisée. Au final, si on veut être sérieux dans en programmation Web avec Python, on finit toujours par passer par Django.</p>
<p><a href="http://flask.pocoo.org/docs/">flask</a> : sa taille est entre django et bottle, et avec tout ce qu&#8217;il faut pour faire un site de taille moyenne. Il commence à avoir pas mal de plugins disponibles un peu partout sur la toile, et est une alternative très sympa quand on n&#8217;a pas besoin de charger les 3 tonnes de Django.</p>
<p><a href="http://web2py.com/">wep2py</a> : se positionne en alternative à Django et Flask, mais avec une philosophie très différente. Pas mal de magie (à la rails), des interfaces graphiques pour l&#8217;admin, composants très couplés mais très intégrés&#8230; Je ne suis pas fan, mais on m&#8217;en a dit du bien.</p>
<p><a href="http://docs.cherrypy.org/">cherrypy</a> : un framework WSGI pure Python qui a de très bonnes perfs sans rien rajouter, même en prod. Mais depuis qu&#8217;ils ont rendu disponible sa partie serveur et qu&#8217;on peut l&#8217;utiliser pour Django/flask/bottle, ça ne vaut plus le coup d&#8217;utiliser sa partie framework qui n&#8217;a rien de fantastique.</p>
<p><a href="http://docs.pylonsproject.org/projects/pyramid/">pyramid</a> : le plus gros compétiteur de Django en termes de fonctionnalités. Beaucoup moins monolithique, et bien plus flexible, donnant pas mal de contrôle. Mais l&#8217;intégration des composants de django le rend beaucoup plus facile à maîtriser, et son écosystème est 100 fois meilleur (j&#8217;ai toujours été en froid avec l&#8217;écosystème Zope).</p>
<p><a href="http://twistedmatrix.com/">twisted</a> un framework internet asynchrone en pur Python. J&#8217;ai dit INTERNET, pas WEB. On peut faire HTTP, mais aussi SSH, IMAP, FTP, et à peu près n&#8217;importe quoi. Surpuissant, et ses performances sont incroyables. Son API est aussi la plus merdique de tout l&#8217;univers Python. Apprendre et utiliser Twisted, c&#8217;est comme se branler avec des gants de boxe en récitant l&#8217;alphabet à l&#8217;envers.</p>
<p><a href="http://tornado.readthedocs.org/">tornado</a> : framework Web asynchrone. Techniquement le seul compétiteur de nodejs en pur Python (pas de gevent, d&#8217;extension C, etc) si on exclue crossbar encore trop immature. L&#8217;API n&#8217;est pas incroyable, mais pas trop dur, et les perfs sont bonnes. Les Websocket marchent clé en main.</p>
<p><a href="http://cyclone.io/">cyclone</a> : tornado qui tourne dans l&#8217;event loop de twisted. Pour pouvoir utiliser l&#8217;API de tornado pour le Web, mais les outils de twisted pour le reste. Si vous êtes capable d&#8217;utiliser ça à son plein potentiel, qu&#8217;est-ce que vous foutez à lire cet article ?</p>
<p><a href="http://webpy.org/">webpy</a> : un ancêtre. Un fantôme du passé. Do not use.</p>
<p>Il existe ensuite tout un tas d&#8217;expérimentations de framework avec asyncio, gevent, etc. C&#8217;est plus du bricolage qu&#8217;autre chose pour le moment, alors je ne recommande pas de base un gros projet dessus à moins d&#8217;être déjà très à l&#8217;aise avec la prog web python.</p>
 <p><a href="http://sametmax.com/?flattrss_redirect&amp;id=11061&amp;md5=45ee0885b281cf798bb1b0e1662ee791" title="Flattr" target="_blank"><img src="media/fc229d00.flattr-badge-large.png" data-original-source="http://sametmax.com/wp-content/plugins/flattr/img/flattr-badge-large.png" alt="flattr this!"/></a></p>


feed_id: http://sametmax.com/?p=11073
title: Prostitution masculine
uri: Prostitution-masculine
url: http://sametmax.com/prostitution-masculine/
content:
<p>Un de mes coloc est un ancien gogo dancer, et je le soupçonne de bien plus. Mais dans les apparences, c&#8217;est un garçon très propre sur lui, et depuis quand je rencontre de nouvelles personnes, je les imagine toujours dans des situations coquines.</p>
<p>Il se trouve que quand on parle de pute, on pense généralement à des nanas, pas à des gigolos, car il y en a beaucoup moins. Enfin en France, parce que quand je travaillais en Afrique, j&#8217;en ai vu des blondes venir se payer leur bois d&#8217;ébène&#8230;</p>
<p>Cependant, il existe une prostitution bien moins reconnue : la prostitution sociale. Coucher pour réussir, pour bénéficier du train de vie d&#8217;une personne, pour éviter des ennuis, pour ne pas perdre quelque chose, en échange d&#8217;un service, d&#8217;un bien ou d&#8217;un avantage&#8230;</p>
<p>Ces putes là sont très nombreuses, parfaitement socialement acceptées, absolument pas assumées, et les mecs sont beaucoup plus représentés.</p>
<p>Non pas qu&#8217;il n&#8217;y a pas de nanas putes sociales, elles sont légions, mais je trouve qu&#8217;on a tendance à omettre trop souvent les deux côtés de la pièce. L&#8217;homme pute est là, et pas juste métaphoriquement.</p>
<p>Comme un de mes potes avocat qui a fisté son batonier car c&#8217;était bon pour sa carrière.</p>
<p>Comme un des potes de Max qui a couché régulièrement avec un thon ce mois-ci car il avait besoin de sa voiture et qu&#8217;elle garde les chiens de sa copine (sic) pendant quelques mois.</p>
<p>Et qui n&#8217;a pas dans son entourage ce type qui se force à se taper sa gonzesse dont il ne veut plus pour éviter qu&#8217;elle l&#8217;emmerde, qu&#8217;elle divorce, qu&#8217;elle prenne les enfants/l&#8217;argent, etc ?</p>
<p>On a tous des tas de gens autour de nous, hommes ou femmes, qui utilisent leur cul pour obtenir quelque chose.</p>
<p>Mais ça ne se limite pas à ça.</p>
<p>Cette semaine, en boîte, une meuf a proposé de l&#8217;argent à Max pour qu&#8217;il rentre avec elle.</p>
<p>Je suis un peu jaloux, ça ne m&#8217;est jamais arrivé.</p>
 <p><a href="http://sametmax.com/?flattrss_redirect&amp;id=11073&amp;md5=2d7660a57b544651740f0a4a39a8f8cd" title="Flattr" target="_blank"><img src="media/fc229d00.flattr-badge-large.png" data-original-source="http://sametmax.com/wp-content/plugins/flattr/img/flattr-badge-large.png" alt="flattr this!"/></a></p>


feed_id: http://sametmax.com/?p=11078
title: Pourquoi sametmax.com utilise WordPress ?
uri: Pourquoi-sametmax-com-utilise-WordPress
url: http://sametmax.com/pourquoi-sametmax-com-utilise-wordpress/
content:
<blockquote><p>WordPress est le pire des blog engines, à l&#8217;exception de tous les autres</p></blockquote>
<p><em>Churchill</em></p>
<h2>Il y a des solutions en Python, pourquoi vous ne les utilisez pas ?</h2>
<p>Parce que c&#8217;est trop de taff. Si on veut une feature, il faudra la coder puisque ces blogs n&#8217;ont pas de communauté de plugins qui approche 1/1000eme de celle de WordPress. </p>
<p>Par exemple, depuis l&#8217;ouverture du blog, on a installé 3 plugins anti-spam, un plugin pour adapter Varnish, un pour avoir des stats sans Google analytics, un formulaire de contact, un live preview pour les commentaires, un outil de notes collaboratives, etc. On a aussi changé 10 fois de thèmes, et custo celui-là à mort (je sais, il est moche :)).</p>
<p>Je ne connais aucune solution de blog <strong>Python ou non</strong> qui permette de faire tout ça out of the box.</p>
<p>Or, durant ces 2 dernières années, on a publié plus de 742 articles. Pas un article par jour, mais pas loin. On a une vie assez remplie également derrière. Et même si on a la chance de pouvoir gérer notre emploi du temps, ce dont on use et abuse, les journées n&#8217;ont que 37h.</p>
<h2>Je connais un super générateur de blog statique qui&#8230;</h2>
<p>Pas de commentaires indexable par Google. Pas de formulaire de contact. Besoin de connaître un VCS pour les contributeurs. Pas de gestion de droits&#8230; J&#8217;ai failli rajouter pas de moteur de recherche mais c&#8217;est vrai que le notre est pourri.</p>
<h2>Pourquoi pas une plateforme en ligne alors ?</h2>
<p>On veut avoir le contrôle de nos données pour éviter de se faire fermer violemment en cas de problème.</p>
<h2>Ok, mais pourquoi pas Dotclear, Ghost, etc ?</h2>
<p>J&#8217;en ai  essayé par mal. Aucun n&#8217;a le nombre de plugins et thèmes de WordPress. Aucun n&#8217;a autant de features. Aucun n&#8217;a autant de tutos. Et rien ne prouve qu&#8217;on aura moins de problèmes avec.</p>
<p>Tout va toujours bien jusqu&#8217;à ce qu&#8217;on dépasse 10 visiteurs par jour.</p>
<h2>Alors arrêtez de vous plaindre !</h2>
<p>Si tu ne veux pas nous entendre nous plaindre, ne nous lis pas. Il y a un petit bouton en forme de croix en haut à droite de ton tab, tu vas voir, c&#8217;est magique.</p>
<h2>Sinon, pas envie de coder votre solution ?</h2>
<p>Très envie. Mais contrairement à la plupart des gens, je ne pense pas que créer un moteur de blog ce soit &#8220;3 jours de boulot&#8221;.</p>
<p>C&#8217;est un taff énorme, et là, tout de suite, c&#8217;est mort.</p>
<p>Et il existe 20000 personnes qui en ont fait un, donc pour avoir une raison de coder quelque chose, il faudrait faire quelque chose de vraiment innovant :</p>
<ul>
<li>Une plateforme qui fasse blog + shaarly + microblogging.</li>
<li>L&#8217;éditeur ressemble à celui de ghost, mais part du principe que le mec aux commandes sait ce qu&#8217;il fait (on n&#8217;empêche pas les iframes, le HTML sensible, etc).</li>
<li>Même éditeur pour les articles, les liens et les microblogs.</li>
<li>Si on poste moins de 140 chars => microblog automatique. Cross posté sur twitter ou autre.</li>
<li>Première ligne est un lien => shaarly.</li>
<li>Sinon c&#8217;est un article.</li>
<li>Pas de categ, uniquement des tags : #microblog, #link, #article et ceux que l&#8217;auteur met. Pas de truc compliqué, tagger c&#8217;est juste mettre #mot dans le corps du post. #tag link automatiquement sur une liste des posts avec tous ces tags.</li>
<li>Un moteur de recherche qui ne soit pas inutile.</li>
<li>Anti spam intégré et efficace.</li>
<li>Tout est en Markdown, même les commentaires. Possibilité de faire un @mention dans les commentaires, comme dans Discourse</li>
<li>RSS pour tout : tags, commentaires, recherche&#8230;</li>
<li>Toute page est visible en JSON.</li>
<li>API de post en JSON.</li>
<li>API de migrations/synchronisation de blog d&#8217;un serveur à l&#8217;autre en JSON. Si on veut changer d&#8217;hébergeur, on peut juste setuper un nouveau blog, le linker au précédent, et attendre qu&#8217;il se synchro, puis basculer le nom de domaine.</li>
<li>Versioning des articles et des commentaires.</li>
<li>Versioning du code du blog, notamment les modifs manuelles des thèmes.</li>
<li>Cron jobs intégrés pour les opérations longues.</li>
<li>Cache in memory intégré pour qu&#8217;on puisse survivre à sebsauvage sans avoir à mettre varnish.</li>
<li>Auto backup régulier.</li>
<li>Dump statique du blog généré automatiquement et disponible en téléchargement.</li>
<li>Tous les articles sont téléchargebles en Markdown et en PDF.</li>
<li>Un bouton &#8220;Dire merci&#8221; en bas du blog qui ouvre une fenêtre modale. Propose flattr, bitcoin, gittip, des liens perso, et juste un bouton &#8220;dire seulement merci&#8221; qui fait une notif dans l&#8217;admin. Pas besoin de laisser un commentaire pour ça, mais ça remonte le moral.</li>
<li>Un bouton share qui ouvre une modale avec tous les boutons de partage de réseaux sociaux. Si on ne clique pas dessus, ils ne sont pas chargés ni visibles.</li>
<li>Un bouton &#8220;proposez une amélioration&#8221; qui permet à un lecteur de modifier l&#8217;article (par exemple pour corriger l&#8217;orthographe), et on reçoit le diff dans l&#8217;admin qu&#8217;on peut merger. Si on merge, une ligne crédite le lecteur dans les commentaires.</li>
<li>Un système de ressources externes (images, videos, etc) qui permet de mettre un lien source qui apparaît en hover. Si on donne l&#8217;URL de la ressource, des metadata sont pullées automatiquement. Sinon, on peut tagger manuellement la ressource. On peut chercher efficacement toutes les ressources et par exemple répondre à une question du type &#8220;est-ce que j&#8217;ai déjà posté ça&#8221; ?</li>
<li>Un form d&#8217;upload d&#8217;image décent qui permet de DL depuis une URL ou le disque et modifier l&#8217;image inplace pour faire des crops, rotates, etc.</li>
<li>Un réseau entre tous les utilisateurs du blog qui peuvent s&#8217;ajouter et se follow comme sur dispora.</li>
<li>Une version mobile intégrée.</li>
<li>Un lecteur de PDF et de video intégré.</li>
<li>De l&#8217;autocompletion partout.</li>
<li>Articles multilingues.</li>
<li>Un chat intégré, discret, avec autocompletion sur les articles du blog, un système de quote, d&#8217;embed de media, etc.</li>
<li>Un formulaire de contact propre, simple, qui marche.</li>
<li>Un système de droits intégré mais simple pour gérer les contributeurs.</i>
<li>Un système de comment interne pour toutes les pages d&#8217;admin et les articles. Sérieux, pourquoi c&#8217;est pas en standard partout ?</li>
<li>Un système de chat interne.</li>
<li>Les articles peuvent s&#8217;éditer en mode colaboratif comme dans etherpad, au lieu de s&#8217;écraser mutuellement la gueule.</li>
<li>Pas de features inutiles comme un champ de recherche in page (sérieusement, WordPress, CTRL+F ça sert à quoi ?) ou de correction orthographique (ghost, mon browser le fait déjà, mon éditeur le fait déjà, Open Office le fait déjà, et ça ne change rien).</li>
<li>Le blog peut s&#8217;installer ainsi : DL, unzip, run. Rien d&#8217;autre à faire.</li>
<li>Un scrap book intégré pour y mettre les ressources qu&#8217;on veut utiliser pour ses publications futures.</li>
<li>Analytics basique intégré.</li>
<li>Edition facile des thèmes in browser. Avec un éditeur décent, type ACE, et une gestion du versioning avec diff et rollback.</li>
<li>Console qui permet d&#8217;obtenir un shell donnant accès au code du blog, à la base de données, etc. Typiquement sur un blog en Python, ce serait un remote IPython avec l&#8217;ORM préchargé. Pour toutes ces fois où je voulais faire un truc one shot que WordPress permettait pas et qui aurait été si facile à faire à la main. Évidement, ne pourrait marcher que si SSL est activé ou qu&#8217;on a installé une extension navigateur pour faire de l&#8217;OTR car en pure JS, c&#8217;est mort.</li>
</ul>
<p>Ça, ça serait un truc qui vaudrait le coup de se faire chier à migrer dessus.</p>
<p>3 jours de dev ? Mouarf. Comptez plutôt 3 ans, pendant tous ces jours de libre. Autant dire que ça n&#8217;arrivera pas.</p>
<p>Bref, je vous laisse vous référer à l&#8217;image d&#8217;illustration de l&#8217;article pour résumer la situation des bloggers sur la toile actuellement.</p>
 <p><a href="http://sametmax.com/?flattrss_redirect&amp;id=11078&amp;md5=7b1ca71ff10888295a9f4397874bb1f6" title="Flattr" target="_blank"><img src="media/fc229d00.flattr-badge-large.png" data-original-source="http://sametmax.com/wp-content/plugins/flattr/img/flattr-badge-large.png" alt="flattr this!"/></a></p>


feed_id: http://sametmax.com/?p=10327
title: D’une base à l’autre en Python
uri: D-une-base-à-l-autre-en-Python
url: http://sametmax.com/dune-base-a-lautre-en-python/
content:
<p>Je vous avais parlé <a href="http://sametmax.com/la-fin-du-mystere-du-binaire-nananere/">des bases</a> dans l&#8217;article sur le binaire. En informatique, on utilise essentiellement la base 10, évidement, mais aussi 2, 8, 16 et 64. Des puissances de 2, quoi.</p>
<p>Cependant, il arrive parfois qu&#8217;on ait besoin d&#8217;une base personalisée. En effet, une base n&#8217;étant qu&#8217;une notation, on peut en créer une de toutes pièces. C&#8217;est parfois très utile :</p>
<ul>
<li>Besoin de limiter au maximum le nombre de caractères pour représenter une donnée. Si la base 64 ne suffit pas, on créera des bases 70, voire plus si on a accès à un charset large.</li>
<li>On est limité dans les caractères qu&#8217;on peut utiliser. Typiquement dans certains formulaires de mots de passe, on a le droit qu&#8217;à <code>-_</code>, des lettres et des chiffres.</li>
<li>On souhaite limiter les caractères à utiliser. Ça m&#8217;est arrivé en travaillant sur un système à base de SMS. Les ID devaient éviter de contenir tous les caractères comme <code>1, l, 0, O, S, 5, Z, 2</code> car trop faciles à confondre sur un nokia 3310.</li>
</ul>
<p>Mais la plupart des outils ne permettent pas de choisir la base de représentation des données qu&#8217;ils génèrent. Pour cette raison, il peut être pratique d&#8217;implémenter une petit convertisseur d&#8217;une base à l&#8217;autre.</p>
<p>Voici un objet Python qui permet de convertir une string d&#8217;une base custom en entier en base 10, et inversement.</p>

<div class="wp_syntax"><div class="code"><pre class="python" style="font-family:monospace;"><span style="color: #808080; font-style: italic;"># Il est possible de passer de n'importe quelle base à</span>
<span style="color: #808080; font-style: italic;"># une autre directement, mais utiliser la base 10,</span>
<span style="color: #808080; font-style: italic;"># comme pivot, est ce qu'il y a de plus facile </span>
<span style="color: #808080; font-style: italic;"># à coder</span>
<span style="color: #ff7700;font-weight:bold;">class</span> BaseConverter:
&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>, symboles<span style="color: black;">&#41;</span>:
      <span style="color: #008000;">self</span>.<span style="color: black;">symboles</span> = symboles
      <span style="color: #008000;">self</span>.<span style="color: black;">sym2val</span> = <span style="color: black;">&#123;</span>l: i <span style="color: #ff7700;font-weight:bold;">for</span> i, l <span style="color: #ff7700;font-weight:bold;">in</span> <span style="color: #008000;">enumerate</span><span style="color: black;">&#40;</span>symboles<span style="color: black;">&#41;</span><span style="color: black;">&#125;</span>
      <span style="color: #008000;">self</span>.<span style="color: black;">val2sym</span> = <span style="color: #008000;">dict</span><span style="color: black;">&#40;</span><span style="color: #008000;">enumerate</span><span style="color: black;">&#40;</span>symboles<span style="color: black;">&#41;</span><span style="color: black;">&#41;</span>
&nbsp;
   <span style="color: #ff7700;font-weight:bold;">def</span> to_base_10<span style="color: black;">&#40;</span><span style="color: #008000;">self</span>, <span style="color: #dc143c;">string</span><span style="color: black;">&#41;</span>:
       i = <span style="color: #ff4500;">0</span>
       base = <span style="color: #008000;">len</span><span style="color: black;">&#40;</span><span style="color: #008000;">self</span>.<span style="color: black;">sym2val</span><span style="color: black;">&#41;</span>
       <span style="color: #808080; font-style: italic;"># On part de la gauche vers la droite,</span>
       <span style="color: #808080; font-style: italic;"># donc on commence avec les valeurs les plus</span>
       <span style="color: #808080; font-style: italic;"># grosses.</span>
       <span style="color: #808080; font-style: italic;"># Pour chaque symbole, on ajoute la valeur</span>
       <span style="color: #808080; font-style: italic;"># de celui-ci (donnée par la table) et</span>
       <span style="color: #808080; font-style: italic;"># avec facteur lié à sa position.</span>
       <span style="color: #ff7700;font-weight:bold;">for</span> c <span style="color: #ff7700;font-weight:bold;">in</span> <span style="color: #dc143c;">string</span>:
           i <span style="color: #66cc66;">*</span>= base
           i += <span style="color: #008000;">self</span>.<span style="color: black;">sym2val</span><span style="color: black;">&#91;</span>c<span style="color: black;">&#93;</span>
       <span style="color: #ff7700;font-weight:bold;">return</span> i
&nbsp;
   <span style="color: #ff7700;font-weight:bold;">def</span> from_base_10<span style="color: black;">&#40;</span><span style="color: #008000;">self</span>, number<span style="color: black;">&#41;</span>:
       <span style="color: #483d8b;">&quot;&quot;&quot; Convert from a base 10 to the custom base&quot;&quot;&quot;</span>
       <span style="color: #dc143c;">array</span> = <span style="color: black;">&#91;</span><span style="color: black;">&#93;</span>
       base = <span style="color: #008000;">len</span><span style="color: black;">&#40;</span><span style="color: #008000;">self</span>.<span style="color: black;">val2sym</span><span style="color: black;">&#41;</span>
       <span style="color: #808080; font-style: italic;"># Division euclidienne en boucle jusqu'à ce que le</span>
       <span style="color: #808080; font-style: italic;"># reste soit égal à zero.</span>
       <span style="color: #ff7700;font-weight:bold;">while</span> number:
           number, value = <span style="color: #008000;">divmod</span><span style="color: black;">&#40;</span>number, base<span style="color: black;">&#41;</span>
           <span style="color: #808080; font-style: italic;"># Le résultat est l'index du symbole.</span>
           <span style="color: #808080; font-style: italic;"># On le place le plus à gauche, chaque</span>
           <span style="color: #808080; font-style: italic;"># symbole ayant une valeur plus grande</span>
           <span style="color: #808080; font-style: italic;"># que le précédent.</span>
           <span style="color: #dc143c;">array</span>.<span style="color: black;">insert</span><span style="color: black;">&#40;</span><span style="color: #ff4500;">0</span>, <span style="color: #008000;">self</span>.<span style="color: black;">val2sym</span><span style="color: black;">&#91;</span>value<span style="color: black;">&#93;</span><span style="color: black;">&#41;</span>
&nbsp;
       <span style="color: #808080; font-style: italic;"># Ne pas oublier le zéro</span>
       <span style="color: #ff7700;font-weight:bold;">return</span> <span style="color: #483d8b;">''</span>.<span style="color: black;">join</span><span style="color: black;">&#40;</span><span style="color: #dc143c;">array</span><span style="color: black;">&#41;</span> <span style="color: #ff7700;font-weight:bold;">or</span> <span style="color: #008000;">self</span>.<span style="color: black;">symboles</span><span style="color: black;">&#91;</span><span style="color: #ff4500;">0</span><span style="color: black;">&#93;</span></pre></div></div>

<p>Maintenant imaginez la génération d&#8217;un UUID qui doit seulement contenir certains caractères ou avoir une certaine taille. Par exemple, votre client vous demande des ID en <a href="http://www.crockford.com/wrmg/base32.html">base 32 de Crockford</a>.</p>

<div class="wp_syntax"><div class="code"><pre class="python" style="font-family:monospace;">uuid = uuid.<span style="color: black;">uuid4</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>uid<span style="color: black;">&#41;</span>
<span style="color: #808080; font-style: italic;">## 50ab7fa9-9643-4aad-be84-548405abea08</span>
CROCKFORD_B32 = BaseConverter<span style="color: black;">&#40;</span><span style="color: #483d8b;">&quot;0123456789abcdefghjkmnpqrstvwxyz&quot;</span><span style="color: black;">&#41;</span>
<span style="color: #ff7700;font-weight:bold;">print</span><span style="color: black;">&#40;</span>CROCKFORD_B32.<span style="color: black;">from_base_10</span><span style="color: black;">&#40;</span>uid.<span style="color: #008000;">int</span><span style="color: black;">&#41;</span><span style="color: black;">&#41;</span>
<span style="color: #808080; font-style: italic;">## 2gndztk5j39apvx12mgg2tqtg8</span></pre></div></div>

<p>Bon, évidément, pour toutes les bases ordinaires, <code>int(string, base)</code> fait ça très bien.</p>
<p>On peut aller plus loin et faire une seule partie de l&#8217;ID dans une base, et l&#8217;autre dans une autre base, pour des raisons de lisibilité. Le cas d&#8217;école étant les plaques d&#8217;immatriculation ou les numéros de vol.</p>
<p>Une fois, on m&#8217;a demandé de faire une URL de type :</p>
<p><code>/ressource/id/</code></p>
<p>Mais l&#8217;id devait être facile à recopier à la main. Le client a opté pour une notation de type &#8220;AAA111&#8243;, 3 lettres, puis 3 chiffres.</p>
<p>Vu qu&#8217;il fallait l&#8217;implémenter en Lua embed dans nginx, la solution la plus simple a été adoptée :</p>

<div class="wp_syntax"><div class="code"><pre class="ini" style="font-family:monospace;">location ~ <span style="color: #933;">&quot;^/([0-9A-Fa-f]{3}[0-9]{3})$&quot;</span> <span style="">&#123;</span>
   set $short_id $1<span style="color: #666666; font-style: italic;">;</span>
   content_by_lua '
       local redirect_url <span style="color: #000066; font-weight:bold;">=</span> <span style="color: #933;">&quot;http://site.com/ressource/&quot;</span> .. string.sub<span style="">&#40;</span>ngx.var.short_id, -<span style="">3</span><span style="">&#41;</span> + tonumber<span style="">&#40;</span>string.sub<span style="">&#40;</span>ngx.var.short_id,<span style="">0</span>,<span style="">3</span><span style="">&#41;</span>, <span style="">16</span> <span style="">&#41;</span> * <span style="">1000</span>
       return ngx.redirect<span style="">&#40;</span>redirect_url..<span style="color: #933;">&quot;/&quot;</span><span style="">&#41;</span>
   '<span style="color: #666666; font-style: italic;">;</span>
<span style="">&#125;</span></pre></div></div>

<p>En gros, on prend les 3 premiers caractères, on les traite comme de l&#8217;hexa, puis on multiplie par 999, et on additionne les 3 derniers chiffres. Cela convertit le truc en base 10.</p>
<p>L&#8217;URL est très lisible, et facile à dicter ou recopier à la main :</p>
<p><code>http://site.com/ressource/FAC101</code></p>
<p>Bien entendu, on peut se retrouver avec des chiffres au début puisque c&#8217;est de l&#8217;hexa, et avoir une URL de type <code>345823</code>, mais ça a été accepté.</p>
<p>Au maximum on ne peut gérer que 4 millions d&#8217;ID. Or le site n&#8217;en avait pas accumulé plus de 350 000 en 7 ans, et à moins que le trafic explose soudainement (auquel cas il aura le budget pour me payer pour changer l&#8217;algo:)), ça devrait tenir 80 ans.</p>
<p>Je vais sans doute ajouter ce code à <a href="https://github.com/sametmax/Bat-belt/tree/master/batbelt">Batbelt</a> du coup.</p>
<hr />
<p><a href="https://github.com/sametmax/codes-des-articles/blob/master/2014/juin/base.py">Télécharger le code de l&#8217;article</a></p>
 <p><a href="http://sametmax.com/?flattrss_redirect&amp;id=10327&amp;md5=2085afe76bac505b7451cd7b00784248" title="Flattr" target="_blank"><img src="media/fc229d00.flattr-badge-large.png" data-original-source="http://sametmax.com/wp-content/plugins/flattr/img/flattr-badge-large.png" alt="flattr this!"/></a></p>


feed_id: http://sametmax.com/?p=11146
title: Petite démo pragmatique d’un usage de WAMP en Python
uri: Petite-démo-pragmatique-d-un-usage-de-WAMP-en-Python
url: http://sametmax.com/introduction-a-wamp-en-python/
content:
<p>Vu que dernièrement je vous ai bien gavé avec <a href="http://sametmax.com/crossbar-le-futur-des-applications-web-python/">WAMP</a>, ça mérite un tuto non ?</p>
<p>Il se trouve que l&#8217;équipe derrière WAMP a publié plus tôt que prévu une version de leurs libs contenant l&#8217;API flaskesque sur laquelle on bosse. L&#8217;idée est que même si on n&#8217;a pas encore les tests unitaires, on peut déjà jouer avec.</p>
<p>Maintenant il me fallait un projet sexy, histoire de donner envie. Donc j&#8217;ai fouillé dans ce qui se faisait côté temps réel (essentiellement du NodeJS et du Tornado, mais pas que) pour trouver l&#8217;inspiration.</p>
<p>Et j&#8217;ai trouvé un truc très sympa : <a href="http://news.softpedia.com/news/YouTube-Will-Turn-Your-Phone-into-a-Smart-TV-Remote-318286.shtml">un player vidéo piloté à distance.</a></p>
<p>En effet, n&#8217;est-il pas chiant de regarder une vidéo en ligne sur son ordi posé sur la commode pendant qu&#8217;on est enfoncé dans le canap ? Si on veut faire pause ou changer le son, il faut se lever, arg.</p>
<p>Les problèmes du tiers monde, c&#8217;est du pipi de chat à côté. Ils ont de la chance, eux, ils ne connaissent pas le streaming.</p>
<p><strong>Voici donc le projet : </strong></p>
<p>Une page avec un player HTML 5 et un QR code.</p>
<div id="attachment_11151" class="wp-caption aligncenter" style="width: 335px"><a href="media/c002902c.video.png" data-original-source="http://sametmax.com/wp-content/uploads/2014/06/video.png"><img src="media/c002902c.video.png" data-original-source="http://sametmax.com/wp-content/uploads/2014/06/video.png" alt="Capture d&#039;écran de la démo, côté player" title="Pour simplifier la démo, on peut cliquer sur le QR code est avoir la télécommande dans un autre tab pour ceux qui n&#039;ont pas de smartphone ou d&#039;app de scan de QRCode." width="325" height="186" class="size-full wp-image-11151" /></a><p class="wp-caption-text">Pour simplifier la démo, on peut cliquer sur le QR code et avoir la télécommande dans un autre tab pour ceux qui n&#039;ont pas de smartphone ou d&#039;app de scan de QRCode.</p></div>
<p>Si on scanne le QR code avec son téléphone, il vous envoie sur une page avec une télécommande pour contrôler le player sans bouger votre cul :</p>
<div id="attachment_11152" class="wp-caption aligncenter" style="width: 306px"><a href="media/27ddbc4e.controles.png" data-original-source="http://sametmax.com/wp-content/uploads/2014/06/controles.png"><img src="media/27ddbc4e.controles.png" data-original-source="http://sametmax.com/wp-content/uploads/2014/06/controles.png" alt="Capture d&#039;écrand de la démo, côté contrôles" title="Évidement, c&#039;est basique. Je vais pas m&#039;amuser à faire un produit complet juste pour un truc dont le code source ne sera même pas regardé par la plupart d&#039;entre vous. Je vous connais, bandes de feignasses !" width="296" height="370" class="size-full wp-image-11152" /></a><p class="wp-caption-text">Évidement, c&#039;est basique. Je vais pas m&#039;amuser à faire un produit complet juste pour un truc dont le code source ne sera même pas regardé par la plupart d&#039;enter vous. Je vous connais, bandes de feignasses !</p></div>
<p>Et vous allez voir, c&#8217;est même pas dur à faire. </p>
<p>Mais d&#8217;abord :</p>
<p style="margin:1em; text-align:center">
<a style="font-size:2em;" href="https://demo.crossbar.io/videocontrol/">La démo</a>
  </p>
<p>Bon, y a plus de latence que je le voudrais, mais sur un projet sérieux, il y a moyen de faire ça mieux. Ici on veut faire simple pour expliquer le principe.</p>
<p><a href="https://github.com/sametmax/codes-des-articles/tree/master/2014/juin/video_remote">Et vous pouvez télécharger le code ici</a>.</p>
<p>Pour comprendre ce qui va suivre, il va vous falloir les bases en prog Javascript et Python, ainsi que bien comprendre la notion de <a href="http://sametmax.com/quest-ce-quun-callback/">callback</a>. Être à l&#8217;aise avec <a href="http://sametmax.com/deferred-future-et-promise-le-pourquoi-le-comment-et-quand-est-ce-quon-mange">promises</a> peut aider.</p>
<p>Et pour bien digérer ce paté, rien ne vaut un peu de son :</p>

<!-- iframe plugin v:2.3 - wordpress.org/extend/plugins/iframe/ -->
<iframe width="560" height="315" src="//www.youtube.com/embed/FU4cnelEdi4" frameborder="0" scrolling="no" class="iframe-class"></iframe>
<h2>Le Chteumeuleu</h2>
<p>Il va nous falloir deux pages Web, une pour le player video, et une pour la télécommande.</p>
<p>Le player :</p>

<div class="wp_syntax"><div class="code"><pre class="html" style="font-family:monospace;">&lt;!DOCTYPE html&gt;
&lt;html&gt;
&lt;head&gt;
   &lt;title&gt;Video&lt;/title&gt;
   &lt;meta charset='utf-8'&gt;
   &lt;!-- Chargement des dépendances : autobahn pour WAMP
   et qrcode pour générer le QR code. Bien entendu, je
   vous invite à ne pas les hotlinker dans vos projets,
   mais pour la démo c'est plus simple. --&gt;
  &lt;script src=&quot;https://autobahn.s3.amazonaws.com/autobahnjs/latest/autobahn.min.jgz&quot;
         type=&quot;text/javascript&quot;&gt;&lt;/script&gt;
   &lt;script src=&quot;http://davidshimjs.github.com/qrcodejs/qrcode.min.js&quot;
           type=&quot;text/javascript&quot;&gt;&lt;/script&gt;
&nbsp;
   &lt;style type=&quot;text/css&quot;&gt;
      /* Quelques styles histoire que ça soit pas
         TROP moche. J'inline, hein, on va pas
         se faire chier à faire un fichier CSS
         externe juste pour ça. */
      #vid {
         /* Taille de la video */
         width:427px;
         height:240px;
      }
      /* Centrage avec la méthode Rache */
      #container {
          width:427px;
          margin:auto;
      }
      #ctrllink {
          display:block;
          width:256px;
          margin:auto;
      }
   &lt;/style&gt;
&nbsp;
&lt;/head&gt;
&lt;body&gt;
&lt;div id=&quot;container&quot;&gt;
  &lt;p&gt;
&nbsp;
   &lt;!-- J'utilise le lecteur video HTML5 car c'est le plus facile
        à faire. Je ne vais pas m'attarder sur comment ça
        marche, y a plein de tutos pas trop mauvais
        sur la question --&gt;
    &lt;video id=&quot;vid&quot;
           class=&quot;video-js vjs-default-skin&quot;
           controls preload=&quot;auto&quot;
           poster=&quot;http://media.w3.org/2010/05/sintel/poster.png&quot; &gt;
&nbsp;
      &lt;!-- Encore une fois, je hotlink la video, mais ne faites
       pas ça à la maison les enfants. Surtout que les perfs du
      serveur du W3C sont merdiques et ça bufferise à mort. --&gt;
       &lt;source id='ogv'
         src=&quot;http://media.w3.org/2010/05/sintel/trailer.ogv&quot;
         type='video/ogg'&gt;
       &lt;source id='mp4'
         src=&quot;http://media.w3.org/2010/05/sintel/trailer.mp4&quot;
         type='video/mp4'&gt;
       &lt;source id='webm'
         src=&quot;http://media.w3.org/2010/05/sintel/trailer.webm&quot;
         type='video/webm'&gt;
    &lt;/video&gt;
  &lt;/p&gt;
  &lt;!-- Un élément vide pour le QR code --&gt;
  &lt;p&gt;
    &lt;a id=&quot;ctrllink&quot; href=&quot;#&quot; target=&quot;_blank&quot;&gt;
      &lt;span id=&quot;qrcode&quot;&gt;&lt;/span&gt;
    &lt;/a&gt;
  &lt;/p&gt;
 &lt;/div&gt;
&nbsp;
&lt;/body&gt;
&lt;/html&gt;</pre></div></div>

<p>Et la télécommande :</p>

<div class="wp_syntax"><div class="code"><pre class="html" style="font-family:monospace;">&lt;!DOCTYPE html&gt;
&lt;html&gt;
&lt;head&gt;
  &lt;title&gt;Télécommande&lt;/title&gt;
  &lt;meta charset='utf-8'&gt;
  &lt;script src=&quot;https://autobahn.s3.amazonaws.com/autobahnjs/latest/autobahn.min.jgz&quot;
         type=&quot;text/javascript&quot;&gt;&lt;/script&gt;
  &lt;!-- Zoom du viewport sur mobile pour éviter d'avoir
       à le faire à la main. --&gt;
  &lt;meta name=&quot;viewport&quot; content=&quot;width=device-width, initial-scale=1&quot;&gt;
  &lt;style type=&quot;text/css&quot;&gt;
    #controls {
      width:350px;
      margin:auto;
    }
    #controls button {
      font-size: 1em;
    }
    #controls input {
      vertical-align:middle;
       width: 200px;
       height:20px;
   }
  &lt;/style&gt;
&lt;/head&gt;
&lt;body&gt;
  &lt;p id=&quot;controls&quot;&gt;
    &lt;!-- Marrant de se dire qu'en 2000, le JS inline était
         considéré comme démoniaque, et maintenant avec
         angularjs et cie, c'est exactement ce que tout
         le monde fait...
         Bref, on attache le clic sur nos contrôles à des
         méthodes de notre objet qui va se charger de la
         logique. --&gt;
&nbsp;
    &lt;button id=&quot;play&quot; onclick=&quot;control.togglePlay()&quot;&gt;Play&lt;/button&gt;
    &lt;input id=&quot;volume&quot;
                    onchange=&quot;control.volume(this.value)&quot;
                    type=&quot;range&quot;&gt;
  &lt;/p&gt;
&lt;/body&gt;
&lt;/html&gt;</pre></div></div>

<p>Rien d&#8217;incroyable. C&#8217;est du HTML, un peu de CSS, on charge les dépendances en JS. Classique.</p>
<p>Vu qu&#8217;on utilise des ressources hotlinkées par souci de simplicité, <strong>il vous faudra être connecté à Internet.</strong></p>
<h2>Setup du serveur</h2>
<p>On va travailler avec Python 2.7. Je sais, je sais, c&#8217;est pas idéal, mais pour le moment je n&#8217;ai fait l&#8217;API flaskesque que pour le backend twisted, qui est en 2.7. On ne peut pas tout faire. Est-ce que vous savez le temps que me prend juste la rédaction de ce tuto, sérieux ?</p>
<p>Bref, il nous faut avant tout un serveur HTTP pour servir nos fichiers  HTML. Normalement crossbar peut le faire pour nous en prod, mais en dev, on va pas setuper crossbar, on va faire plus simple.</p>
<p>Donc, à la racine du projet, lancez la commande :</p>

<div class="wp_syntax"><div class="code"><pre class="bash" style="font-family:monospace;">python <span style="color: #660033;">-m</span> SimpleHTTPServer</pre></div></div>

<p>Vos pages Web seront ainsi servies en local sur le port 8000. Par exemple, pour afficher la page de video :</p>
<p><code>http:localhost:8000/index.html</code></p>
<p>Ensuite, faut installer de quoi faire du WAMP en Python avec <a href="http://sametmax.com/votre-python-aime-les-pip/">pip</a> :</p>

<div class="wp_syntax"><div class="code"><pre class="bash" style="font-family:monospace;">pip <span style="color: #c20cb9; font-weight: bold;">install</span> <span style="color: #ff0000;">&quot;autobahn[twisted]&quot;</span></pre></div></div>

<p>Pour simplifier le développement, notre app Python va lancer automatiquement un petit serveur WAMP, du coup pas besoin d&#8217;installer crossbar du tout pour cette démo.</p>
<h2>Création de l&#8217;App WAMP côté serveur</h2>
<p>Pour cette démo, le serveur n&#8217;a pas grand chose à faire. On pourrait en fait la faire sans aucun code serveur, mais ça va nous simplifier la vie.</p>
<p>En effet, on a deux problématiques que le serveur va résoudre facilement pour nous : <strong>créer un ID unique pour le player et récupérer l&#8217;IP sur le réseau local.</strong></p>
<p>L&#8217;ID, c&#8217;est simplement que si plusieurs personnes lancent en même temps un player, on ne veut pas que les télécommandes puissent lancer un ordre à un autre player que le sien. On pourrait utiliser un timestamp, mais ils sont contiguës, n&#8217;importe quel script kiddies pourrait faire un script pour foutre la merde. On va donc créer un ID unique qui ne soit pas facilement prévisible. Javascript n&#8217;a rien pour faire ça en natif, et c&#8217;est un peu con de charger une lib de plus pour ça alors que Python peut le faire pour nous.</p>
<p>L&#8217;IP, c&#8217;est parce qu&#8217;il faut donner l&#8217;adresse de notre machine au téléphone qui va l&#8217;afficher sur la télécommande, puisque notre serveur tourne dessus. Sinon notre démo ne marchera pas. Bien sûr, en prod, on ne fera pas ça, mais en local, il faut faire avec les moyens du bord.</p>
<p>Cela veut dire aussi que le téléphone doit être sur le même réseau local pour que ça fonctionne. <strong>Donc mettez votre téléphone en Wifi, par en 3G.</strong></p>
<p>Voilà ce que donne notre code WAMP côté serveur :</p>

<div class="wp_syntax"><div class="code"><pre class="python" style="font-family:monospace;"><span style="color: #808080; font-style: italic;"># -*- coding: utf-8 -*-</span>
&nbsp;
<span style="color: #ff7700;font-weight:bold;">from</span> autobahn.<span style="color: black;">twisted</span>.<span style="color: black;">wamp</span> <span style="color: #ff7700;font-weight:bold;">import</span> Application
&nbsp;
<span style="color: #ff7700;font-weight:bold;">import</span> <span style="color: #dc143c;">socket</span>
<span style="color: #ff7700;font-weight:bold;">import</span> uuid
&nbsp;
<span style="color: #808080; font-style: italic;"># Comme pour flask, l'objet app</span>
<span style="color: #808080; font-style: italic;"># est ce qui lie tous les éléments</span>
<span style="color: #808080; font-style: italic;"># de notre code ensemble. On lui donne</span>
<span style="color: #808080; font-style: italic;"># un nom, ici &quot;demo&quot;</span>
app = Application<span style="color: black;">&#40;</span><span style="color: #483d8b;">'demo'</span><span style="color: black;">&#41;</span>
<span style="color: #808080; font-style: italic;"># Bien que l'app va démarrer un serveur</span>
<span style="color: #808080; font-style: italic;"># pour nous, l'app est bien un CLIENT</span>
<span style="color: #808080; font-style: italic;"># du serveur WAMP. Le serveur démarré</span>
<span style="color: #808080; font-style: italic;"># automatiquement n'est qu'une facilité</span>
<span style="color: #808080; font-style: italic;"># pour le dev. En prod on utiliserait</span>
<span style="color: #808080; font-style: italic;"># crossbar.</span>
&nbsp;
<span style="color: #808080; font-style: italic;"># Juste un conteneur pour y mettre notre IP</span>
app._data = <span style="color: black;">&#123;</span><span style="color: black;">&#125;</span>
&nbsp;
<span style="color: #808080; font-style: italic;"># On déclare que cette fonction sera appelée</span>
<span style="color: #808080; font-style: italic;"># quand l'app se sera connectée au serveur WAMP.</span>
<span style="color: #808080; font-style: italic;"># Ceci permet de lancer du code juste après</span>
<span style="color: #808080; font-style: italic;"># le app.run() que l'on voit en bas du fichier.</span>
<span style="color: #808080; font-style: italic;"># '_' est une convention en Python pour dire</span>
<span style="color: #808080; font-style: italic;"># &quot;ce nom n'a aucune importance, c'est du code</span>
<span style="color: #808080; font-style: italic;"># jetable qu'on utilisera une seule fois&quot;.</span>
@app.<span style="color: #dc143c;">signal</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'onjoined'</span><span style="color: black;">&#41;</span>
<span style="color: #ff7700;font-weight:bold;">def</span> _<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>:
   <span style="color: #808080; font-style: italic;"># On récupère notre adresse IP sur le réseau local</span>
   <span style="color: #808080; font-style: italic;"># C'est une astuce qui demande de se connecter et donc</span>
   <span style="color: #808080; font-style: italic;">#  à une IP externe, on a besoin d'une connexion internet.</span>
   s = <span style="color: #dc143c;">socket</span>.<span style="color: #dc143c;">socket</span><span style="color: black;">&#40;</span><span style="color: #dc143c;">socket</span>.<span style="color: black;">AF_INET</span>, <span style="color: #dc143c;">socket</span>.<span style="color: black;">SOCK_DGRAM</span><span style="color: black;">&#41;</span>
   s.<span style="color: black;">connect</span><span style="color: black;">&#40;</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">&quot;8.8.8.8&quot;</span>, <span style="color: #ff4500;">80</span><span style="color: black;">&#41;</span><span style="color: black;">&#41;</span>
   <span style="color: #808080; font-style: italic;"># On stocke l'adresse IP locale dans un conteneur</span>
   <span style="color: #808080; font-style: italic;"># qui sera accessible partout ailleur.</span>
   app._data<span style="color: black;">&#91;</span><span style="color: #483d8b;">'LOCAL_IP'</span><span style="color: black;">&#93;</span> = s.<span style="color: black;">getsockname</span><span style="color: black;">&#40;</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>
   s.<span style="color: black;">close</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
&nbsp;
<span style="color: #808080; font-style: italic;"># On déclare que la fonction &quot;ip()&quot; est appelable</span>
<span style="color: #808080; font-style: italic;"># via RCP. Ce qui veut dire que tout autre client</span>
<span style="color: #808080; font-style: italic;"># WAMP peut obtenir le résultat de cette fonction.</span>
<span style="color: #808080; font-style: italic;"># Donc on va pouvoir l'appeler depuis notre navigateur.</span>
<span style="color: #808080; font-style: italic;"># Comme notre app s'appelle &quot;demo&quot; et notre fonction</span>
<span style="color: #808080; font-style: italic;"># s'appelle &quot;ip&quot;, un client pourra l'appeler en faisant</span>
<span style="color: #808080; font-style: italic;"># &quot;demo.ip&quot;.</span>
@app.<span style="color: black;">register</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
<span style="color: #ff7700;font-weight:bold;">def</span> ip<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>:
   <span style="color: #808080; font-style: italic;"># On ne fait que retourner l'IP locale. Rien de fou.</span>
   <span style="color: #ff7700;font-weight:bold;">return</span> app._data<span style="color: black;">&#91;</span><span style="color: #483d8b;">'LOCAL_IP'</span><span style="color: black;">&#93;</span>
&nbsp;
<span style="color: #808080; font-style: italic;"># Je voulais appeler cette fonction distante &quot;uuid&quot;, mais ça</span>
<span style="color: #808080; font-style: italic;"># override le module Python uuid. Ce n'est pas une bonne</span>
<span style="color: #808080; font-style: italic;"># idée. Je l'appelle donc 'get_uuid' mais je déclare le</span>
<span style="color: #808080; font-style: italic;"># namespace complet dans register(). Un client WAMP pourra donc</span>
<span style="color: #808080; font-style: italic;"># bien l'appeler via &quot;demo.uuid&quot;.</span>
<span style="color: #808080; font-style: italic;"># Notez que ce namespace doit toujours s'écrire</span>
<span style="color: #808080; font-style: italic;"># truc.machine.bidule. Pas truc/machin ou truc:machin.</span>
<span style="color: #808080; font-style: italic;"># ou truc et bidule.MACHIN.</span>
@app.<span style="color: black;">register</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'demo.uuid'</span><span style="color: black;">&#41;</span>
<span style="color: #ff7700;font-weight:bold;">def</span> get_uuid<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>:
   <span style="color: #808080; font-style: italic;"># Retourne un UUID, sans les tirets.</span>
   <span style="color: #808080; font-style: italic;"># ex: b27f7e9360c04efabfae5ac21a8f4e3c</span>
   <span style="color: #ff7700;font-weight:bold;">return</span> <span style="color: #008000;">str</span><span style="color: black;">&#40;</span>uuid.<span style="color: black;">uuid4</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span><span style="color: black;">&#41;</span>.<span style="color: black;">replace</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'-'</span>, <span style="color: #483d8b;">''</span><span style="color: black;">&#41;</span>
&nbsp;
<span style="color: #808080; font-style: italic;"># On lance l'application. Ceci va lancer le serveur</span>
<span style="color: #808080; font-style: italic;"># puis le client. On peut désactiver le lancement du</span>
<span style="color: #808080; font-style: italic;"># serveur une fois qu'on met tout ça en prod.</span>
<span style="color: #ff7700;font-weight:bold;">if</span> __name__ == <span style="color: #483d8b;">'__main__'</span>:
    app.<span style="color: black;">run</span><span style="color: black;">&#40;</span>url=<span style="color: #483d8b;">&quot;ws://0.0.0.0:8080/&quot;</span><span style="color: black;">&#41;</span>
<span style="color: #808080; font-style: italic;"># On ne peut rien mettre comme code ici, il faut le</span>
<span style="color: #808080; font-style: italic;"># mettre dans @app.signal('onjoined') si on veut</span>
<span style="color: #808080; font-style: italic;"># entrer du code après que l'app soit lancée.</span></pre></div></div>

<p>Et on lance notre app :</p>

<div class="wp_syntax"><div class="code"><pre class="bash" style="font-family:monospace;">python app.py</pre></div></div>

<p>Nous avons maintenant deux serveurs qui tournent : un serveur HTTP qui écoute sur le port 8000, et un serveur WAMP qui écoute sur le port 8080. En prod, crossbar peut servir à la fois HTTP et WAMP, donc pas besoin de lancer deux outils.</p>
<h2>Le lecteur video</h2>
<p>Il nous faut maintenant définir le comportement de notre lecteur video via Javascript. Il s&#8217;agit essentiellement de se connecter au serveur WAMP, et d&#8217;échanger des messages via RPC ou PUB/SUB :</p>

<div class="wp_syntax"><div class="code"><pre class="javascript" style="font-family:monospace;">  <span style="color: #003366; font-weight: bold;">var</span> player <span style="color: #339933;">=</span> <span style="color: #009900;">&#123;</span><span style="color: #009900;">&#125;</span><span style="color: #339933;">;</span>
  <span style="color: #003366; font-weight: bold;">var</span> url<span style="color: #339933;">;</span>
  <span style="color: #006600; font-style: italic;">/* On va utiliser du pur JS histoire de pas mélanger
    des notions de jQuery dans le tas. Je ne vais
    PAS utiliser les best practices sinon vous allez
    être noyés dans des détails */</span>
&nbsp;
  <span style="color: #009966; font-style: italic;">/* Lancer le code une fois que la page est chargée */</span>
  window.<span style="color: #660066;">addEventListener</span><span style="color: #009900;">&#40;</span><span style="color: #3366CC;">&quot;load&quot;</span><span style="color: #339933;">,</span> <span style="color: #003366; font-weight: bold;">function</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#123;</span>
&nbsp;
    <span style="color: #006600; font-style: italic;">/* Connexion au serveur WAMP. J'utilise
       les valeurs par défaut du serveur de
       dev. On ouvre explicitement la connection
       à la fin du script. */</span>
    <span style="color: #003366; font-weight: bold;">var</span> connection <span style="color: #339933;">=</span> <span style="color: #003366; font-weight: bold;">new</span> autobahn.<span style="color: #660066;">Connection</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#123;</span>
       url<span style="color: #339933;">:</span> <span style="color: #3366CC;">'ws://'</span> <span style="color: #339933;">+</span> window.<span style="color: #660066;">location</span>.<span style="color: #660066;">hostname</span> <span style="color: #339933;">+</span> <span style="color: #3366CC;">':8080/'</span><span style="color: #339933;">,</span>
       realm<span style="color: #339933;">:</span> <span style="color: #3366CC;">'realm1'</span>
    <span style="color: #009900;">&#125;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
&nbsp;
    <span style="color: #006600; font-style: italic;">/* Lancer ce code une fois que la connexion
       est réussie. Notez que je ne gère pas
       les erreurs dans dans une APP JS, c'est
       un puits sans fond. */</span>
    connection.<span style="color: #660066;">onopen</span> <span style="color: #339933;">=</span> <span style="color: #003366; font-weight: bold;">function</span> <span style="color: #009900;">&#40;</span>session<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
&nbsp;
      <span style="color: #009966; font-style: italic;">/* Appel de la fonction ip() sur le serveur */</span>
      session.<span style="color: #660066;">call</span><span style="color: #009900;">&#40;</span><span style="color: #3366CC;">'demo.ip'</span><span style="color: #009900;">&#41;</span>
&nbsp;
      <span style="color: #006600; font-style: italic;">/* Une fois qu'on a récupéré l'IP,
         on peut fabriquer l'URL de notre
         projet et on appelle la fonction
         get_uuid() du serveur */</span>
      .<span style="color: #660066;">then</span><span style="color: #009900;">&#40;</span><span style="color: #003366; font-weight: bold;">function</span><span style="color: #009900;">&#40;</span>ip<span style="color: #009900;">&#41;</span><span style="color: #009900;">&#123;</span>
        url <span style="color: #339933;">=</span> <span style="color: #3366CC;">'http://'</span> <span style="color: #339933;">+</span> ip <span style="color: #339933;">+</span> <span style="color: #3366CC;">':8000'</span><span style="color: #339933;">;</span>
        <span style="color: #000066; font-weight: bold;">return</span> session.<span style="color: #660066;">call</span><span style="color: #009900;">&#40;</span><span style="color: #3366CC;">'demo.uuid'</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
      <span style="color: #009900;">&#125;</span><span style="color: #009900;">&#41;</span>
&nbsp;
      <span style="color: #006600; font-style: italic;">/* Une fois qu'on a l'UUID, on peut commencer
         à gérer la partie télécommande */</span>
      .<span style="color: #660066;">then</span><span style="color: #009900;">&#40;</span><span style="color: #003366; font-weight: bold;">function</span><span style="color: #009900;">&#40;</span>uuid<span style="color: #009900;">&#41;</span><span style="color: #009900;">&#123;</span>
&nbsp;
        <span style="color: #006600; font-style: italic;">/* Création du QR code avec le lien pointant
           sur la bonne URL. On met l'ID dans le hash. */</span>
        <span style="color: #003366; font-weight: bold;">var</span> controlUrl <span style="color: #339933;">=</span> url <span style="color: #339933;">+</span> <span style="color: #3366CC;">'/control.html#'</span> <span style="color: #339933;">+</span> uuid<span style="color: #339933;">;</span>
        <span style="color: #003366; font-weight: bold;">var</span> codeDiv <span style="color: #339933;">=</span> document.<span style="color: #660066;">getElementById</span><span style="color: #009900;">&#40;</span><span style="color: #3366CC;">&quot;qrcode&quot;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
        <span style="color: #003366; font-weight: bold;">new</span> QRCode<span style="color: #009900;">&#40;</span>codeDiv<span style="color: #339933;">,</span> controlUrl<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
        <span style="color: #003366; font-weight: bold;">var</span> ctrllink <span style="color: #339933;">=</span> document.<span style="color: #660066;">getElementById</span><span style="color: #009900;">&#40;</span><span style="color: #3366CC;">&quot;ctrllink&quot;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
        ctrllink.<span style="color: #660066;">href</span> <span style="color: #339933;">=</span> controlUrl<span style="color: #339933;">;</span>
&nbsp;
        <span style="color: #006600; font-style: italic;">/* Notre travail consiste essentiellement à
           manipuler cet élément */</span>
        <span style="color: #003366; font-weight: bold;">var</span> video <span style="color: #339933;">=</span> document.<span style="color: #660066;">getElementById</span><span style="color: #009900;">&#40;</span><span style="color: #3366CC;">&quot;vid&quot;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
&nbsp;
        <span style="color: #006600; font-style: italic;">/* On attache déclare 4 fonctions comme étant
           appelable à distance. Ces fonctions sont
           appelables en utilisant le nom composé
           de notre ID et de l'action qu'on souhaite
           faire. Ex:
           'b27f7e9360c04efabfae5ac21a8f4e3c.play'
           pour appeler &quot;play&quot; sur notre session. */</span>
        session.<span style="color: #660066;">register</span><span style="color: #009900;">&#40;</span>uuid <span style="color: #339933;">+</span> <span style="color: #3366CC;">'.play'</span><span style="color: #339933;">,</span> <span style="color: #003366; font-weight: bold;">function</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#123;</span>
           video.<span style="color: #660066;">play</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
        <span style="color: #009900;">&#125;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
&nbsp;
        session.<span style="color: #660066;">register</span><span style="color: #009900;">&#40;</span>uuid <span style="color: #339933;">+</span> <span style="color: #3366CC;">'.pause'</span><span style="color: #339933;">,</span> <span style="color: #003366; font-weight: bold;">function</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#123;</span>
           video.<span style="color: #660066;">pause</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
        <span style="color: #009900;">&#125;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
&nbsp;
        session.<span style="color: #660066;">register</span><span style="color: #009900;">&#40;</span>uuid <span style="color: #339933;">+</span> <span style="color: #3366CC;">'.volume'</span><span style="color: #339933;">,</span> <span style="color: #003366; font-weight: bold;">function</span><span style="color: #009900;">&#40;</span>val<span style="color: #009900;">&#41;</span><span style="color: #009900;">&#123;</span>
           video.<span style="color: #660066;">volume</span> <span style="color: #339933;">=</span> val<span style="color: #009900;">&#91;</span><span style="color: #CC0000;">0</span><span style="color: #009900;">&#93;</span><span style="color: #339933;">;</span>
        <span style="color: #009900;">&#125;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
&nbsp;
        session.<span style="color: #660066;">register</span><span style="color: #009900;">&#40;</span>uuid <span style="color: #339933;">+</span> <span style="color: #3366CC;">'.status'</span><span style="color: #339933;">,</span> <span style="color: #003366; font-weight: bold;">function</span><span style="color: #009900;">&#40;</span>val<span style="color: #009900;">&#41;</span><span style="color: #009900;">&#123;</span>
          <span style="color: #000066; font-weight: bold;">return</span> <span style="color: #009900;">&#123;</span>
            <span style="color: #3366CC;">'playing'</span><span style="color: #339933;">:</span> <span style="color: #339933;">!</span>video.<span style="color: #660066;">paused</span><span style="color: #339933;">,</span>
            <span style="color: #3366CC;">'volume'</span><span style="color: #339933;">:</span> video.<span style="color: #660066;">volume</span>
          <span style="color: #009900;">&#125;</span><span style="color: #339933;">;</span>
        <span style="color: #009900;">&#125;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
&nbsp;
&nbsp;
&nbsp;
       <span style="color: #006600; font-style: italic;">/* Quelqu'un peut très bien
           appuyer sur play directement sur cette page.
&nbsp;
          Il faut donc réagir si l'utilisateur le fait,
          publier un événement via WAMP pour permettre
          à notre télécommande de se mettre à jour
          */</span>
       video.<span style="color: #660066;">addEventListener</span><span style="color: #009900;">&#40;</span><span style="color: #3366CC;">'play'</span><span style="color: #339933;">,</span> <span style="color: #003366; font-weight: bold;">function</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#123;</span>
         <span style="color: #006600; font-style: italic;">/* On publie un message indiquant que
            le player a recommencé à lire la vidéo.
            */</span>
         session.<span style="color: #660066;">publish</span><span style="color: #009900;">&#40;</span>uuid <span style="color: #339933;">+</span> <span style="color: #3366CC;">'.play'</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
       <span style="color: #009900;">&#125;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
&nbsp;
        video.<span style="color: #660066;">addEventListener</span><span style="color: #009900;">&#40;</span><span style="color: #3366CC;">'pause'</span><span style="color: #339933;">,</span> <span style="color: #003366; font-weight: bold;">function</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#123;</span>
          session.<span style="color: #660066;">publish</span><span style="color: #009900;">&#40;</span>uuid <span style="color: #339933;">+</span> <span style="color: #3366CC;">'.pause'</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
        <span style="color: #009900;">&#125;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
&nbsp;
        video.<span style="color: #660066;">addEventListener</span><span style="color: #009900;">&#40;</span><span style="color: #3366CC;">'volumechange'</span><span style="color: #339933;">,</span> <span style="color: #003366; font-weight: bold;">function</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#123;</span>
          session.<span style="color: #660066;">publish</span><span style="color: #009900;">&#40;</span>uuid <span style="color: #339933;">+</span> <span style="color: #3366CC;">'.volume'</span><span style="color: #339933;">,</span> <span style="color: #009900;">&#91;</span>video.<span style="color: #660066;">volume</span><span style="color: #009900;">&#93;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
        <span style="color: #009900;">&#125;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
&nbsp;
     <span style="color: #009900;">&#125;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
    <span style="color: #009900;">&#125;</span><span style="color: #339933;">;</span>
&nbsp;
    <span style="color: #006600; font-style: italic;">/* Ouverture de la connection une fois que tous les
       callbacks sont bien en place.*/</span>
    connection.<span style="color: #000066;">open</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
  <span style="color: #009900;">&#125;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span></pre></div></div>

<h2>Code de la télécommande</h2>
<p>La télécommande est notre 3eme client WAMP (on peut avoir des centaines de clients WAMP). Notre 1er est l&#8217;App Python, notre second est le player.</p>
<p>Son code a pour but d&#8217;envoyer des ordres au player HTML5, mais aussi de mettre à jour son UI si le player change d&#8217;état.</p>

<div class="wp_syntax"><div class="code"><pre class="javascript" style="font-family:monospace;"><span style="color: #006600; font-style: italic;">/* L'objet qui se charge de la logique de nos
   controles play/pause et changement de
   volume.
   Rien de fou, il change l'affichage
   du bouton et du slider selon qu'on
   est en pause/play et la valeur du
   volume.
   */</span>
<span style="color: #003366; font-weight: bold;">var</span> control <span style="color: #339933;">=</span> <span style="color: #009900;">&#123;</span>
   playing<span style="color: #339933;">:</span> <span style="color: #003366; font-weight: bold;">false</span><span style="color: #339933;">,</span>
   setPlaying<span style="color: #339933;">:</span> <span style="color: #003366; font-weight: bold;">function</span><span style="color: #009900;">&#40;</span>val<span style="color: #009900;">&#41;</span><span style="color: #009900;">&#123;</span>
      control.<span style="color: #660066;">playing</span> <span style="color: #339933;">=</span> val<span style="color: #339933;">;</span>
      <span style="color: #003366; font-weight: bold;">var</span> button <span style="color: #339933;">=</span> window.<span style="color: #660066;">document</span>.<span style="color: #660066;">getElementById</span><span style="color: #009900;">&#40;</span><span style="color: #3366CC;">'play'</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
      <span style="color: #000066; font-weight: bold;">if</span> <span style="color: #009900;">&#40;</span><span style="color: #339933;">!</span>val<span style="color: #009900;">&#41;</span><span style="color: #009900;">&#123;</span>
         button.<span style="color: #660066;">innerHTML</span> <span style="color: #339933;">=</span> <span style="color: #3366CC;">'Play'</span>
      <span style="color: #009900;">&#125;</span> <span style="color: #000066; font-weight: bold;">else</span> <span style="color: #009900;">&#123;</span>
         button.<span style="color: #660066;">innerHTML</span> <span style="color: #339933;">=</span> <span style="color: #3366CC;">'Pause'</span><span style="color: #339933;">;</span>
      <span style="color: #009900;">&#125;</span>
   <span style="color: #009900;">&#125;</span><span style="color: #339933;">,</span>
   setVolume<span style="color: #339933;">:</span> <span style="color: #003366; font-weight: bold;">function</span><span style="color: #009900;">&#40;</span>val<span style="color: #009900;">&#41;</span><span style="color: #009900;">&#123;</span>
      <span style="color: #003366; font-weight: bold;">var</span> slider <span style="color: #339933;">=</span> window.<span style="color: #660066;">document</span>.<span style="color: #660066;">getElementById</span><span style="color: #009900;">&#40;</span><span style="color: #3366CC;">'volume'</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
      slider.<span style="color: #660066;">value</span> <span style="color: #339933;">=</span> val<span style="color: #339933;">;</span>
   <span style="color: #009900;">&#125;</span>
<span style="color: #009900;">&#125;</span><span style="color: #339933;">;</span>
window.<span style="color: #000066;">onload</span> <span style="color: #339933;">=</span> <span style="color: #003366; font-weight: bold;">function</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#123;</span>
  <span style="color: #003366; font-weight: bold;">var</span> connection <span style="color: #339933;">=</span> <span style="color: #003366; font-weight: bold;">new</span> autobahn.<span style="color: #660066;">Connection</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#123;</span>
    url<span style="color: #339933;">:</span> <span style="color: #3366CC;">'ws://'</span> <span style="color: #339933;">+</span> window.<span style="color: #660066;">location</span>.<span style="color: #660066;">hostname</span> <span style="color: #339933;">+</span> <span style="color: #3366CC;">':8080/'</span><span style="color: #339933;">,</span>
    realm<span style="color: #339933;">:</span> <span style="color: #3366CC;">'realm1'</span>
  <span style="color: #009900;">&#125;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
&nbsp;
  connection.<span style="color: #660066;">onopen</span> <span style="color: #339933;">=</span> <span style="color: #003366; font-weight: bold;">function</span> <span style="color: #009900;">&#40;</span>session<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
&nbsp;
    <span style="color: #009966; font-style: italic;">/* Récupération de l'ID dans le hash de l'URL */</span>
    <span style="color: #003366; font-weight: bold;">var</span> uuid <span style="color: #339933;">=</span> window.<span style="color: #660066;">location</span>.<span style="color: #660066;">hash</span>.<span style="color: #660066;">replace</span><span style="color: #009900;">&#40;</span><span style="color: #3366CC;">'#'</span><span style="color: #339933;">,</span> <span style="color: #3366CC;">''</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
&nbsp;
    <span style="color: #006600; font-style: italic;">/* Mise à jour des controles selon le status actuel
       du player grace à un appel RPC vers notre autre
       page. */</span>
    session.<span style="color: #660066;">call</span><span style="color: #009900;">&#40;</span>uuid <span style="color: #339933;">+</span> <span style="color: #3366CC;">'.status'</span><span style="color: #009900;">&#41;</span>.<span style="color: #660066;">then</span><span style="color: #009900;">&#40;</span><span style="color: #003366; font-weight: bold;">function</span><span style="color: #009900;">&#40;</span><span style="color: #000066;">status</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#123;</span>
&nbsp;
      control.<span style="color: #660066;">setPlaying</span><span style="color: #009900;">&#40;</span><span style="color: #000066;">status</span><span style="color: #009900;">&#91;</span><span style="color: #3366CC;">'playing'</span><span style="color: #009900;">&#93;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
      control.<span style="color: #660066;">setVolume</span><span style="color: #009900;">&#40;</span><span style="color: #000066;">status</span><span style="color: #009900;">&#91;</span><span style="color: #3366CC;">'volume'</span><span style="color: #009900;">&#93;</span><span style="color: #009900;">&#41;</span>
&nbsp;
      <span style="color: #006600; font-style: italic;">/* On attache l'appui sur les contrôles à
         un appel de la fonction play() sur le
         player distant. L'uuid nous permet
         de n'envoyer l'événement que sur le
         bon player. */</span>
      control.<span style="color: #660066;">togglePlay</span> <span style="color: #339933;">=</span> <span style="color: #003366; font-weight: bold;">function</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
        <span style="color: #000066; font-weight: bold;">if</span> <span style="color: #009900;">&#40;</span>control.<span style="color: #660066;">playing</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#123;</span>
          session.<span style="color: #660066;">call</span><span style="color: #009900;">&#40;</span>uuid <span style="color: #339933;">+</span> <span style="color: #3366CC;">'.pause'</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
          control.<span style="color: #660066;">setPlaying</span><span style="color: #009900;">&#40;</span><span style="color: #003366; font-weight: bold;">false</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
        <span style="color: #009900;">&#125;</span> <span style="color: #000066; font-weight: bold;">else</span> <span style="color: #009900;">&#123;</span>
          session.<span style="color: #660066;">call</span><span style="color: #009900;">&#40;</span>uuid <span style="color: #339933;">+</span> <span style="color: #3366CC;">'.play'</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
          control.<span style="color: #660066;">setPlaying</span><span style="color: #009900;">&#40;</span><span style="color: #003366; font-weight: bold;">true</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
        <span style="color: #009900;">&#125;</span>
      <span style="color: #009900;">&#125;</span><span style="color: #339933;">;</span>
&nbsp;
      control.<span style="color: #660066;">volume</span> <span style="color: #339933;">=</span> <span style="color: #003366; font-weight: bold;">function</span><span style="color: #009900;">&#40;</span>val<span style="color: #009900;">&#41;</span><span style="color: #009900;">&#123;</span>
        session.<span style="color: #660066;">call</span><span style="color: #009900;">&#40;</span>uuid <span style="color: #339933;">+</span> <span style="color: #3366CC;">'.volume'</span><span style="color: #339933;">,</span> <span style="color: #009900;">&#91;</span>val <span style="color: #339933;">/</span> <span style="color: #CC0000;">100</span><span style="color: #009900;">&#93;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
      <span style="color: #009900;">&#125;</span><span style="color: #339933;">;</span>
&nbsp;
      <span style="color: #006600; font-style: italic;">/* On ajoute un callback sur les événements
         de changement de status du player. Si
         quelqu'un fait play/pause ou change le
         volume, on veut mettre à jour la page. */</span>
      session.<span style="color: #660066;">subscribe</span><span style="color: #009900;">&#40;</span>uuid <span style="color: #339933;">+</span> <span style="color: #3366CC;">'.play'</span><span style="color: #339933;">,</span> <span style="color: #003366; font-weight: bold;">function</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#123;</span>
        control.<span style="color: #660066;">setPlaying</span><span style="color: #009900;">&#40;</span><span style="color: #003366; font-weight: bold;">true</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
      <span style="color: #009900;">&#125;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
&nbsp;
      session.<span style="color: #660066;">subscribe</span><span style="color: #009900;">&#40;</span>uuid <span style="color: #339933;">+</span> <span style="color: #3366CC;">'.pause'</span><span style="color: #339933;">,</span> <span style="color: #003366; font-weight: bold;">function</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#123;</span>
        control.<span style="color: #660066;">setPlaying</span><span style="color: #009900;">&#40;</span><span style="color: #003366; font-weight: bold;">false</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
      <span style="color: #009900;">&#125;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
&nbsp;
      session.<span style="color: #660066;">subscribe</span><span style="color: #009900;">&#40;</span>uuid <span style="color: #339933;">+</span> <span style="color: #3366CC;">'.volume'</span><span style="color: #339933;">,</span> <span style="color: #003366; font-weight: bold;">function</span><span style="color: #009900;">&#40;</span>val<span style="color: #009900;">&#41;</span><span style="color: #009900;">&#123;</span>
        control.<span style="color: #660066;">setVolume</span><span style="color: #009900;">&#40;</span>val<span style="color: #009900;">&#91;</span><span style="color: #CC0000;">0</span><span style="color: #009900;">&#93;</span> <span style="color: #339933;">*</span> <span style="color: #CC0000;">100</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
      <span style="color: #009900;">&#125;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
    <span style="color: #009900;">&#125;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
  <span style="color: #009900;">&#125;</span><span style="color: #339933;">;</span>
&nbsp;
  connection.<span style="color: #000066;">open</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
<span style="color: #009900;">&#125;</span><span style="color: #339933;">;</span></pre></div></div>

<h2>En résumé</h2>
<p>Voici à quoi ressemble le projet final :</p>
<pre>.
├── app.py (client python))
├── control.html (télécommande)
├── index.html (player video)
</pre>
<div id="attachment_11173" class="wp-caption aligncenter" style="width: 499px"><a href="media/e8d6da18.demo_video_wamp.png" data-original-source="http://sametmax.com/wp-content/uploads/2014/06/demo_video_wamp.png"><img src="media/e8d6da18.demo_video_wamp.png" data-original-source="http://sametmax.com/wp-content/uploads/2014/06/demo_video_wamp.png" alt="Schéma de fonctionnement de la démo" title="Bien que l&#039;app Python lance le serveur automatiquement et de manière invisible, c&#039;est bien un composant à part." width="489" height="346" class="size-full wp-image-11173" /></a><p class="wp-caption-text">Bien que l&#039;app Python lance le serveur automatiquement et de manière invisible, c&#039;est bien un composant à part.</p></div>
<p>Pour ce projet, on aura utilisé :</p>
<ul>
<li><a href="http://wamp.ws/">WAMP</a>: le protocole qui permet de faire communiquer en temps réel des parties d&#8217;application via RPC et PUB/SUB.</li>
<li><a href="http://autobahn.ws/js/">Autobahn.js</a>: une lib pour créer des clients WAMP en javascript.</li>
<li><a href="http://autobahn.ws/python/">Autobahn.py</a>: une lib pour créer des clients WAMP en Python.</li>
</ul>
<p>On n&#8217;aura pas utilisé crossbar, le serveur WAMP Python. On a utilisé un petit serveur de dev inclus dans autobahn. En prod, on utiliserait crossbar comme serveur WAMP.</p>
<p>Il y a pas mal de notions à prendre en compte.</p>
<p>D&#8217;abord, le <strong>RPC</strong>.</p>
<p>Cela permet à un client de dire &#8220;les autres clients peuvent appeler cette fonction à distance&#8221;. On l&#8217;utilise pour exposer <code>ip()</code> et <code>get_uuid()</code> sur notre serveur et notre javascript peut donc les appeler. Mais on l&#8217;utilise AUSSI pour qu&#8217;une des pages (le player) expose <code>play()</code>, <code>pause()</code> et <code>volume()</code> et que l&#8217;autre page (notre télécommande) puisse les utiliser.</p>
<p>La grosse différence, c&#8217;est que <code>ip()</code> peut être appelé par tous les clients en utilisant &#8220;demo.ip&#8221; alors que <code>play()</code> ne peut être appelé que par les clients qui connaissent l&#8217;ID du player, puisqu&#8217;il faut utiliser &#8220;&lt;id&gt;.play&#8221;.</p>
<p>Ensuite, il y a le <strong>PUB/SUB</strong>.</p>
<p>Cela permet à un client de dire &#8220;j&#8217;écoute tous les messages adressés à ce nom&#8221;. Et un autre client peut envoyer un message (on appelle ça aussi un événement, c&#8217;est pareil) sur ce nom, de telle sorte que tous les clients abonnés le reçoivent.</p>
<p>On l&#8217;utilise pour que notre télécommande dise &#8220;j&#8217;écoute tous les messages qui concernent les changements de status du player.&#8221; De l&#8217;autre côté, quand on clique sur un contrôle du player, on envoie un message précisant si le volume a changé, ou si on a appuyé sur play/pause. La télécommande peut ainsi mettre son UI à jour et refléter par exemple, la nouvelle valeur du volume.</p>
<p>Cela résume bien les usages principaux de ces deux outils :</p>
<ul>
<li>RPC permet de donner un ordre ou récupérer une information.</li>
<li>PUB/SUB permet de (se) tenir au courant d&#8217;un événement.</li>
</ul>
<p>Voici le workflow de notre projet :</p>
<ul>
<li>On lance un serveur WAMP.</li>
<li>On connecte des clients dessus (du code Python ou Js dans notre exemple).</li>
<li>Les clients déclarent les fonctions qu&#8217;ils exposent en RPC et les événements qu&#8217;ils écoutent en PUB/SUB.</li>
<li>Ensuite on réagit aux actions utilisateurs et on fait les appels RPC et les publications PUB/SUB en conséquence.</li>
</ul>
<p><strong>Si vous virez tous les commentaires, vous verrez que le code est en fait vraiment court pour une application aussi complexe.</strong></p>
<p>Encore une fois, il est possible de le faire sans WAMP, ce sera juste plus compliqué. Je vous invite à essayer de le faire pour vour rendre compte. Avec PHP, Ruby ou une app WSGI, c&#8217;est pas marrant du tout. Avec NodeJs, c&#8217;est plus simple, mais il faut quand même se taper la logique de gestion RPC et PUB/SUB à la main ou installer pas mal de libs en plus.</p>
<p>WAMP rend ce genre d&#8217;app triviale à écrire. Enfin triviale parce que là j&#8217;ignore tous les edge cases, évidemment. Pour un produit solide, il faut toujours suer un peu.</p>
<h2>Les limites du truc</h2>
<p>C&#8217;est du Python 2.7. Bientôt on pourra le faire avec asyncio et donc Python 3.4, mais malheureusement sans le serveur de dev.</p>
<p>Heureusement, Twisted est en cours de portage vers Python 3, et donc tout finira par marcher en 3.2+.</p>
<p>C&#8217;est du HTML5, mais bien entendu, rien ne vous empêche de faire ça avec du flash si ça vous amuse.</p>
<p>C&#8217;est du websocket, mais on peut utiliser un peu de flash pour <a href="https://github.com/gimite/web-socket-js">simuler websocket</a> pour les vieux navigateurs qui ne le supportent pas.</p>
<p>Non, la vraie limite c&#8217;est encore la jeunesse du projet : pas d&#8217;autoreload pour le serveur (super chiant de devoir le faire à la main à chaque fois qu&#8217;on modifie le code) et les erreurs côté serveur se lisent dans la console JS, et pas dans le terminal depuis lequel on a lancé le serveur. Plein de petits détails comme ça.</p>
<h2>EDIT: Mise en prod</h2>
<p>On m&#8217;a demandé à quoi ça ressemblerait si on mettais le truc en prod, alors je vais donner un exemple.</p>
<p>D&#8217;abord, on installe crossbar :</p>

<div class="wp_syntax"><div class="code"><pre class="bash" style="font-family:monospace;">pip <span style="color: #c20cb9; font-weight: bold;">install</span> crossbar</pre></div></div>

<p>Ensuite, on le fait créer un dossier de configuration :</p>
<p><code>crossbar init</code></p>
<p>Ça va créer un dossier <code>.crossbar</code> avec un fichier <code>config.json</code> dedans.</p>
<p>On édite le fichier, de telle sorte qu&#8217;il ressemble à ça :</p>

<div class="wp_syntax"><div class="code"><pre class="javascript" style="font-family:monospace;"><span style="color: #009900;">&#123;</span>
   <span style="color: #3366CC;">&quot;controller&quot;</span><span style="color: #339933;">:</span> <span style="color: #009900;">&#123;</span>
   <span style="color: #009900;">&#125;</span><span style="color: #339933;">,</span>
   <span style="color: #3366CC;">&quot;workers&quot;</span><span style="color: #339933;">:</span> <span style="color: #009900;">&#91;</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;router&quot;</span><span style="color: #339933;">,</span>
         <span style="color: #3366CC;">&quot;options&quot;</span><span style="color: #339933;">:</span> <span style="color: #009900;">&#123;</span>
            <span style="color: #3366CC;">&quot;pythonpath&quot;</span><span style="color: #339933;">:</span> <span style="color: #009900;">&#91;</span><span style="color: #3366CC;">&quot;..&quot;</span><span style="color: #009900;">&#93;</span>
         <span style="color: #009900;">&#125;</span><span style="color: #339933;">,</span>
         <span style="color: #3366CC;">&quot;realms&quot;</span><span style="color: #339933;">:</span> <span style="color: #009900;">&#91;</span>
            <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;">&#123;</span>
                     <span style="color: #3366CC;">&quot;name&quot;</span><span style="color: #339933;">:</span> <span style="color: #3366CC;">&quot;anonymous&quot;</span><span style="color: #339933;">,</span>
                     <span style="color: #3366CC;">&quot;permissions&quot;</span><span style="color: #339933;">:</span> <span style="color: #009900;">&#91;</span>
                        <span style="color: #009900;">&#123;</span>
                           <span style="color: #3366CC;">&quot;uri&quot;</span><span style="color: #339933;">:</span> <span style="color: #3366CC;">&quot;*&quot;</span><span style="color: #339933;">,</span>
                           <span style="color: #3366CC;">&quot;publish&quot;</span><span style="color: #339933;">:</span> <span style="color: #003366; font-weight: bold;">true</span><span style="color: #339933;">,</span>
                           <span style="color: #3366CC;">&quot;subscribe&quot;</span><span style="color: #339933;">:</span> <span style="color: #003366; font-weight: bold;">true</span><span style="color: #339933;">,</span>
                           <span style="color: #3366CC;">&quot;call&quot;</span><span style="color: #339933;">:</span> <span style="color: #003366; font-weight: bold;">true</span><span style="color: #339933;">,</span>
                           <span style="color: #3366CC;">&quot;register&quot;</span><span style="color: #339933;">:</span> <span style="color: #003366; font-weight: bold;">true</span>
                        <span style="color: #009900;">&#125;</span>
                     <span style="color: #009900;">&#93;</span>
                  <span style="color: #009900;">&#125;</span>
               <span style="color: #009900;">&#93;</span>
            <span style="color: #009900;">&#125;</span>
         <span style="color: #009900;">&#93;</span><span style="color: #339933;">,</span>
         <span style="color: #3366CC;">&quot;transports&quot;</span><span style="color: #339933;">:</span> <span style="color: #009900;">&#91;</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;web&quot;</span><span style="color: #339933;">,</span>
               <span style="color: #3366CC;">&quot;endpoint&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;tcp&quot;</span><span style="color: #339933;">,</span>
                  <span style="color: #3366CC;">&quot;port&quot;</span><span style="color: #339933;">:</span> <span style="color: #CC0000;">8080</span>
               <span style="color: #009900;">&#125;</span><span style="color: #339933;">,</span>
               <span style="color: #3366CC;">&quot;paths&quot;</span><span style="color: #339933;">:</span> <span style="color: #009900;">&#123;</span>
                  <span style="color: #3366CC;">&quot;/&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;static&quot;</span><span style="color: #339933;">,</span>
                     <span style="color: #3366CC;">&quot;directory&quot;</span><span style="color: #339933;">:</span> <span style="color: #3366CC;">&quot;../templates&quot;</span>
                  <span style="color: #009900;">&#125;</span><span style="color: #339933;">,</span>
                  <span style="color: #3366CC;">&quot;ws&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;websocket&quot;</span>
                  <span style="color: #009900;">&#125;</span>
               <span style="color: #009900;">&#125;</span>
            <span style="color: #009900;">&#125;</span>
         <span style="color: #009900;">&#93;</span>
      <span style="color: #009900;">&#125;</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;container&quot;</span><span style="color: #339933;">,</span>
         <span style="color: #3366CC;">&quot;options&quot;</span><span style="color: #339933;">:</span> <span style="color: #009900;">&#123;</span>
            <span style="color: #3366CC;">&quot;pythonpath&quot;</span><span style="color: #339933;">:</span> <span style="color: #009900;">&#91;</span><span style="color: #3366CC;">&quot;..&quot;</span><span style="color: #009900;">&#93;</span>
         <span style="color: #009900;">&#125;</span><span style="color: #339933;">,</span>
         <span style="color: #3366CC;">&quot;components&quot;</span><span style="color: #339933;">:</span> <span style="color: #009900;">&#91;</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;class&quot;</span><span style="color: #339933;">,</span>
               <span style="color: #3366CC;">&quot;classname&quot;</span><span style="color: #339933;">:</span> <span style="color: #3366CC;">&quot;app.app&quot;</span><span style="color: #339933;">,</span>
               <span style="color: #3366CC;">&quot;realm&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;transport&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;websocket&quot;</span><span style="color: #339933;">,</span>
                  <span style="color: #3366CC;">&quot;endpoint&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;tcp&quot;</span><span style="color: #339933;">,</span>
                     <span style="color: #3366CC;">&quot;host&quot;</span><span style="color: #339933;">:</span> <span style="color: #3366CC;">&quot;127.0.0.1&quot;</span><span style="color: #339933;">,</span>
                     <span style="color: #3366CC;">&quot;port&quot;</span><span style="color: #339933;">:</span> <span style="color: #CC0000;">8080</span>
                  <span style="color: #009900;">&#125;</span><span style="color: #339933;">,</span>
                  <span style="color: #3366CC;">&quot;url&quot;</span><span style="color: #339933;">:</span> <span style="color: #3366CC;">&quot;ws://127.0.0.1/ws&quot;</span>
               <span style="color: #009900;">&#125;</span>
            <span style="color: #009900;">&#125;</span>
         <span style="color: #009900;">&#93;</span>
      <span style="color: #009900;">&#125;</span>
   <span style="color: #009900;">&#93;</span>
<span style="color: #009900;">&#125;</span></pre></div></div>

<p>Ceci n&#8217;est pas un tuto sur la mise en prod d&#8217;un projet WAMP avec crossbar, alors je ne vais pas rentrer dans les détails, mais en gros on change les ports HTTP et WAMP pour le port 8080, et on donne juste l&#8217;url <code>/ws</code> comme point d&#8217;entrée pour WAMP (on doit éditer le code source de la démo en conséquence). On sert les fichiers statiques en utilisant la section &#8220;transports&#8221; et notre app via la section &#8220;components&#8221;. Pour le realm, encore une fois, je skip, c&#8217;est un sujet à part, celui par défaut marche pour notre usage.</p>
<p>A partir de là il y a plusieurs solutions possibles :</p>
<ul>
<li>On souhaite que crossbar gère tout lui-même. Dans ce cas il faut utiliser <a href="https://wiki.apache.org/httpd/NonRootPortBinding">un outil</a> pour binder le port 80 sur le port 8080. Ça évite de lancer crossbar en root.</li>
<li>On peut mettre nginx devant (parce qu&#8217;on en a déjà un qui tourne, parce que c&#8217;est cool pour le load balancing, parce l&#8217;hébergeur le fournit) et dans ce cas, comme pour les autres config, il faut qu&#8217;il fasse un <a href="">proxy_pass</a> pour HTTP et Websocket sur les bonnes URLs.</li>
<li>On est un gros bourrin, on met le port 80 dans le fichier de config, on lance le tout avec un sudo et on sert les fesses. Si un sys admin passe par là il vous lapidera, mais ça marche bien et facilement tant que personne n&#8217;essaye de pirater votre serveur :)</li>
</ul>
<p>Pour lancer la machine :</p>

<div class="wp_syntax"><div class="code"><pre class="bash" style="font-family:monospace;">crossbar run</pre></div></div>

<p>On met un petit script d&#8217;init pour le lancer au démarrage, et on est good.</p>
 <p><a href="http://sametmax.com/?flattrss_redirect&amp;id=11146&amp;md5=229ce90a2b3a4686669eef89a213ec29" title="Flattr" target="_blank"><img src="media/fc229d00.flattr-badge-large.png" data-original-source="http://sametmax.com/wp-content/plugins/flattr/img/flattr-badge-large.png" alt="flattr this!"/></a></p>


