<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
<channel>
<title><![CDATA[Planet Python francophone]]></title>
<description><![CDATA[News Python francophones]]></description>
<link>http://www.afpy.org/planet/</link>
<language>en</language>
<copyright>Copyright 2008, Atomisator</copyright>
<pubDate>Sat, 15 Mar 2008 00:15:05 +0200</pubDate>
<lastBuildDate>Sat, 15 Mar 2008 00:15:05 +0200</lastBuildDate>
  <item>
    <title><![CDATA[[afpyro] AFPyro à Lyon - le 22 mai 2013]]></title>
    <description><![CDATA[<div class="section" id="afpyro-a-lyon-le-22-mai-2013">
 
<p>Un Afpyro aura lieu le mercredi 22 mai à partir de 20h à l’<a class="reference external" href="http://www.lantreautre.fr/">Antre Autre</a> - <a class="reference external" href="http://www.openstreetmap.org/?mlat=45.769148&mlon=4.831513&zoom=18&layers=M">11 rue Terme - 69001 Lyon</a>.</p>
<p><a class="reference external" href="http://hashbang.fr">Arthur Vuillard</a> fera une présentation éclair sur Django, le fameux framework web pour les perfectionnistes. Cette présentation donnera suite à une discussion sur les diffèrents frameworks webs disponibles en Python.</p>
<p>L’<a class="reference external" href="http://www.lantreautre.fr/">Antre Autre</a> est un lieu où nous pouvons discuter autour d’un verre, et, pour ceux qui le souhaitent, prendre un repas.</p>
<dl class="docutils"><dt>Pour se rendre à l’<a class="reference external" href="http://www.lantreautre.fr/">Antre Autre</a> :</dt>
<dd><ul class="first last simple"><li>en métro : arrêt Hôtel de Ville</li>
<li>en bus : lignes C13 et C18 arrêt Mairie du 1er ou lignes 19, C14 et C3 à l’arrêt Terreaux</li>
<li>en vélo’v : stations Place Sathonay, Carmélites Burdeau, Place de la paix</li>
</ul></dd>
</dl></div>]]></description>
    <link><![CDATA[http://afpy.ro/dates/2013/2013_05_22.html]]></link>
    <pubDate>2013-05-22 00:00:00</pubDate>
  </item>
  <item>
    <title><![CDATA[[afpy.org] Solution linux 2013 à Paris]]></title>
    <description><![CDATA[L'AFPy sera présente au salon Solution Linux le 28 et 29 mai.]]></description>
    <link><![CDATA[http://www.afpy.org/Members/gawel/solution-linux-2013]]></link>
    <pubDate>2013-05-21 15:41:19</pubDate>
  </item>
  <item>
    <title><![CDATA[[carlchenet] Vrac de mini-messages n°2]]></title>
    <description><![CDATA[Suivez-moi aussi sur Identi.ca ou sur Twitter. Pour cette catégorie d&#8217;article, je passe à une publication hebdomadaire Les liens d&#8217;origine sont enrichis des approfondissements que j&#8217;ai pu effectuer entre la publication du dent/tweet et la publication de cet article. #debian #wheezy 7.1 devrait être publiée samedi 15 juin http://ur1.ca/dvraj  =&#62; Information très intéressante qui n&#8217;a pas été beaucoup relayée. La [&#8230;]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=carlchenet.com&blog=7722010&post=1649&subd=carlchenet&ref=&feed=1" width="1" height="1" />]]></description>
    <link><![CDATA[http://carlchenet.com/2013/05/21/vrac-de-mini-messages-n2/]]></link>
    <pubDate>2013-05-21 11:01:35</pubDate>
    <category>april</category>
    <category>debian-fr</category>
    <category>planet-cullt</category>
    <category>python</category>
    <category>vrac-mini-messages</category>
    <category>debian</category>
    <category>django</category>
    <category>postgresql</category>
    <category>qnap</category>
    <category>wheezy</category>
    <category>zinnia</category>
  </item>
  <item>
    <title><![CDATA[[tarek] A step-by-step introduction to Circus]]></title>
    <description><![CDATA[<div class="note">
<p class="first admonition-title">Note</p>
<p class="last">Circus is a process &amp; socket manager. See <a class="reference external" href="https://circus.readthedocs.org">https://circus.readthedocs.org</a></p>
</div>
<div class="figure">
<a class="reference external image-reference" href="https://secure.flickr.com/photos/kennethreitz/8751753401/in/pool-2174519@N25/"><img alt="https://farm9.staticflickr.com/8420/8751753401_0760d37279.jpg" src="https://farm9.staticflickr.com/8420/8751753401_0760d37279.jpg" /></a>
<p class="caption">Photo by kennethreitz</p>
</div>
<p>During Django Con, I was asked how to use Circus to run &amp; monitor a Python
web application. The documentation has no single page step-by-step tutorial
yet, so here goes... this blog post will be integrated into the documentation
for the next release.</p>
<div class="section" id="installation">
<h2>Installation</h2>
<p>Circus is tested under Mac OS X and Linux, on the latest Python 2.6 and 2.7.
To run a full Circus, you will also need <strong>libzmq</strong>, <strong>libevent</strong> &amp;
<strong>virtualenv</strong>.</p>
<p>Under Debuntu:</p>
<pre class="literal-block">
$ sudo apt-get install libzmq-dev libevent python-virtualenv
</pre>
<p>Create a virtualenv and install <em>circus</em>, <em>circus-web</em> and <em>chaussette</em>
in it</p>
<pre class="literal-block">
$ virtualenv /tmp/circus
$ cd /tmp/circus
$ bin/pip install circus
$ bin/pip install circus-web
$ bin/pip install chaussette
</pre>
<p>Once this is done, you'll find a plethora of commands in the local bin dir.</p>
</div>
<div class="section" id="usage">
<h2>Usage</h2>
<p><em>Chaussette</em> comes with a default Hello world app, try to run it:</p>
<pre class="literal-block">
$ bin/chaussette
</pre>
<p>You should be able to visit <a class="reference external" href="http://localhost:8080">http://localhost:8080</a> and see <em>hello world</em>.</p>
<p>Stop Chaussette and add a circus.ini file in the directory containing:</p>
<pre class="literal-block">
[circus]
stats_endpoint = tcp://127.0.0.1:5557
httpd = 1

[watcher:webapp]
cmd = bin/chaussette --fd $(circus.sockets.web)
numprocesses = 3
use_sockets = True

[socket:web]
host = 127.0.0.1
port = 9999
</pre>
<p>This config file tells Circus to bind a socket on port <em>9999</em> and run
3 chaussettes workers against it. It also activates the Circus web
dashboard and the statistics module.</p>
<p>Save it &amp; run it using <strong>circusd</strong>:</p>
<pre class="literal-block">
$ bin/circusd --daemon circus.ini
</pre>
<p>Now visit <a class="reference external" href="http://127.0.0.1:9999">http://127.0.0.1:9999</a>, you should see the hello world app.</p>
<p>You can also visit <a class="reference external" href="http://localhost:8080/">http://localhost:8080/</a> and enjoy the Circus web dashboard.</p>
</div>
<div class="section" id="interaction">
<h2>Interaction</h2>
<p>Let's use the circusctl shell while the system is running:</p>
<pre class="literal-block">
$ bin/circusctl
circusctl 0.7.1
circusd-stats: active
circushttpd: active
webapp: active
(circusctl)
</pre>
<p>You get into an interactive shell. Type <strong>help</strong> to get all commands:</p>
<pre class="literal-block">
(circusctl) help

Documented commands (type help &lt;topic&gt;):
========================================
add     get            list         numprocesses  quit     rm      start   stop
decr    globaloptions  listen       numwatchers   reload   set     stats
dstats  incr           listsockets  options       restart  signal  status

Undocumented commands:
======================
EOF  help
</pre>
<p>Let's try basic things. Let's list the web workers processes and add a
new one:</p>
<pre class="literal-block">
(circusctl) list webapp
13712,13713,13714
(circusctl) incr webapp
4
(circusctl) list webapp
13712,13713,13714,13973
</pre>
<p>Congrats, you've interacted with your Circus! Get off the shell
with Ctrl+D and now run circus-top:</p>
<pre class="literal-block">
$ bin/circus-top
</pre>
<p>This is a top-like command to watch all your processes' memory and CPU
usage in real time.</p>
<p>Hit Ctrl+C and now let's quit Circus completely via circus-ctl:</p>
<pre class="literal-block">
$ bin/circusctl quit
ok
</pre>
</div>
<div class="section" id="next-steps">
<h2>Next steps</h2>
<p>You can plug your own WSGI application instead of Chaussette's hello
world simply by pointing the application callable.</p>
<p>Chaussette also comes with many backends like Gevent or Meinheld.</p>
<p>Read <a class="reference external" href="https://chaussette.readthedocs.org/">https://chaussette.readthedocs.org/</a> for all options.</p>
</div>]]></description>
    <link><![CDATA[http://blog.ziade.org/2013/05/20/a-step-by-step-introduction-to-circus/]]></link>
    <pubDate>2013-05-20 16:30:00</pubDate>
    <category>python</category>
    <category>mozilla</category>
  </item>
  <item>
    <title><![CDATA[[anybox] Python : compréhension des intensions en 1 minute]]></title>
    <description><![CDATA[]]></description>
    <link><![CDATA[http://anybox.fr/blog/python-comprehension-des-intensions-en-1-minute]]></link>
    <pubDate>2013-05-19 11:10:00</pubDate>
    <category>Python</category>
  </item>
  <item>
    <title><![CDATA[[Biologeek] Une quête de sens]]></title>
    <description><![CDATA[<p>Je suis (non-)intervenu à <a href="http://sudweb.fr/2013/">SudWeb 2013</a> pour animer un débat sur le sens de notre implication dans notre métier suite à une informelle donnée à <a href="http://www.paris-web.fr/2012/">ParisWeb 2012</a>. J'avais choisi <a href="https://larlet.fr/david/talks/">des thématiques très larges</a> telles que l'argent, l'utilité, la reconnaissance, l'adrénaline, le partage, la santé, l'écologie ou le fun. L'objectif était d'avoir un débat non technique et de faire se poser quelques questions à l'auditoire sur les choix qu'ils font professionnellement (et finalement personnellement aussi) au cours de leur vie. <strong>Petite rétrospective sur cette heure d'échanges à plus de 100.</strong></p>
<h3>Ce qui a bien marché</h3>
<ul>
<li>le fait d'énoncer des règles claires en début de session a permis de ne pas avoir de monopolisation de la parole par un seul petit groupe de personnes (après discussion avec le staff de SudWeb, ils faisaient attention dans la distribution des micros à répartir équitablement la parole donc cela ne se faisait pas tout seul non plus ;-)) ;</li>
<li>la disposition de la salle a beaucoup fait pour que le débat soit possible et qu'une dynamique de communication en face-à-face se mette en place ;</li>
<li>la participation a été assez exceptionnelle avec un démarrage immédiat du débat (je stressais un peu de me retrouver avec une salle muette) et un engagement qui n'a pas faibli au cours de la session ;</li>
<li>la variabilité dans les points de vues et les niveaux de recul des participants, c'était à titre personnel assez rafraichissant d'avoir des remarques qui partaient un peu dans tous les sens sans forcément suivre les thématiques proposées ;</li>
<li>mon silence, j'ai réussi à me retenir plusieurs fois d'intervenir pour laisser la parole à la salle, c'est extrêmement frustrant mais je pense que cela a été bénéfique au débat ;</li>
<li>les discussions que cela a produit au cours des jours suivants, je n'avais jamais eu autant de retours suite à une intervention et au-delà des retours personnels j'ai pu observer de nombreux échanges — en périphérie des sessions — relatifs au débat ce qui ne peut que me ravir.</li>
</ul>
<h3>Ce qui pourrait être amélioré</h3>
<ul>
<li>beaucoup de consensualité dans les échanges et j'ai du mal à trouver comment est-ce que cela pourrait évoluer, ni même si ça doit l'être. Ma crainte est plus dans le mode « bisounours » activé par la prise de parole en public ;</li>
<li>l'introduction des thèmes à partir de questions focalisait la salle sur les questions et peu sur la thématique au sens large, c'est dommage mais je n'ai pas trouvé mieux pour lancer les sujets ;</li>
<li>le sujet sur l'adrénaline n'a pas été compris par tout le monde et j'ai eu des retours très contrastés par la suite (certains ne s'y retrouvant pas du tout et d'autres à fond), l'objectif était surtout de faire une pause dans les sujets plutôt lourds qui étaient discutés avant et après ;</li>
<li>le sentiment de frustration vu le nombre de personnes qui souhaitent s'exprimer mais c'est le jeu, on avait 60 minutes pour une centaine de personnes ça fait quelques secondes seulement par participant…</li>
</ul>
<h3>Et la suite ?</h3>
<p>Une question ouverte en guise de conclusion sans avoir vraiment de proposition technique concrète pour continuer le débat. Après réflexion (et de nombreuses discussions), je ne pense pas qu'il soit pertinent de continuer en ligne par contre je serais ravi que les discussions continuent ici ou ailleurs en espérant <a href="https://larlet.fr/david/blog/2013/manuel-jardin/">avoir semé quelques graines</a> qui pourront germer de proche en proche.</p>]]></description>
    <link><![CDATA[https://larlet.fr/david/blog/2013/quete-sens/]]></link>
    <pubDate>2013-05-18 23:00:00</pubDate>
  </item>
  <item>
    <title><![CDATA[[carlchenet] Les nouveautés de Python 3.3 (GLMF)]]></title>
    <description><![CDATA[Suivez-moi aussi sur Identi.ca ou sur Twitter. Mon article sur les nouveautés de Python 3.3 est paru dans le Gnu/Linux Magazine France d&#8217;avril 2013. Voici le sommaire de ce long article détaillant les principales nouveautés de la dernière mouture du langage Python, exemples à l&#8217;appui le plus souvent. Avec l&#8217;accord de mon éditeur, je vous propose [&#8230;]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=carlchenet.com&blog=7722010&post=1584&subd=carlchenet&ref=&feed=1" width="1" height="1" />]]></description>
    <link><![CDATA[http://carlchenet.com/2013/05/16/les-nouveautes-de-python-3-3-glmf/]]></link>
    <pubDate>2013-05-15 22:01:27</pubDate>
    <category>april</category>
    <category>articles</category>
    <category>debian-fr</category>
    <category>planet-cullt</category>
    <category>python</category>
    <category>debian</category>
    <category>glmf</category>
    <category>jessie</category>
    <category>sid</category>
  </item>
  <item>
    <title><![CDATA[[anybox] Python : comprendre les docstrings en 1 minute]]></title>
    <description><![CDATA[]]></description>
    <link><![CDATA[http://anybox.fr/blog/python-comprendre-les-docstrings-en-1-minute]]></link>
    <pubDate>2013-05-13 21:15:00</pubDate>
    <category>Python</category>
  </item>
  <item>
    <title><![CDATA[[tarek] FaitMain magazine #2]]></title>
    <description><![CDATA[<img alt="http://faitmain.org/volume-2/xcover-pdf.png.pagespeed.ic.kjZ_axhrlA.png" class="align-right" src="http://faitmain.org/volume-2/xcover-pdf.png.pagespeed.ic.kjZ_axhrlA.png" />
<p>We've launched the second issue of <a class="reference external" href="http://faitmain.org">Fait Main</a>, a
French magazine about the DIY movement. There are a lot of rough edges
remaining since the magazine is built on our week-ends, but it's starting
to look good. I love the content of the second issue because it talks about
very different topics and not only the usual suspects (Python, Raspberry-PI,
Arduino.)</p>
<p>One thing I want to mention to all my English speaking friends:
<strong>we also accept articles in English - we take care of the translation</strong>.</p>
<p>This issue has two articles that were originally in English:</p>
<ul class="simple">
<li><a class="reference external" href="http://faitmain.org/volume-2/ouvrir-puce.html">How to open a microchip</a></li>
<li><a class="reference external" href="http://faitmain.org/volume-2/surveillance-digues.html">Levees monitoring in Holland</a></li>
</ul>
<p>The first one appeared on my radar in Hacker News, and I asked the author
the permission to translate it and publish it in the magazine. The second one
was written by a member of the Python community,
<a class="reference external" href="http://faitmain.org/auteurs/fritz_van_deventer.html">Fritz van Deventer</a> I
met during the FOSDEM in a corridor session. I was impressed by his work
and asked him to write a little article for our magazine.</p>
<p>So, if you want to write something for the third issue, or know a great
article we should translate - let me know in the comments.</p>
<p>Issue #3 will be out in August.</p>]]></description>
    <link><![CDATA[http://blog.ziade.org/2013/05/13/faitmain-magazine-2/]]></link>
    <pubDate>2013-05-13 19:56:00</pubDate>
  </item>
  <item>
    <title><![CDATA[[sciunto] Et si github fermait ?]]></title>
    <description><![CDATA[Que feriez-vous ? Le titre est provocateur. Pour ceux qui n&#8217;utilisent pas github, je me doute que vous ne ferez rien. Quoique&#8230; Comme vous le savez, github est un service de gestion de projet utilisant git. Comme tout service administré par un tiers (qu&#8217;il soit libre ou non, ça ne change rien), sa pérennité n&#8217;est [&#8230;]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=sciunto.wordpress.com&blog=10984286&post=901&subd=sciunto&ref=&feed=1" width="1" height="1" />]]></description>
    <link><![CDATA[http://sciunto.wordpress.com/2013/05/12/et-si-github-fermait/]]></link>
    <pubDate>2013-05-12 09:31:12</pubDate>
    <category>auto-hébergement</category>
    <category>Developpement</category>
    <category>Planet-april</category>
    <category>Planet-libre</category>
    <category>Python</category>
    <category>Sysadmin</category>
  </item>
  <item>
    <title><![CDATA[[afpyro] AFPyro à Bruxelles (BE) - vendredi 10 Mai]]></title>
    <description><![CDATA[<div class="section" id="afpyro-a-bruxelles-be-vendredi-10-mai">
 
<p>Un AFPyro, Apéro Python, aura lieu ce 10 mai à l’Université de Bruxelles. Il se composera d’une série de Lightning Talks au Batiment K de l’ULB suivi d’un passage au Restaurant « La Bécasse ». Celles-ci auront lieu au batiment K de l’ULB  (Campus Solbosh), salle K.4.601 (quasiment la même salle pour la devroom python du FOSDEM) et seront suivi d’un repas à “La bécasse” à partir de 21:00 [2]</p>
<p>L’ULB est facilement accessible via les bus 71 et 95 ainsi que les trams 7, 25, 94 et via le Ring. Si vous ne voyez pas comment venir n’hésitez pas à demander.</p>
<div class="section" id="horaire">
<h2>Horaire</h2>
<ul class="simple"><li>19:00 Ouverture</li>
<li>19:15 OpenPyXL : réalisation de la librairie, dont reverse-engineering des specs OOXML par Eric Gazoni (30min, en français)</li>
<li>19:55 Internal workings of the Declarative extension of SQLAlchemy par Erik Janssens (30min, en anglais)</li>
<li>20:30 Discussion Ouverte sur les AFPyro-BE, la communauté belge python et pouvant dériver sur ce que vous voulez</li>
<li>21:00 Repas à La Becasse</li>
</ul><p>Merci de vous inscrire au Framadate pour que nous puissons reserver le nombre correct de place au restaurant: <a class="reference external" href="http://framadate.org/jze5k7xeqgo3dlh5">http://framadate.org/jze5k7xeqgo3dlh5</a></p>
</div>
</div>]]></description>
    <link><![CDATA[http://afpy.ro/dates/2013/2013_05_10.html]]></link>
    <pubDate>2013-05-10 00:00:00</pubDate>
  </item>
  <item>
    <title><![CDATA[[ascendances] Évolution du nombre de messages sur debian-l10n-fr et debian-users-fr avec pychart]]></title>
    <description><![CDATA[Pychart est une bibliothèque Python permettant des graphiques directement en python. Un paquet Debian est disponible (nommé python-pychart) avec la dernière version 1.39 (qui date de 2006). Debian utilise massivement des listes de diffusion pour la collaboration de ses membres. Parmi les nombreuses listes existantes, trois sont francophones : debian-users-fr, dédiée aux questions des utilisateurs ; debian-l10n-fr, [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=ascendances.wordpress.com&blog=24442983&post=1111&subd=ascendances&ref=&feed=1" width="1" height="1" />]]></description>
    <link><![CDATA[http://ascendances.wordpress.com/2013/05/02/evolution-du-nombre-de-messages-sur-debian-l10n-fr-et-debian-users-fr-avec-pychart/]]></link>
    <pubDate>2013-05-02 08:34:34</pubDate>
    <category>Debian</category>
    <category>Python</category>
    <category>okiwi</category>
    <category>yaal</category>
  </item>
  <item>
    <title><![CDATA[[sciunto] Des logiciels pour être efficace]]></title>
    <description><![CDATA[Depuis que j&#8217;utilise des logiciels libres, je me suis tourné vers la programmation mais c&#8217;est aussi parce que je voulais faire cette apprentissage que je suis passé à GNU/Linux. De cette expérience, je tire différents savoirs nouveaux et au final, je me demande si je n&#8217;ai pas appris d&#8217;avantage concernant les éléments périphériques que sur [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=sciunto.wordpress.com&blog=10984286&post=895&subd=sciunto&ref=&feed=1" width="1" height="1" />]]></description>
    <link><![CDATA[http://sciunto.wordpress.com/2013/05/01/logiciels-efficace/]]></link>
    <pubDate>2013-05-01 16:57:10</pubDate>
    <category>Developpement</category>
    <category>Logiciel</category>
    <category>Planet-april</category>
    <category>Planet-libre</category>
    <category>Python</category>
    <category>Science</category>
    <category>Sysadmin</category>
    <category>adminsys</category>
    <category>git</category>
    <category>python</category>
    <category>screen</category>
    <category>ssh</category>
    <category>tmux</category>
    <category>vim</category>
    <category>zsh</category>
  </item>
  <item>
    <title><![CDATA[[afpyro] AFPyro à Toulouse le 30 avril 2013]]></title>
    <description><![CDATA[<div class="section" id="afpyro-a-toulouse-le-30-avril-2013">
 
<p>Un Afpyro aura lieu le mardi 30 avril à partir de 20h à <a class="reference external" href="http://www.openstreetmap.org/?lat=43.60428035259247&lon=1.4375889301300049&zoom=18">La Tireuse 24 rue Pargaminières</a></p>
<p>Pas de présentation prévue, car organisé au pied levé à la pause café ! Ce sera juste une rencontre autour d’une bière (ou d’un jus de fruit) pour parler de python, avec des pythonistas toulousains.</p>
<p>Pour se rendre à la tireuse, métro ligne A : arrêt Capitole, puis aller dans la rue qui rejoint la place St Pierre.</p>
</div>]]></description>
    <link><![CDATA[http://afpy.ro/dates/2013/2013_04_30.html]]></link>
    <pubDate>2013-04-30 00:00:00</pubDate>
  </item>
  <item>
    <title><![CDATA[[tarek] Thoughts on Load Testing]]></title>
    <description><![CDATA[<p>We are <a class="reference external" href="http://funkload.nuxeo.org/">Funkload</a> fans at Mozilla Services. Writing a
load test against one of our web service is dead easy using that tool.</p>
<p>It boils down to writing a functional test in Python, that calls the service,
then ask Funkload to run it in parallel, accross many threads and boxes.</p>
<p>We've written a web app on the top of Funkload called Marteau -
<a class="reference external" href="http://blog.ziade.org/2012/08/22/marteau-distributed-load-tests/">initial blog post about it</a>
that now allows us to provision slaves on demand on Amazon WS and run
distributed load tests.</p>
<p>You can watch this hangout to better understand what Marteau &amp; Funkload are <a class="reference external" href="https://plus.google.com/106436370949746015255/posts/Lq7t4jkiwNR">https://plus.google.com/106436370949746015255/posts/Lq7t4jkiwNR</a></p>
<p>Marteau is already useful to us but I am now facing some difficulties to
improve the tool and add some features we want, like:</p>
<ul class="simple">
<li>realtime JS charts of the ongoing load test</li>
<li>web socket load testing</li>
<li>the ability to run load tests written in other languages or frameworks, like node.js</li>
</ul>
<p>Funkload is a project that was started 10 years ago, and is very robust. But it
has evolved from a tool that runs on a single box to a distributed load testing
tool by using SSH. In other words, a distributed load test in Funkload is just
several load tests running in parallel, then a merge of all the results files
that are copied back to a single box through SSH.</p>
<p>It means that there is no specific client/server protocol and no way
to interact live with the slaves that are running in the distributed mode.
You can kill them of course, or just wait for them to return.</p>
<p>I started to add some <a class="reference external" href="https://github.com/nuxeo/FunkLoad/blob/master/src/funkload/rtfeedback.py">communication channel</a>
in the tool but the core itself was not built with this in mind.</p>
<p>I have found the <a class="reference external" href="http://locust.io">locust.io</a> project, which has this
approach and which looks very promising, but does not exactly provide
what I am looking for.</p>
<p>For example, I don't really want developers to have to write load tests
using yet another set of APIs. The concept of writing Python unit tests is
fabulous and I don't want to lose it.</p>
<div class="section" id="starting-loads">
<h2>Starting Loads</h2>
<p>I am experimenting on something new, based on what we've learned from our experience
with Funkload and what we need in Marteau.</p>
<p>It's called <strong>Loads</strong> and it's a client/server architecture based on ZMQ
that will use a very simple protocol based on Message Pack or maybe BJson
- we will see.</p>
<p>It's quite similar to locust.io in the principles, but instead of introducing
new APIS, it's going to rely on a set of API people know &amp; like :
<a class="reference external" href="http://docs.python-requests.org">Requests</a>.</p>
<p>So, how will a load test with <strong>Loads</strong> look like ?</p>
<div class="highlight"><pre><span class="kn">import</span> <span class="nn">unittest</span>
<span class="kn">from</span> <span class="nn">loads</span> <span class="kn">import</span> <span class="n">Session</span>


<span class="k">class</span> <span class="nc">TestWebSite</span><span class="p">(</span><span class="n">unittest</span><span class="o">.</span><span class="n">TestCase</span><span class="p">):</span>

    <span class="k">def</span> <span class="nf">setUp</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
        <span class="bp">self</span><span class="o">.</span><span class="n">session</span> <span class="o">=</span> <span class="n">Session</span><span class="p">()</span>

    <span class="k">def</span> <span class="nf">test_something</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
        <span class="n">res</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">session</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s">'http://blog.ziade.org'</span><span class="p">)</span>
        <span class="bp">self</span><span class="o">.</span><span class="n">assertTrue</span><span class="p">(</span><span class="s">'Tarek'</span> <span class="ow">in</span> <span class="n">res</span><span class="o">.</span><span class="n">content</span><span class="p">)</span>
</pre></div>
<p>That's it. A unittest class that uses a <em>Session</em> class. The Session object
is <a class="reference external" href="http://docs.python-requests.org/en/latest/user/advanced/#session-objects">the same one you find in Requests</a>.</p>
<p>I am not sure yet how I will extend the API so it can work with web sockets.
That'll come later.</p>
<p>This test can then be executed using the <strong>loads</strong> command. Example for 10 concurrent users
and 10 runs each:</p>
<div class="highlight"><pre><span class="err">$</span> <span class="nb">bin</span><span class="o">/</span><span class="n">loads</span> <span class="n">test_blog</span><span class="o">.</span><span class="n">TestWebSite</span><span class="o">.</span><span class="n">test_something</span> <span class="o">-</span><span class="n">c</span> <span class="mi">10</span> <span class="o">-</span><span class="n">n</span> <span class="mi">10</span>
<span class="p">[</span><span class="o">====================================================================================================</span><span class="p">]</span>  <span class="mi">100</span><span class="o">%</span>
</pre></div>
<p>Like locust.io, Loads uses <a class="reference external" href="http://sdiehl.github.com/gevent-tutorial/#greenlets">greenlets</a>
here, so you can already push quite some load from a single box.</p>
<p>Everytime a request is done, the status code returned by the server and the time it took
are pushed in a stream. The stream can be the standard output or a ZMQ stream.</p>
<p>And I will be using the ZMQ stream to actually drive distributed tests.</p>
<p>Each <strong>agent</strong> will connect to a master through ZMQ. The master will be able to drive
them through a dedicated ROUTE socket and will ask agents to run some load tests.</p>
<p>Results will be sent back via ZMQ in real-time using a dedicated channel.</p>
<p>The master will then publish all results in a merged stream - a ZMQ pub socket.</p>
<p>From there, the Marteau web app will be able to register to that stream of result to
display some real time charts and allow any kind of interaction.
Or any app that whishes to do something with the results.</p>
<p>And since there are some ZMQ bindings in most languages, it's possible to
implement a node.js client for example, so the system can have agents able
to run Javascript-based tests and report back results to the master like
the built-in Python agent.</p>
<p>That's the plan. I have started the prototype here: <a class="reference external" href="https://github.com/tarekziade/loads">https://github.com/tarekziade/loads</a>
and I am very excited about this project.</p>
</div>]]></description>
    <link><![CDATA[http://blog.ziade.org/2013/04/25/thoughts-on-load-testing/]]></link>
    <pubDate>2013-04-25 21:50:00</pubDate>
    <category>python</category>
    <category>mozilla</category>
  </item>
  <item>
    <title><![CDATA[[Biologeek] Des API et des hommes]]></title>
    <description><![CDATA[<p>Les API actuelles — s'auto-proclamant RESTful — nécessitent bien souvent de développer un client qui leur est propre pour accéder aux données en raison de leurs spécificités. Au mieux, ces API utilisent HTTP à bon escient et font transiter du JSON à partir d'URL « propres ».</p>
<p>Cela semble bien éloigné de la vision de Roy T. Fielding (qui a défini REST <a href="http://opikanoba.org/tr/fielding/rest/">en 2000 dans sa thèse</a>) et qui a écrit <a href="http://roy.gbiv.com/untangled/2008/rest-apis-must-be-hypertext-driven">un billet on ne peux plus clair en 2008</a> :</p>
<blockquote>
<p>A truly RESTful API looks like hypertext. Every addressable unit of information carries an address.</p>
</blockquote>
<p>Puis surenchérit en commentaire :</p>
<blockquote>
<p>Think of it in terms of the Web. How many Web browsers are aware of the distinction between an online-banking resource and a wiki resource? None of them.</p>
</blockquote>
<p>5 ans plus tard, <strong>on en est encore à réécrire un client pour chaque API ce qui équivaudrait à écrire un navigateur propre à chaque site web visité !</strong> Comment y remédier facilement ? Revenir à la partie oubliée de REST : <strong>les liens</strong>.</p>
<p>Si votre API devient navigable, en liant chaque ressource présentée depuis sa racine, un client générique va pouvoir la parcourir de proche en proche en suivant les liens comme un utilisateur le fait sur le Web.</p>
<p>Cela résout énormément de problématiques à la fois lorsque l'on prend cette approche :</p>
<ul>
<li><em>versionnement</em> : est-ce qu'un utilisateur se soucie de la version du site qu'il consulte ? Non. Il suit les liens et si la migration a bien été effectuée il y a des redirections et les codes HTTP appropriées pour gérer ses anciens favoris. Les formulaires ont été mis à jour avec le site et il suffit qu'il remplisse correctement ceux qui lui sont dorénavant présentés.</li>
<li><em>URL propres</em> : est-ce qu'un utilisateur se soucie de la beauté des URL qu'il parcoure ? Quand je vois la tête de celles produites par Google ou Amazon j'en doute. Un développeur ne devrait pas avoir à se soucier de cela si le client suit les liens qui lui sont proposés.</li>
<li><em>documentation</em> : est-ce qu'un utilisateur a besoin d'une documentation pour naviguer sur votre site ? De toute façon, il y a peu de chance qu'il la lise, en revanche il est utile de lui formuler des messages d'erreurs intelligibles lorsqu'il se trompe de chemin. Il peut être intéressant de faire un rappel sur le métier et les concepts abordés car le développeur — à la différence du visiteur — n'est peut-être pas concerné par le sujet de l'API en question.</li>
<li><em>pagination</em> : en utilisant les attributs permettant de typer la relation entre les liens, il est possible de fournir les liens vers la page suivante et précédente explicitement.</li>
</ul>
<p>Ces questions se sont posées pour les sites Web également il y a des années : souvenez-vous des sites avec un /v4/ dans l'URL ou d'une page d'accueil expliquant comment accéder aux différentes parties du site en « cliquant ici ».</p>
<p>Bien sûr tout cela implique d'avoir un format qui soit hypertexte (pas JSON donc) comme XHTML ou Atom. Si vous voulez vraiment adapter votre JSON actuel il existe 4 implémentations tentant d'introduire des liens typés :</p>
<ul>
<li><a href="http://json-ld.org/">JSON-LD</a> (LD pour Linked Data), proche des concepts du Web Sémantique ;</li>
<li><a href="http://stateless.co/hal_specification.html">HAL JSON</a> le plus simple, peut-être un peu trop ;</li>
<li><a href="http://www.amundsen.com/media-types/collection/">JSON Collections</a> que je n'ai pas essayé ;</li>
<li><a href="https://github.com/kevinswiber/siren">Siren</a> le plus récent qui est en train de monter rapidement.</li>
</ul>
<p>Lorsque vous voulez fournir un moyen d'accéder à vos données via une API hypermedia, <strong>mettez vous à la place du développeur et demandez vous si votre API est navigable, fait partie intégrante du Web et nécessite une documentation.</strong></p>
<p><em>Ce billet fait suite à mon intervention à Mix-IT lors d'un lightning talk dont vous pourrez retrouver le support <a href="https://larlet.fr/david/talks/#20">sur la partie dédiée</a>.</em></p>]]></description>
    <link><![CDATA[https://larlet.fr/david/blog/2013/api-hommes/]]></link>
    <pubDate>2013-04-24 23:00:00</pubDate>
  </item>
  <item>
    <title><![CDATA[[afpyro] AFPyro à Lyon - le 24 avril 2013]]></title>
    <description><![CDATA[<div class="section" id="afpyro-a-lyon-le-24-avril-2013">
 
<p>Un Afpyro aura lieu le mercredi 24 avril à partir de 20h à l’<a class="reference external" href="http://www.lantreautre.fr/">Antre Autre</a> - <a class="reference external" href="http://www.openstreetmap.org/?mlat=45.769148&mlon=4.831513&zoom=18&layers=M">11 rue Terme - 69001 Lyon</a>.</p>
<p><a class="reference external" href="https://twitter.com/bbourgois">Blandine Bourgois</a> (du groupe <a class="reference external" href="http://naosurseine.eventbrite.fr/">Nao sur Seine</a>) et Xavier Basset (du groupe <a class="reference external" href="http://www.pepinno.com/NaoIsAGone/">Nao is a gone</a>) nous feront une présentation sur et avec un <a class="reference external" href="http://www.aldebaran-robotics.com/fr/Decouvrir-NAO/caracteristiques-principales/plateforme-hardware.html">robot Nao</a>, un petit robot humanoïde programmé en Python.</p>
<p>L’<a class="reference external" href="http://www.lantreautre.fr/">Antre Autre</a> est un lieu où nous pouvons discuter autour d’un verre, et, pour ceux qui le souhaitent, prendre un repas.</p>
<dl class="docutils"><dt>Pour se rendre à l’<a class="reference external" href="http://www.lantreautre.fr/">Antre Autre</a> :</dt>
<dd><ul class="first last simple"><li>en métro : arrêt Hôtel de Ville</li>
<li>en bus : lignes C13 et C18 arrêt Mairie du 1er ou lignes 19, C14 et C3 à l’arrêt Terreaux</li>
<li>en vélo’v : stations Place Sathonay, Carmélites Burdeau, Place de la paix</li>
</ul></dd>
</dl></div>]]></description>
    <link><![CDATA[http://afpy.ro/dates/2013/2013_04_24.html]]></link>
    <pubDate>2013-04-24 00:00:00</pubDate>
  </item>
  <item>
    <title><![CDATA[[afpyro] AFPyro à Paris - le 19 avril 2013]]></title>
    <description><![CDATA[<div class="section" id="afpyro-a-paris-le-19-avril-2013">
 
<p>Le premier Afpyro du printemps aura lieu le vendredi 19 avril, à partir de
19h30, à JMSI / HackSpark, 64 Rue Alexandre Dumas, Paris.</p>
<p>Nous pourrons prendre des bières au bar à côté, et profiter des locaux de JMSI
pour discuter.</p>
<dl class="docutils"><dt>Pour se rendre à JMDS / HackSpark :</dt>
<dd><ul class="first last simple"><li>en métro : arrêt Alexandre Dumas</li>
<li>en bus : bus 76, arrêt Alexandre Dumas</li>
</ul></dd>
</dl></div>]]></description>
    <link><![CDATA[http://afpy.ro/dates/2013/2013_04_19.html]]></link>
    <pubDate>2013-04-19 00:00:00</pubDate>
  </item>
  <item>
    <title><![CDATA[[Biologeek] Passage à l'échelle]]></title>
    <description><![CDATA[<p>Je m'interroge de plus en plus sur cette notion de passage à l'échelle que l'on nous encourage à anticiper avec le Cloud. J'ai de plus en plus l'impression qu'elle est liée à des <em>business models</em> déficients qui misent sur la masse d'utilisateurs pour avoir une chance à terme de monétiser le service. Je comprends que l'on puisse avoir des investissements à rentabiliser et qu'en visant haut, malléable, on pense pouvoir retrouver un équilibre financier plus rapidement. <em>Pourquoi placer des paliers financiers aussi élevés ?</em></p>
<p>Avoir l'ambition d'un service mondial avec des millions d'utilisateurs est finalement aller à l'encontre du Web en centralisant des données et des usages. Il y a de la place pour plusieurs services, pour de la diversité, pour des motivations et des valeurs différentes, pour des communautés complémentaires. <em>Pourquoi vouloir devenir le TF1 du Web ?</em></p>
<p>Quelles sont les relations que vous pouvez entretenir avec des millions d'usagers ? Sont-elles sincères ? Automatisées ? Avez-vous délégué ces relations ? La reconnaissance de l'utilité d'un service passe par ces retours, c'est une forme de motivation qui s'inscrit dans la durée. On ne vend pas un service au plus offrant lorsque l'on a établi ces relations. <em>Pourquoi chercher à ne plus être en mesure de gérer ces liens ?</em></p>
<p><strong>Le passage à l'échelle est emblématique d'une croissance effrénée et malsaine.</strong></p>]]></description>
    <link><![CDATA[https://larlet.fr/david/blog/2013/passage-echelle/]]></link>
    <pubDate>2013-04-18 23:00:00</pubDate>
  </item>
  <item>
    <title><![CDATA[[logilab] Pylint 10th years anniversary from June 17  to 19 in Toulouse]]></title>
    <description><![CDATA[<p>After a quick survey, we're officially scheduling Pylint 10th years anniversary sprint from monday, June 17 to wednesday, June 19 in <a class="reference" href="http://www.logilab.fr/contact">Logilab's Toulouse</a> office.</p>
<p>There is still some room available if more people want to come, drop me a note (sylvain dot thenault at logilab dot fr).</p>]]></description>
    <link><![CDATA[http://feedproxy.google.com/~r/logilaborg/~3/Tuw3w2VP7TQ/133321]]></link>
    <pubDate>2013-04-18 15:37:00</pubDate>
  </item>
  <item>
    <title><![CDATA[[logilab] Pylint development moving to BitBucket]]></title>
    <description><![CDATA[<p>Hi everyone,</p>
<p>After 10 years of hosting Pylint on our own forge at logilab.org, we've decided to publish version 1.0 and move Pylint and astng development to <a class="reference" href="https://bitbucket.org/logilab/pylint">BitBucket</a>. There has been repository mirrors there for some time, but we intend now to use all BitBucket features, notably Pull Request, to handle various development tasks.</p>
<p>There are several reasons behind this. First, using both BitBucket and our own forge is rather cumbersome, for integrators at least. This is mainly because BitBucket doesn't provide support for Mercurial's <a class="reference" href="http://mercurial.selenic.com/wiki/ChangesetEvolution">changeset evolution</a> feature while our forge relies on it. Second, our forge has several usability drawbacks that make it hard to use for newcomers, and we lack the time to be responsive on this. Finally, we think that our quality-control process, as exposed by our forge, is a bit heavy for such community projects and may keep potential contributors away.</p>
<p>All in all, we hope this will help to have a wider contributor audience as well as more regular maintainers / integrators which are not Logilab employees. And so, bring the best Pylint possible to the Python community!</p>
<p>Logilab.org web pages will be updated to mention this, but kept as there is still valuable information there (eg tickets). We may also keep automatic tests and package building services there.</p>
<p>So, please use <a class="reference" href="https://bitbucket.org/logilab/pylint">https://bitbucket.org/logilab/pylint</a> as main web site regarding pylint development. Bug reports, feature requests as well as contributions should be done there. The same move will be done for Pylint's underlying library, logilab-astng (<a class="reference" href="https://bitbucket.org/logilab/astng">https://bitbucket.org/logilab/astng</a>). We also wish in this process to move it out of the 'logilab' python package. It may be a good time to give it another name, if you have any idea don't hesitate to express yourself.</p>
<p>Last but not least, remember that <a class="reference" href="http://www.pylint.org">Pylint home page</a> may be edited using <a class="reference" href="https://bitbucket.org/logilab/pylint.org">Mercurial</a>, and that the new <a class="reference" href="http://docs.pylint.org">http://docs.pylint.org</a> is generated using the content found in <a class="reference" href="https://bitbucket.org/logilab/pylint/src">Pylint source</a> <cite>doc</cite> subdirectory.</p>
<p>Pylint turning 10 and moving out of its parents is probably a good time to thank Logilab for paying me and some colleagues to create and maintain this project!</p>
<img alt="https://bitbucket-assetroot.s3.amazonaws.com/c/photos/2013/Apr/05/pylint-logo-1661676867-0_avatar.png" src="https://bitbucket-assetroot.s3.amazonaws.com/c/photos/2013/Apr/05/pylint-logo-1661676867-0_avatar.png" />]]></description>
    <link><![CDATA[http://feedproxy.google.com/~r/logilaborg/~3/msVIm341AE8/129458]]></link>
    <pubDate>2013-04-16 18:31:00</pubDate>
  </item>
  <item>
    <title><![CDATA[[novapost] Django1.5 : passer au Configurable User Model]]></title>
    <description><![CDATA[<p>Depuis la version 1.5 de Django, il est possible d'utiliser un <a class="reference external" href="https://docs.djangoproject.com/en/1.5/topics/auth/customizing/#auth-custom-user">Configurable
User Model</a> en lieu et place de <tt class="docutils literal">django.contrib.auth.User</tt>.</p>
<p>Cela permet, par exemple, de se passer de <em>proxy model</em> ou encore de fusionner
le profil avec l'utilisateur, pour éviter des <em>join</em> dans les requêtes SQL.</p>
<p>Très pratique, et facile à mettre en place sur un projet qui commence juste,
mais comment gérer ça en utilisant <a class="reference external" href="http://south.aeracode.org/">South</a> sur un projet déjà bien en place&nbsp;?</p>
<p>Le but est donc de fusionner l'utilisateur et le profil, avec pour
aide/contrainte d'utiliser South, autant sur des plateformes existantes
(serveur de production, de pré-production) que sur les plateformes de
développement&nbsp;: donc les migrations doivent fonctionner sur une création de
base, tout autant que sur une migration simple.</p>
<p>Nous allons détailler plusieurs stratégies.</p>
<div class="section" id="contexte">
<h2>Contexte</h2>
<p>Notre projet utilise depuis longtemps un <em>proxy model</em> sur l'utilisateur, ne
rajoutant que quelques méthodes. Toutes les données liées à l'utilisateur sont
par ailleurs stockées dans un profil, qui est utilisé par le biais de
<tt class="docutils literal">get_profile()</tt> (et le <em>setting</em> <tt class="docutils literal">AUTH_PROFILE_MODULE</tt>).</p>
<div class="highlight"><pre><span class="kn">from</span> <span class="nn">django.db</span> <span class="kn">import</span> <span class="n">models</span>
<span class="kn">from</span> <span class="nn">django.contrib.auth.models</span> <span class="kn">import</span> <span class="n">User</span>


<span class="k">class</span> <span class="nc">RH2User</span><span class="p">(</span><span class="n">User</span><span class="p">):</span>

    <span class="k">class</span> <span class="nc">Meta</span><span class="p">:</span>
        <span class="n">proxy</span> <span class="o">=</span> <span class="bp">True</span>

    <span class="o">...</span>


<span class="k">class</span> <span class="nc">RH2UserProfile</span><span class="p">(</span><span class="n">models</span><span class="o">.</span><span class="n">Model</span><span class="p">):</span>
    <span class="n">some_field</span> <span class="o">=</span> <span class="n">models</span><span class="o">.</span><span class="n">CharField</span><span class="p">(</span><span class="n">max_length</span><span class="o">=</span><span class="mi">50</span><span class="p">)</span>
    <span class="n">some_other_field</span> <span class="o">=</span> <span class="n">models</span><span class="o">.</span><span class="n">BooleanField</span><span class="p">()</span>

    <span class="k">def</span> <span class="nf">some_method</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
        <span class="o">...</span>
</pre></div>
<p>Le résultat, une fois le profil fusionné avec l'utilisateur&nbsp;:</p>
<div class="highlight"><pre><span class="kn">from</span> <span class="nn">django.db</span> <span class="kn">import</span> <span class="n">models</span>
<span class="kn">from</span> <span class="nn">django.contrib.auth.models</span> <span class="kn">import</span> <span class="n">AbstractUser</span>


<span class="k">class</span> <span class="nc">RH2User</span><span class="p">(</span><span class="n">AbstractUser</span><span class="p">):</span>
    <span class="n">some_field</span> <span class="o">=</span> <span class="n">models</span><span class="o">.</span><span class="n">CharField</span><span class="p">(</span><span class="n">max_length</span><span class="o">=</span><span class="mi">50</span><span class="p">)</span>
    <span class="n">some_other_field</span> <span class="o">=</span> <span class="n">models</span><span class="o">.</span><span class="n">BooleanField</span><span class="p">()</span>

    <span class="k">def</span> <span class="nf">some_method</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
        <span class="o">...</span>
</pre></div>
<p>Ne pas oublier de fusionner les <em>managers</em>, les méthodes <tt class="docutils literal">save()</tt>, et de
dédoublonner les champs ayant le même nom (dans notre cas,
<tt class="docutils literal">RH2UserProfile.last_login</tt> a été renommé en <tt class="docutils literal">RH2User.previous_last_login</tt>,
étant donné que le modèle <tt class="docutils literal">auth.User</tt> d'origine avait déjà un champ
<tt class="docutils literal">last_login</tt>).</p>
<p>Il faut par ailleurs rechercher et remplacer le cas échéant toutes les
occurrences de&nbsp;:</p>
<ul class="simple">
<li><tt class="docutils literal">RH2UserProfile</tt></li>
<li><tt class="docutils literal">get_profile()</tt></li>
<li><tt class="docutils literal">rh2userprofile__</tt></li>
<li><tt class="docutils literal">.user</tt></li>
</ul>
</div>
<div class="section" id="la-problematique">
<h2>La problématique</h2>
<p>À partir du moment où le paramètre <tt class="docutils literal">AUTH_USER_MODEL</tt> est renseigné&nbsp;:</p>
<ul class="simple">
<li>les tables <tt class="docutils literal">auth_user</tt>, <tt class="docutils literal">auth_user_user_permissions</tt>,
<tt class="docutils literal">auth_user_groups</tt> ne sont plus automatiquement crées par un <tt class="docutils literal">python
manage.py syncdb</tt></li>
<li>toutes les migrations South existantes sur des modèles ayant une <em>ForeignKey</em>
ou <em>Many to Many</em> ne passerons plus tel quel</li>
</ul>
<p>Il y a donc principalement deux stratégies pour les migrations South, une fois
qu'on a notre modèle <tt class="docutils literal">RH2User</tt> complet (et non plus <em>proxy</em>) ainsi que
<tt class="docutils literal">AUTH_USER_MODEL = 'account.RH2User'</tt> dans les paramètres&nbsp;:</p>
<ul class="simple">
<li>Modifier la migration initiale de l'app <em>account</em>, puis toutes les
migrations suivantes ainsi que les migrations des app ayant une relation
avec l'utilisateur pour qu'elles se basent sur <tt class="docutils literal">account_rh2user</tt> au lieu
de <tt class="docutils literal">auth_user</tt></li>
<li>Rajouter la création de la table <tt class="docutils literal">auth_user</tt>,
<tt class="docutils literal">auth_user_user_permissions</tt> et <tt class="docutils literal">auth_user_groups</tt> dans la migration
initiale de l'app contenant le modèle complet, puis rajouter une migration
qui va renommer la table <tt class="docutils literal">auth_user</tt> en <tt class="docutils literal">account_rh2user</tt></li>
</ul>
<p>Dans les deux cas, il faudra être attentif à l'ordre d'exécution des
migrations&nbsp;: toutes les applications ayant une relation avec l'utilisateur
devront dépendre de la migration initiale qui crée la table <tt class="docutils literal">auth_user</tt> ou
<tt class="docutils literal">account_rh2user</tt>.</p>
<p>Dans le deuxième cas, il faudra de plus que la première des migration suivant
le renommage, pour chaque application, dépende de cette migration.</p>
</div>
<div class="section" id="creation-de-account-rh2user-et-modification-des-migrations">
<h2>Création de account_rh2user et modification des migrations</h2>
<p>Le plus simple est de créer une migration de schéma pour avoir le code
nécessaire à la migration <tt class="docutils literal">0001_initial</tt> de l'application account&nbsp;:</p>
<pre class="literal-block">
$ python manage.py schemamigration account
</pre>
<p>Il suffit alors de recopier le code de la migration créée, de le rajouter au
fichier <tt class="docutils literal">account/migrations/0001_initial.py</tt>, puis de supprimer cette
nouvelle migration qui ne sera pas utilisée.</p>
<p>Il faut ensuite modifier chacune des migration, en prenant exemple sur ce qui a
été fait sur <a class="reference external" href="https://github.com/caffeinehit/django-oauth2-provider/pull/18/files">django-oauth2-provider</a>.</p>
<p>Il reste la problématique de la migration des serveurs déjà en production (qui
ont déjà un certain nombre de migrations effectuées, et une base de donnée à
conserver). Une solution serait de créer une migration de données et de tester
l'existence de la table <tt class="docutils literal">auth_user</tt>, et le cas échéant de dupliquer les
données dans la table <tt class="docutils literal">account_rh2user</tt>.</p>
<p>N'ayant pas testé cette solution, je ne peux la garantir.</p>
</div>
<div class="section" id="creation-de-auth-user-puis-renommage">
<h2>Création de auth_user puis renommage</h2>
<p>C'est la solution que nous avons choisi, étant donné le nombre de migrations
que nous avons (près d'une centaine), qu'il aurait fallu modifier une à une,
ainsi que le soucis de migration des serveurs déjà en production.</p>
<p>Il faut dans l'ordre&nbsp;:</p>
<ul class="simple">
<li>créer les tables <tt class="docutils literal">auth_user</tt>, <tt class="docutils literal">auth_user_user_permissions</tt> et
<tt class="docutils literal">auth_user_groups</tt> dans la migration <tt class="docutils literal">0001_initial</tt> de account</li>
<li>créer une migration dans account qui renomme la table <tt class="docutils literal">auth_user</tt> en
<tt class="docutils literal">account_rh2user</tt></li>
<li>créer une migration dans account qui rajoute les champs du modèle profil à
l'utilisateur</li>
<li>créer une migration de données pour dupliquer toutes les données de profil
dans la table <tt class="docutils literal">account_rh2user</tt></li>
<li>pour chaque application ayant une relation vers l'utilisateur, la prochaine
migration créée devra dépendre de la migration qui renomme la table</li>
</ul>
</div>
<div class="section" id="conclusion">
<h2>Conclusion</h2>
<p>Le plus compliqué dans toute cette histoire est la gestion de dépendances entre
les migrations.</p>
<p>Une autre solution non évoquée aurait été de repartir de 0 pour les
migrations&nbsp;: supprimer toutes les migrations existantes, ainsi que la table
<tt class="docutils literal">south_migrationhistory</tt>, puis reconvertir toutes les applications à South&nbsp;:</p>
<pre class="literal-block">
$ python manage.py convert_to_south ....
</pre>
<p>L'avantage est qu'il n'y a alors aucun soucis de dépendances entre les
migrations, et qu'on repars de quelque chose de propre.</p>
<p>Les inconvénients sont multiples&nbsp;: gérer une migration (à la main?) pour les
plateformes en cours d'utilisation, impossibilité de retourner en arrière
automatiquement, perte de l'historique...</p>
<p>Il y a une autre possibilité (à tester&nbsp;!) qui consiste à spécifier l'attribut
<tt class="docutils literal">db_table = 'auth_user'</tt> dans la <em>Meta</em> de notre nouveau modèle <tt class="docutils literal">RH2User</tt>,
pour qu'il utilise exactement la même table. En théorie, il n'y a alors pas
besoin de migration, mais il reste à gérer la fusion du profil dans
l'utilisateur.</p>
</div>]]></description>
    <link><![CDATA[http://tech.novapost.fr/django15-passer-au-configurable-user-model.html]]></link>
    <pubDate>2013-04-16 12:13:00</pubDate>
    <category>python</category>
    <category>django</category>
    <category>south</category>
    <category>django-fr</category>
  </item>
  <item>
    <title><![CDATA[[ascendances] Cohabitation Jinja et AngularJS]]></title>
    <description><![CDATA[Jinja2, un moteur de templates en Python, et AngularJS, un framework web côté client en Javascript, utilisent tous les deux les accolades pour indiquer une variable ou une structure à interpréter. Par exemple {{ choucroute }} pour une variable qui a du goût. D&#8217;où conflit. Pour résoudre ce problème, trois possibilités : Diviser les fichiers pour régner [&#8230;]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=ascendances.wordpress.com&blog=24442983&post=1085&subd=ascendances&ref=&feed=1" width="1" height="1" />]]></description>
    <link><![CDATA[http://ascendances.wordpress.com/2013/04/16/cohabitation-jinja-et-angularjs/]]></link>
    <pubDate>2013-04-15 22:38:23</pubDate>
    <category>Python</category>
    <category>okiwi</category>
    <category>yaal</category>
  </item>
  <item>
    <title><![CDATA[[tshirtman] Kivy hackathon in paris]]></title>
    <description><![CDATA[<p>Last week saw the <a href="http://www.cifacom.com/ecole-web/actualites/l-ecole-du-web-evenement-python">first 24h hackathon 
event</a>
   entirely dedicated to <a href="http://kivy.org">Kivy</a> happening, in Paris. It was initiated by
   a hightly motivated student of the Cifacom school in paris, David Scheck, and
   attended by 4 groups of students, each with 3 to 5 members. I was pleased to
   share the hard task to help students discover the technology with my colleague
   Julien Miotte, from <a href="http://majerti.fr">Majerti</a> during the event.
</p>
<p>The student weren’t very familiar with <a href="http://python.org">Python</a>, and totally new to
   kivy, so it was a really challenging situation for both the student and the
   framework, would they be able to achieve anything in the hard limit of 24
   hours?
</p>
<p>First, after the students brainstormed on their project ideas, we got them
   through the major concepts and important classes of Kivy, using the <a href="http://kivy.org/docs/gettingstarted/">Getting
started</a> page, it’s hard to get the point
   of <a href="http://kivy.org/docs/gettingstarted/properties.html">properties</a> accross in
   10 minutes to student that didn’t have experienced the need of them, but I
   tried to at least make them aware it would be useful. The <a href="http://kivy.org/docs/gettingstarted/rules.html">kv</a>
   language presentation at this point, even if rudimentary, was probably useful,
   as we will see later.
</p>
<p>Then students went on to try to build their project, except for a few quickly
   solved issues with windows installation, the starting was smooth. Julien and I
   gave some more explanations about how to start building a basic App (the doc is
   <a href="http://kivy.org/docs/gettingstarted/first_app.html">there</a>, but who reads the
   doc?  ;)) and people started designing their interface, and build the core of
   their application.
</p>
<p><img src="http://pub.tshirtman.fr/hackathon_kivy/eatsmart.jpg" alt="Eat Smart" />
</p>
<p>One of the most required feature was certainly the use of multiple screens in
   an application, to build menu, so the use of
   <a href="http://kivy.org/docs/api-kivy.uix.screenmanager.html">ScreenManager</a> was
   explained to at least two groups.  Most of the end results consisted of
   interfaces done mainly in Kv, and some internal logic to display data, most
   group had a quite ambitious target, using geolocalisation, databases, complex
   interactions, and I wasn’t very optimistic on the odds of seeing them
   completed.  Although I was right in that, I was still happy of the good designs
   they came up with, applications were incomplete (to a notable exception, will
   come back to that soon), but some interfaces were beautiful, reactive and
   engaging, so it was nice to see the magic happen in such a short time.
</p>
<p><img src="http://pub.tshirtman.fr/hackathon_kivy/floor2be.jpg" alt="Floor2be" />
</p>
<p>One group had a very different objective of the others, and although they had a
   difficult start, it turns out they were probably the best at teamwork, and had
   chosen a target they could actually achieve, a game! These motivated
   coders/artists came up with a classic 2D sidescroller, when the main character
   has to avoid elements on her way. Simple, to the point, and a quite a good
   realisation in such a short time, the fact that they created original artworks
   and integrated them in their game, and that the general theme had a nice touch
   of humour in the current political events in France certainly hearned them
   points, on top of having a completed project. They even could test it on
   Android during the competition time, and it was running perfectly. Their hard
   work earned them the right to run in a bigger international competition in the
   near future the angel hack in paris, next month, congratulation! I can only
   hope they’ll chose kivy as their secret weapon in this competion too.
</p>
<p><img src="http://pub.tshirtman.fr/hackathon_kivy/runboutin.jpg" alt="The BoutinRun team at work" />
</p>
<p>I would love to have better pictures, but i didn’t think much of taking them during
   the event, I should have better ones soon.
</p>]]></description>
    <link><![CDATA[http://blog.tshirtman.fr/2013/4/15/kivy-hackathon-in-paris]]></link>
    <pubDate>2013-04-15 22:04:00</pubDate>
  </item>
  <item>
    <title><![CDATA[[Biologeek] Être geek]]></title>
    <description><![CDATA[<p>Si j'en crois <a href="http://n.survol.fr/n/la-paralysie-du-tout-parfait">les récents</a> et <a href="http://hanblog.info/blog/post/2012/01/La-paralysie-du-tout-parfait">moins récents</a> <a href="http://standblog.org/blog/post/2013/04/11/La-paralysie-de-la-perfection">billets</a>, le <em>geek</em> serait un paralytique. Doublé d'un perfectionniste à la limite de l'autisme. <em>Dur.</em></p>
<p>Difficile pour moi car c'est justement ce que je considère être la définition du <em>geek</em> : non pas celle de la paralysie mais celle de la curiosité (maladive parfois, soit). Pour moi le terme « geek » n'est plus du tout connoté informatique et encore moins lié à une culture, <strong>c'est le fait de pouvoir se renseigner dans un domaine particulier de façon efficace et rationnelle</strong>.</p>
<p>Cette faculté se développe souvent au service de la consommation car cela permet de croiser facilement des données tangibles mais pas uniquement. Il y a des <em>geeks</em> dans tous les domaines qui vont être à la recherche de la rareté, de la qualité, de l'esthétique dans leurs professions et/ou dans leurs loisirs. Qui vont se réaliser dans l'apprentissage de nouveaux métiers qui ne leur serviront parfois qu'une fois dans leur vie mais ils auront au moins eu le sentiment d'avoir essayé de bien le faire (de façon insatisfaisante d'après eux mais c'est un autre débat).</p>
<p>Revenons à l'informatique, connaissez-vous un autre métier qui génère autant de diversité ? On pourrait y voir un manque de maturité du domaine tout jeune, j'y vois plutôt cette approche <em>geek</em> poussée à l'extrême : creuser tout ce qui peut l'être, faire pousser l'arbre des possibles autant que cela est faisable, rejoindre un horizon qui en ouvre tant d'autres. <strong>La chance de l'informatique ce n'est pas d'être un secteur encore à défricher mais un catalyseur à personnes curieuses.</strong> Et c'est la raison pour laquelle j'aime mon métier, en mouvement :-).</p>]]></description>
    <link><![CDATA[https://larlet.fr/david/blog/2013/etre-geek/]]></link>
    <pubDate>2013-04-14 23:00:00</pubDate>
  </item>
  <item>
    <title><![CDATA[[tarek] Declaring dependencies in Python]]></title>
    <description><![CDATA[<p>In Python Packaging, when you are giving for your project
a list of dependencies, the right approach is to do whatever
works for the <em>majority</em> of your users because there's a
plethora of techniques used by people out there
to deploy Python software.</p>
<p>That's the case for 2 main reasons:</p>
<ol class="arabic simple">
<li>Most Python projects can be deployed in different operating
systems - and unless you're doing a specific packaging work
for each one of them, you are doomed to provide a good enough
generic package.</li>
<li>There are different installers with different approaches
and your Python projects should try to be compatible with
all of them.</li>
</ol>
<p>Some of users <em>will</em> have issues with your projects, you have
to accept this fact and just make sure you provide enough
documentation and hints for them to work around those issues.</p>
<p>This blog entry tries to summarize my current knowledge on what's
the best way to defining dependencies - I hope I'll have some feedback so
I can update it with better techniques. Also, note that I have not
applied this to all my projects. I should.</p>
<p>So if you disagree on my approach please comment !</p>
<div class="note">
<p class="first admonition-title">Note</p>
<p class="last">I am not talking about Virtualenv on purpose here, to avoid
extra complexity.</p>
</div>
<div class="section" id="nature-of-your-project">
<h2>Nature of your project</h2>
<p>The first thing to think about is the nature of your project. They are two
kind of projects in the Python world that can be installed by users:</p>
<ol class="arabic simple">
<li>library &amp; tools that will be used in conjunction with other
Python projects.</li>
<li>End-user applications that are using a plethora of other
Python projects themselves.</li>
</ol>
<p>The first category is what we create most of the time: utility modules,
extensions for some frameworks, library to connect to a database, etc.</p>
<p>The second is a bit specific. It can be a framework, a website or a
desktop application - Most of the time it's driving the whole Python
environment it's running in and dictates what should be installed.</p>
</div>
<div class="section" id="library-tools">
<h2>Library &amp; tools</h2>
<p>For library &amp; tools, my advice is to do the following:</p>
<ol class="arabic simple">
<li>provide a setup.py file that uses distribute/setuptools <em>install_requires</em>
option, and when appliable provide a pure distutils fallback.</li>
<li><strong>do not pin any dependencies</strong> in your setup.py - it turns out it's
making people's life a pain when they want to tweak the versions of the
libraries themselves in tools like zc.buildout</li>
<li>provide a pip <a class="reference external" href="http://www.pip-installer.org/en/latest/cookbook.html#requirements-files">requirements</a>
file where everything is pinned. That's your dependencies documentation.
It says what versions of each dependencies your project depends on.
When possible, add indirect dependencies as well in it.</li>
<li>In your installation instructions, explain that using the pip
requirements file is the recommended way -- I usually even provide
a Makefile that does it - but that running <em>pip install</em> directly
should work fine.</li>
<li>In your continuous integration tool - <em>you are using one, right? ;)</em>
use <a class="reference external" href="http://tox.readthedocs.org/en/latest/">tox</a> to run your tests in all
Python versions you are supporting, and also by deploying your code
with pinned dependencies <em>and</em> unpinned dependencies.</li>
</ol>
<p>Here's a setup.py example:</p>
<div class="highlight"><pre><span class="k">try</span><span class="p">:</span>
    <span class="kn">from</span> <span class="nn">setuptools</span> <span class="kn">import</span> <span class="n">setup</span>

    <span class="n">install_requires</span> <span class="o">=</span> <span class="p">[</span><span class="s">'gevent'</span><span class="p">,</span> <span class="s">'requests'</span><span class="p">]</span>

    <span class="k">try</span><span class="p">:</span>
        <span class="kn">import</span> <span class="nn">argparse</span>
    <span class="k">except</span> <span class="ne">ImportError</span><span class="p">:</span>
        <span class="n">install_requires</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="s">'argparse'</span><span class="p">)</span>

    <span class="n">kws</span> <span class="o">=</span> <span class="p">{</span><span class="s">'install_requires'</span><span class="p">:</span> <span class="n">install_requires</span><span class="p">}</span>
<span class="k">except</span> <span class="ne">ImportError</span><span class="p">:</span>
    <span class="kn">from</span> <span class="nn">distutils.core</span> <span class="kn">import</span> <span class="n">setup</span>
    <span class="n">kws</span> <span class="o">=</span> <span class="p">{}</span>


<span class="n">setup</span><span class="p">(</span><span class="n">name</span><span class="o">=</span><span class="s">'yourproject'</span><span class="p">,</span> <span class="n">version</span><span class="o">=</span><span class="s">'1.1'</span><span class="p">,</span> <span class="n">etc</span><span class="o">..</span><span class="p">,</span> <span class="o">**</span><span class="n">kws</span><span class="p">)</span>
</pre></div>
<p>And the Pip requirements file for Python 2.6</p>
<pre class="literal-block">
gevent==0.13.8
requests==1.2.0
argparse==1.2.1
</pre>
</div>
<div class="section" id="end-user-application">
<h2>End-user application</h2>
<p>Not maintaining one myself, I have no clue what's the best way to do this
but I suspect you really want to maintain a list of projects versions that
are working with a given version of your project.</p>
<p>I recall Zope has this pretty neat thing called the <strong>Known Good Set</strong> (KGS)
where they maintain a list of versions that are known to work well together:
<a class="reference external" href="https://pypi.python.org/pypi/zope.kgs">https://pypi.python.org/pypi/zope.kgs</a></p>
<p>In any case, deploying a whole Python stack in real life cannot be done with a simple
<em>pip install PROJECT</em> call, unless it's a small thing. So maintaining a pip requirements
file sounds like a good approach here.</p>
<p>So all-in-all I guess every advice I gave in the first section can be applied for
end-user applications as well - as long as you make it clear that running
<em>pip install PROJECT</em> won't be enough.</p>
</div>]]></description>
    <link><![CDATA[http://blog.ziade.org/2013/04/13/declaring-dependencies-in-python/]]></link>
    <pubDate>2013-04-13 07:15:00</pubDate>
    <category>python</category>
    <category>mozilla</category>
  </item>
  <item>
    <title><![CDATA[[sciunto] Mat : supprimer les métadonnées de fichiers]]></title>
    <description><![CDATA[mat est un logiciel libre (GPLv2) utilisable en console et en interface graphique. Il permet de supprimer les métadonnées présents dans des fichiers (images, documents, multimédia). Ces métadonnées peuvent contenir des noms d&#8217;auteur, des numéros de série d&#8217;appareil ou encore des données de localisation. Je suis le mainteneur du logiciel pour archlinux. Mat est écrit [&#8230;]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=sciunto.wordpress.com&blog=10984286&post=864&subd=sciunto&ref=&feed=1" width="1" height="1" />]]></description>
    <link><![CDATA[http://sciunto.wordpress.com/2013/04/08/mat-supprimer-les-metadonnees-de-fichiers/]]></link>
    <pubDate>2013-04-08 20:09:57</pubDate>
    <category>Archlinux</category>
    <category>Logiciel</category>
    <category>Planet-april</category>
    <category>Planet-libre</category>
    <category>Python</category>
    <category>hachoir</category>
    <category>mat</category>
    <category>métadonnée</category>
  </item>
  <item>
    <title><![CDATA[[Biologeek] Manuel vs. jardin]]></title>
    <description><![CDATA[<p><a href="http://www.gawel.org/">Gawel</a> me faisait remarquer que mes réflexions sur ce site laissaient un goût d'inachevé. Cela m'a fait pas mal réfléchir après 3 mois à publier un jour sur deux et je pense avoir trouvé la raison de cette insatisfaction. J'écrivais auparavant surtout des articles techniques qui expliquent une bonne pratique ou une façon de faire quelque chose de précis à la manière d'un <em>manuel</em>. Ce n'est plus le cas, <strong>je sème aujourd'hui des graines de réflexions pour qu'elles puissent germer chez d'autres</strong>. Je ne cherche plus à apporter des réponses toutes faites, je souhaite qu'elles se développent indépendamment du semeur. À vous de faire fleurir votre jardin !</p>
<p><em>PS : j'accepte aussi les graines étrangères dans mon jardin.</em></p>]]></description>
    <link><![CDATA[https://larlet.fr/david/blog/2013/manuel-jardin/]]></link>
    <pubDate>2013-03-31 23:00:00</pubDate>
  </item>
  <item>
    <title><![CDATA[[afpy.org] Le nouveau site de l'AFPy]]></title>
    <description><![CDATA[Le week-end du 30-31 Mars 2013, nous avons terminé et mis en prod le nouveau site de l'AFPy...]]></description>
    <link><![CDATA[http://www.afpy.org/news/le-nouveau-site-de-lafpy]]></link>
    <pubDate>2013-03-31 18:50:00</pubDate>
  </item>
  <item>
    <title><![CDATA[[Biologeek] Propriéterre]]></title>
    <description><![CDATA[<blockquote>
<p>Le premier qui, ayant enclos un terrain, s'avisa de dire « Ceci est à moi », et trouva des gens assez simples pour le croire, fut le vrai fondateur de la société civile. Que de crimes, de guerres, de meurtres, que de misères et d'horreurs n'eût point épargnés au genre humain celui qui, arrachant les pieux ou comblant le fossé, eût crié à ses semblables : « Gardez-vous d'écouter cet imposteur ; vous êtes perdus, si vous oubliez que les fruits sont à tous, et que la terre n'est à personne. »</p>
<p><cite>Jean-Jacques Rousseau, <em>L'origine de l'inégalité parmi les hommes (2ème Discours seconde partie)</em></cite></p>
</blockquote>
<p><a href="https://case.oncle-tom.net/">Thomas</a> me demandait hier mon avis sur la propriété (vs. location) et je dois dire que mon avis n'a pas changé au cours de ces dernières années : <strong>j'ai beaucoup de mal à concevoir que l'on puisse être propriétaire d'un morceau de planète.</strong> Je ne peux cautionner <a href="https://fr.wikipedia.org/wiki/Solutions_locales_pour_un_d%C3%A9sordre_global">les dérives que cela a engendré</a>.</p>
<p>Ce qui m'importe par contre c'est le suivi <em>volontaire</em> de mon adresse postale, de mon numéro de téléphone ou de mon nom de domaine. Les affaires peuvent changer de lieux, les amis de répertoire, les données de serveurs mais <em>le plus important est de pouvoir y accéder de manière pérenne</em>. Quel que soit le nombre de redirections nécessaires. Malheureusement, si cela est facile et encouragé sur le web, il est beaucoup plus difficile de le mettre en pratique pour un numéro de téléphone ou une adresse physique, les redirections étant temporaires (et coûteuses) a fortiori lorsque l'on change de pays !</p>
<p>La propriété de la terre est souvent mise en avant comme un <strong>héritage</strong> — on se dédouane de cet acte en proposant ses enfants comme alibi, excuse culpabilisante classique. Or il se trouve que je suis également contre le mécanisme d'héritage ;-). L'autre argument courant est « <strong>si les choses tournent mal</strong> » et là je suis assez circonspect. Si ça tourne vraiment mal, il vaudra mieux être prêt à survivre de manière itinérante qu'à avoir un lieu fixe à défendre. S'il s'agit juste de la peur de la vieillesse et de la rupture du lien social avec sa famille et ses enfants il y a d'autres questions à se poser en amont pour éviter cette situation.</p>
<p>J'aime la liberté et la légèreté que me procure la location, l'idée d'être « de passage » dans cet endroit et d'en apprécier pleinement chaque instant, l'idée de <a href="https://larlet.fr/david/thoughts/#dequiring">se limiter dans ses appartenances</a> pour pouvoir être mobile. De plus, <strong>la propriété d'un bien m'est beaucoup plus stressante que son éphémérité.</strong></p>]]></description>
    <link><![CDATA[https://larlet.fr/david/blog/2013/proprieterre/]]></link>
    <pubDate>2013-03-30 23:00:00</pubDate>
  </item>
  <item>
    <title><![CDATA[[blog.afpy.org] Comment écrire un blog post ?]]></title>
    <description><![CDATA[<p>Tous les membres de l'AFPy peuvent proposer un article si ils le
souhaitent. La procédure est simple:</p>
<ul>
<li><p class="first">récupérez les sources du blog:</p>
<pre class="literal-block">
hg clone http://hg.afpy.org/blog
</pre>
</li>
<li><p class="first">vérifiez que le blog se compile bien correctement: après avoir installer
pelican en local, tapez:</p>
<pre class="literal-block">
make install
</pre>
</li>
<li><p class="first">pushez votre code sur le hg de l'afpy:</p>
<pre class="literal-block">
hg add votre_fichier.py
hg commit -m &quot;votre message&quot; -u username
hg push
</pre>
</li>
<li><p class="first">et voilà !</p>
</li>
</ul>
<p>Si vous souhaitez uniquement proposer un article, vous pouvez utiliser la
fonctionalité de &quot;draft&quot; de pelican, il suffit de mettre &quot;:status: draft&quot;
comme métadonnée pour que l'article ne soit pas publié.</p>
<p>Tout cela est rappelé dans le fichier README du dépôt !</p>]]></description>
    <link><![CDATA[http://blog.afpy.org/comment-ecrire-un-blog-post.html]]></link>
    <pubDate>2013-03-29 23:00:00</pubDate>
  </item>
  <item>
    <title><![CDATA[[logilab] PyLint 10th years anniversary, 1.0 sprint]]></title>
    <description><![CDATA[<p>In a few week, pylint will be 10 years old (0.1 released on may 19 2003!).
At this occasion, I would like to release a 1.0. Well, not exactly at that date,
but not too long after would be great. Also, I think it would be a good time
to have a few days sprint to work a bit on this 1.0 but also to meet all together
and talk about pylint status and future, as more and more contributions come from
outside Logilab (actually mostly Google, which employs Torsten and Martin, the most
active contributors recently).</p>
<p>The first thing to do is to decide a date and place. Having discussed a bit with
Torsten about that, it seems reasonable to target a  sprint during june or july.
Due to personal constraints, I would like to host this sprint in Logilab's
Toulouse office.</p>
<p>So, who would like to jump in and sprint to make pylint even better? I've created
a doodle so every one interested may tell his preferences:
<a class="reference" href="http://doodle.com/4uhk26zryis5x7as">http://doodle.com/4uhk26zryis5x7as</a></p>
<p>Regarding the location, is everybody ok with Toulouse? Other ideas are Paris, or
Florence around EuroPython, or... &lt;add your proposition here&gt;.</p>
<p>We'll talk about the sprint topics later, but there are plenty of exciting ideas
around there.</p>
<p>Please, answer quickly so we can move on. And I hope to see you all there!</p>]]></description>
    <link><![CDATA[http://feedproxy.google.com/~r/logilaborg/~3/4gcsddtWeZ4/124658]]></link>
    <pubDate>2013-03-29 17:20:00</pubDate>
  </item>
  <item>
    <title><![CDATA[[QuiSaura] Python et les boucles imbriquées via itertools.product]]></title>
    <description><![CDATA[Je ne connaissais pas non plus.]]></description>
    <link><![CDATA[http://www.qui-saura.fr/monBlog/python-et-les-boucles-imbriquees-via-itertools-product.html]]></link>
    <pubDate>2013-03-29 00:27:55</pubDate>
    <category>Python</category>
  </item>
  <item>
    <title><![CDATA[[Biologeek] Fondée sur des valeurs]]></title>
    <description><![CDATA[<p>J'étais à Devoxx hier soir comme <a href="https://larlet.fr/david/blog/2013/conferences-diversite/">annoncé</a> pour présenter <a href="http://www.devoxx.com/display/FR13/BOF+Not+Only+SSII">une approche différente de la SSII</a> et je devais décrire l'expérience <a href="http://scopyleft.fr">scopyleft</a> :</p>
<blockquote>
<p>scopyleft est une SCOP (Société Coopérative et Participative) toute jeune — seulement 3 mois — ce qui ne permet pas d'avoir le recul nécessaire pour valider ou invalider une approche. Ni même pour juger de sa viabilité. C'est pourquoi j'ai choisi de vous parler de sa genèse : autrement dit, de <em>l'avant scopyleft</em>.</p>
<p>Notre approche a été de <strong>ne pas</strong> commencer par le traditionnel <em>business plan</em> mais de nous aligner entre nous 4 sur les <strong>valeurs fondatrices</strong> que nous voulions comme cap au cours de la vie de l'entreprise. Si l'approche économique nous donnait une vision pour environ 1 an avec son lot d'incertitudes et autres pivotages, passer par des valeurs nous amenait à considérer une durée beaucoup plus longue… de l'ordre de la décennie. Ambitieux projet.</p>
<p>Cela commence par mieux se connaître, discuter de valeurs permet d'aller beaucoup plus en profondeur qu'une discussion sur le retour sur investissement, les salaires ou le titre que l'on souhaite avoir dans l'entreprise. Une fois d'accord sur le fond — <strong>honnêteté intellectuelle, courage, bien-être, respect et partage</strong> —, nous sommes arrivés à formuler une phrase (à défaut d'un manifeste) résumant notre objectif commun :</p>
<blockquote>
<p><strong>Travailler entre humains, sur des projets éthiques et intéressants, tout en privilégiant le bien-être et le plaisir de chacun.</strong></p>
</blockquote>
<p><em>Ces valeurs et cette maxime nous guident dans nos choix stratégiques au quotidien pour accepter ou non un client, pour concrétiser une initiative ou pour accompagner un projet.</em></p>
<p>C'est notamment ce qui nous a menés à choisir le statut de <strong>SCOP</strong>,  un statut basé sur une <em>gouvernance démocratique</em> (1 homme = 1 voix) et favorisant la <em>pérennité</em> des emplois et du projet d'entreprise (co-entrepreunariat et réserve importante imposés). <strong>Ce statut met l'humain au cœur de l'entreprise, ce qui diffère d'une entreprise traditionnelle qui se concentre sur son capital.</strong> C'est un changement de paradigme majeur dans une société capitaliste. Nous avons enrichi ces statuts d'une co-gérance tournante (faute de pouvoir gérer à 4) et d'une égalité salariale.</p>
<p>Un autre aspect de la SCOP qui a attisé notre curiosité est la notion de <strong>solidarité inter-entreprises</strong> au sein de la confédération des SCOP. C'est notamment ce que l'on a pu constater avec les entreprises qui nous ressemblent, il existe relativement peu de SCOP (dans l'informatique) mais elles se serrent les coudes !</p>
<p>Ces 3 derniers mois nous ont permis de reconsidérer notre approche économique, de mieux nous connaître, d'avoir énormément de retours (à la fois de nos clients et de nos pairs), de coder <em>ensemble</em>, d'accompagner <em>ensemble</em>, d'assister à des conférences <em>ensemble</em>, de stresser <em>ensemble</em>, de faire de l'administratif <em>ensemble</em>, autant de tâches qui sont loin d'être insurmontables et qui sont le lot quotidien du créateur d'entreprise mais qui s'avèrent être moins pénibles lorsque l'on poursuit <strong>un objectif un peu plus « élevé »</strong> (sain ?) que le simple aspect financier. Augmenté par le fait de le réaliser à plusieurs.</p>
<p>Cette aventure aurait difficilement pu être envisageable sans avoir confiance dans notre <em>savoir-faire</em> acquis lors de nos expériences respectives à nos comptes. Malgré notre expérience dans le domaine, l'un de nos objectifs à terme est de nous libérer de la prestation pour <strong>co-produire des produits utiles, éthiques et open-source</strong>.</p>
<p>Pour résumer, <strong>scopyleft est une entreprise fondée sur des valeurs pour créer de la valeur.</strong> En coopérant.</p>
</blockquote>
<p>Je n'ai pas dû dire la moitié de tout ça au final car l'approche monologue était un peu ennuyante et je préférais avoir plus de temps pour discuter. Et là je n'ai pas été déçu car les réactions ont été nombreuses, un peu décousues et agressives mais cela montrait un intérêt certain.</p>
<p><em>Décousues</em> car on était nombreux à pouvoir répondre (une dizaine) et qu'il y avait beaucoup de questions qui passaient brutalement du fond à la forme selon le niveau de réflexion de chacun. Peut-être qu'un autre format (groupes de discussions par exemple) se prêtait mieux à la discussion ouverte mais la salle n'était pas adaptée.</p>
<p><em>Agressives</em> principalement car le titre <strong>NoSSII</strong> a été interprété comme un affrontement alors que l'on avait bien mis en avant le côté <em>Not only</em>. Dommage, l'idée n'était pas du tout d'aller dans ce sens mais il est peut-être normal que certains se sentent déstabilisés lorsqu'on présente quelque chose de différent.</p>
<p>Quelques questions dont je me souviens :</p>
<blockquote>
<p>À quoi cela sert-il de créer une SCOP vs. un GIE (Groupement d'intérêt économique) d'indépendants ?</p>
</blockquote>
<p>L'objectif n'est pas du tout le même, principalement car <strong>on ne se regroupe pas en SCOP pour un intérêt économique</strong> mais pour partager et échanger à un autre niveau.</p>
<blockquote>
<p>Qu'est-ce qui vous différencie d'une SSII classique finalement ?</p>
</blockquote>
<p>D'une part le fait d'avoir le contrôle sur les objectifs de la société, ils peuvent être lucratifs ou pas, ils peuvent être citoyens ou pas, ils peuvent être éthiques ou pas, ils peuvent s'émanciper de la prestation ou pas. D'autre part, le fait de <strong>mettre l'humain au cœur du cadre de travail</strong> est un changement radical, ce qui change aussi les relations avec les clients.</p>
<blockquote>
<p>Comment gérez-vous les problèmes d'éthiques ?</p>
</blockquote>
<p><strong>Nous n'avons pas de règle pré-définie</strong>, chaque cas aux limites est discuté longuement et un vote suivant <a href="http://www.oss-watch.ac.uk/resources/meritocraticGovernanceVoting#voting">les pratiques de l'Open-Source</a> (et de <a href="https://docs.djangoproject.com/en/dev/internals/contributing/bugs-and-features/#how-we-make-decisions">Django</a>) permet de trancher.</p>
<blockquote>
<p>Est-ce que ça peut fonctionner à plus grande échelle ?</p>
</blockquote>
<p>L'exemple <a href="http://www.valvesoftware.com/company/Valve_Handbook_LowRes.pdf">du handbook de Valve</a> a été mis en avant avec quelques autres. La question c'est plutôt de savoir quel est l'intérêt de passer à une autre échelle ? Dans notre cas par exemple, <strong>c'est une volonté forte de rester à taille humaine</strong>.</p>
<p>Ça rejoint d'ailleurs une question relative au référencement dans les services achats des grosses entreprises. C'est peut-être plus difficile en étant petit mais en fait ça nous intéresse peu de travailler avec ce type de structures donc ça limite le problème. Beaucoup de questions n'allaient pas assez loin dans le <em>pourquoi</em> et se limitaient au <em>comment</em>.</p>
<blockquote>
<p>Comment trouvez-vous des clients ?</p>
</blockquote>
<p>On ne sait pas démarcher. Partant de ce constat, ça passe plutôt par <strong>de la recommandation ou des connaissances</strong> qui nous suivent depuis longtemps et le partage de nos expériences. On a également la chance d'avoir des entreprises partageant nos valeurs qui nous transmettent des demandes.</p>
<p>Les <a href="http://www.freelanceinformatique.com/2013/03/29/devoxxfr-nossii-reaction-a-chaud/">réactions à chaud</a> sont plutôt positives, j'espère que l'on aura réussi avec <a href="http://ninja-squad.com/">Ninja Squad</a> et <a href="http://www.lateral-thoughts.com/">Lateral Thoughts</a> à au moins attiser la curiosité de certains et pourquoi pas à en motiver pour monter leur propre structure !</p>
<p><strong>[Mise à jour]</strong> : <a href="http://www.pingtimeout.fr/2013/04/retour-sur-devoxx-france-bof-nossii.html">Retour sur Devoxx France - BOF NoSSII</a></p>]]></description>
    <link><![CDATA[https://larlet.fr/david/blog/2013/scopyleft-valeurs/]]></link>
    <pubDate>2013-03-28 23:00:00</pubDate>
  </item>
  <item>
    <title><![CDATA[[tarek] News from the Quality front]]></title>
    <description><![CDATA[<div class="section" id="flake8-plugins">
<h2>Flake8 plugins</h2>
<p>Some time ago, we've unified our efforts to build &amp; maintain code quality
checkers. For instance, Flake8 is now entirely based on plugins: there's a
pep8, pyflakes &amp; mccabe plugin and you can add more.</p>
<p>The family of Flake8 plugin is growing &amp; Florent has started listing them
at <a class="reference external" href="http://flake8.readthedocs.org/en/latest/extensions.html#existing-extensions">http://flake8.readthedocs.org/en/latest/extensions.html#existing-extensions</a></p>
</div>
<div class="section" id="mailing-list">
<h2>Mailing list</h2>
<p>Another good news is the creation of a mailing list dedicated to all
the code quality tools : <a class="reference external" href="http://mail.python.org/mailman/listinfo/code-quality">http://mail.python.org/mailman/listinfo/code-quality</a></p>
<p>If you're interested in those tools, or you are maintaing one yourself,
consider joining this mailing list.</p>
</div>]]></description>
    <link><![CDATA[http://blog.ziade.org/2013/03/28/news-from-the-quality-front/]]></link>
    <pubDate>2013-03-28 06:50:00</pubDate>
    <category>python</category>
    <category>mozilla</category>
  </item>
  <item>
    <title><![CDATA[[afpyro] AFPyro à Lyon - le 27 mars 2013]]></title>
    <description><![CDATA[<div class="section" id="afpyro-a-lyon-le-27-mars-2013">
 
<p>Le premier Afpyro du printemps aura lieu le mercredi 27 mars à partir de 20h à l’<a class="reference external" href="http://www.lantreautre.fr/">Antre Autre</a> - <a class="reference external" href="http://www.openstreetmap.org/?mlat=45.769148&mlon=4.831513&zoom=18&layers=M">11 rue Terme - 69001 Lyon</a>.</p>
<p>Simon Sapin nous fera une présentation sur <a class="reference external" href="http://sphinx-doc.org/">Sphinx</a>. Sphinx est un outil qui permet de créer de la documentation pour vos projets ou vos produits.</p>
<p>L’<a class="reference external" href="http://www.lantreautre.fr/">Antre Autre</a> est un lieu où nous pouvons discuter autour d’un verre, et, pour ceux qui le souhaitent, prendre un repas.</p>
<dl class="docutils"><dt>Pour se rendre à l’<a class="reference external" href="http://www.lantreautre.fr/">Antre Autre</a> :</dt>
<dd><ul class="first last simple"><li>en métro : arrêt Hôtel de Ville</li>
<li>en bus : lignes C13 et C18 arrêt Mairie du 1er ou lignes 19, C14 et C3 à l’arrêt Terreaux</li>
<li>en vélo’v : stations Place Sathonay, Carmélites Burdeau, Place de la paix</li>
</ul></dd>
</dl></div>]]></description>
    <link><![CDATA[http://afpy.ro/dates/2013/2013_03_27.html]]></link>
    <pubDate>2013-03-27 00:00:00</pubDate>
  </item>
  <item>
    <title><![CDATA[[noirbizarre] Huit recettes pour Pelican]]></title>
    <description><![CDATA[<p>Après avoir passé beaucoup de temps sur mon blog depuis le passage à <a class="reference external" href="http://getpelican.com/">Pelican</a>,
j&#8217;ai décidé de publier quelques recettes que&nbsp;j&#8217;utilise.</p>
<p>Libre à vous de les utiliser et de les améliorer.
Je suis évidement intéréssé par votre&nbsp;retour.</p>
<div class="section" id="arborescence-plate">
<h2>Arborescence&nbsp;plate</h2>
<p>Cette recette n&#8217;en est pas vraiment une,
mais elle servira de base pour les chemins des autres&nbsp;recettes.</p>
<p>Je l&#8217;utilise pour avoir l&#8217;arborescence suivante dans mon dépôt&nbsp;git:</p>
<pre class="literal-block">
├── articles
│&nbsp;&nbsp; ├── categorie1
│&nbsp;&nbsp; │&nbsp;&nbsp; └─ article.rst
│&nbsp;&nbsp; └── categorie2
├── extras
│&nbsp;&nbsp; ├─ 404.html
│&nbsp;&nbsp; └─ robots.txt
├── images
│   ├── theme1
│   │&nbsp;&nbsp; └─ image.png
│   ├── theme2
│   │&nbsp;&nbsp; └─ image.png
│&nbsp;&nbsp; └─ image.png
├── local_plugins
│&nbsp;&nbsp; └─ plugin.py
├── pages
│&nbsp;&nbsp; └─ page.rst
├── theme
│   ├── static
│   │&nbsp;&nbsp; ├── css
│   │&nbsp;&nbsp; └── images
│   └── templates
│       ├─ template1.html
│       └─ template2.html
├─ .gitignore
├─ Makefile
├─ nginx.conf
├─ pelicanconf.py
├─ publish.sh
├─ publishconf.py
└─ requirements.pip
</pre>
<p>Pour cela j&#8217;ai modifié mon fichier <tt class="docutils literal">pelicanconf.py</tt> comme&nbsp;suit:</p>
<div class="highlight"><pre><span class="n"><span class="caps">PATH</span></span> <span class="o">=</span> <span class="n">dirname</span><span class="p">(</span><span class="n">__file__</span><span class="p">)</span>
<span class="n">OUTPUT_PATH</span> <span class="o">=</span> <span class="n">join</span><span class="p">(</span><span class="n"><span class="caps">PATH</span></span><span class="p">,</span> <span class="s">'output'</span><span class="p">)</span>
<span class="n">ARTICLE_DIR</span> <span class="o">=</span> <span class="s">'articles'</span>
<span class="n"><span class="caps">THEME</span></span> <span class="o">=</span> <span class="s">'theme'</span>
<span class="n">STATIC_PATHS</span> <span class="o">=</span> <span class="p">(</span><span class="s">&quot;images&quot;</span><span class="p">,</span> <span class="p">)</span>
<span class="n">FILES_TO_COPY</span> <span class="o">=</span> <span class="p">(</span>
    <span class="p">(</span><span class="s">'extras/robots.txt'</span><span class="p">,</span> <span class="s">'robots.txt'</span><span class="p">),</span>
<span class="p">)</span>
<span class="n"><span class="caps">PLUGINS</span></span> <span class="o">=</span> <span class="p">(</span>
    <span class="s">'pelican.plugins.gzip_cache'</span><span class="p">,</span>
    <span class="s">'pelican.plugins.sitemap'</span><span class="p">,</span>
    <span class="s">'local_plugins.plugin'</span>
<span class="p">)</span>
</pre></div>
</div>
<div class="section" id="delegation-d-authentification-openid">
<h2>Délégation d&#8217;authentification&nbsp;OpenID</h2>
<p>Mon blog Wordpress me servait aussi à m&#8217;authentifier grâce à OpenID.
Cette modification a donc été la première que j&#8217;ai réalisée.
Je l&#8217;ai réalisée avec <a class="reference external" href="http://www.myopenid.com/">MyOpenId</a> comme fournisseur puisque c&#8217;est celui que j&#8217;utilise,
libre à vous de l&#8217;adapter à votre&nbsp;fournisseur.</p>
<ol class="arabic">
<li><p class="first">Créez un template <tt class="docutils literal">theme/templates/myopenid.html</tt>:</p>
<div class="highlight"><pre><span class="cp">{%</span> <span class="k">if</span> <span class="nv">MYOPENID_USERNAME</span> <span class="cp">%}</span><span class="x"></span>
<span class="x">&lt;link rel=&quot;openid.server&quot; href=&quot;http://www.myopenid.com/server&quot; /&gt;</span>
<span class="x">&lt;link rel=&quot;openid.delegate&quot; href=&quot;http://</span><span class="cp">{{</span><span class="nv">MYOPENID_USERNAME</span><span class="cp">}}</span><span class="x">.myopenid.com/&quot; /&gt;</span>
<span class="x">&lt;link rel=&quot;openid2.local_id&quot; href=&quot;http://</span><span class="cp">{{</span><span class="nv">MYOPENID_USERNAME</span><span class="cp">}}</span><span class="x">.myopenid.com&quot; /&gt;</span>
<span class="x">&lt;link rel=&quot;openid2.provider&quot; href=&quot;http://www.myopenid.com/server&quot; /&gt;</span>
<span class="x">&lt;meta http-equiv=&quot;X-<span class="caps">XRDS</span>-Location&quot; content=&quot;http://www.myopenid.com/xrds?username=</span><span class="cp">{{</span><span class="nv">MYOPENID_USERNAME</span><span class="cp">}}</span><span class="x">.myopenid.com&quot; /&gt;</span>
<span class="cp">{%</span> <span class="k">endif</span> <span class="cp">%}</span><span class="x"></span>
</pre></div>
</li>
<li><p class="first">Dans le bloc <tt class="docutils literal">&lt;head&gt;</tt> du template <tt class="docutils literal">theme/templates/base.html</tt> ajoutez:</p>
<div class="highlight"><pre><span class="cp">{%</span> <span class="k">include</span> <span class="s1">'myopenid.html'</span> <span class="cp">%}</span><span class="x"></span>
</pre></div>
</li>
<li><p class="first">Dans le fichier de configuration de publication <tt class="docutils literal">publishconf.py</tt>,&nbsp;ajoutez:</p>
<div class="highlight"><pre><span class="n">MYOPENID_USERNAME</span> <span class="o">=</span> <span class="s">'me'</span>
</pre></div>
</li>
</ol>
</div>
<div class="section" id="boutons-google-1-twitter-et-flattr">
<h2>Boutons Google +1, Twitter et&nbsp;Flattr</h2>
<p>Cette modification de thème permet d&#8217;insérer des boutons de partage chargés de façon&nbsp;asynchrone.</p>
<p>Ces boutons ne seront visibles qu&#8217;en mode publié
puisqu&#8217;ils requierent que <tt class="docutils literal"><span class="caps">SITEURL</span></tt> soit&nbsp;définie.</p>
<ol class="arabic">
<li><p class="first">Créez les templates des&nbsp;boutons</p>
<ul>
<li><p class="first"><tt class="docutils literal">theme/templates/plusone.html</tt>:</p>
<blockquote>
<div class="highlight"><pre><span class="cp">{%</span> <span class="k">if</span> <span class="nv">PLUS_ONE</span> <span class="k">and</span> <span class="nv">share_url</span> <span class="cp">%}</span><span class="x"></span>
<span class="x">&lt;span class=&quot;g-plusone&quot; data-href=&quot;</span><span class="cp">{{</span> <span class="nv">share_url</span> <span class="cp">}}</span><span class="x">&quot; data-size=&quot;medium&quot;&gt;&lt;/span&gt;</span>
<span class="cp">{%</span> <span class="k">endif</span> <span class="cp">%}</span><span class="x"></span>
</pre></div>
</blockquote>
</li>
<li><p class="first"><tt class="docutils literal">theme/templates/twitter.html</tt>:</p>
<blockquote>
<div class="highlight"><pre><span class="cp">{%</span> <span class="k">if</span> <span class="nv">TWITTER_USERNAME</span> <span class="k">and</span> <span class="nv">share_url</span> <span class="k">and</span> <span class="nv">share_title</span> <span class="cp">%}</span><span class="x"></span>
<span class="x">&lt;a href=&quot;http://twitter.com/share&quot; class=&quot;twitter-share-button&quot; data-count=&quot;horizontal&quot; data-via=&quot;</span><span class="cp">{{</span><span class="nv">TWITTER_USERNAME</span><span class="cp">}}</span><span class="x">&quot; data-related=&quot;</span><span class="cp">{{</span><span class="nv">TWITTER_USERNAME</span><span class="cp">}}</span><span class="x">&quot; data-url=&quot;</span><span class="cp">{{</span><span class="nv">share_url</span><span class="cp">}}</span><span class="x">&quot; data-text=&quot;</span><span class="cp">{{</span><span class="nv">share_title</span><span class="o">|</span><span class="nf">striptags</span><span class="cp">}}</span><span class="x">&quot;&gt;Tweet&lt;/a&gt;</span>
<span class="cp">{%</span> <span class="k">endif</span> <span class="cp">%}</span><span class="x"></span>
</pre></div>
</blockquote>
</li>
<li><p class="first"><tt class="docutils literal">theme/templates/flattr.html</tt>:</p>
<blockquote>
<div class="highlight"><pre><span class="cp">{%</span> <span class="k">if</span> <span class="nv">FLATTR_USERNAME</span> <span class="k">and</span> <span class="nv">share_url</span> <span class="k">and</span> <span class="nv">share_title</span> <span class="cp">%}</span><span class="x"></span>
<span class="x">&lt;a class=&quot;FlattrButton&quot; style=&quot;display:none;&quot;</span>
<span class="x">    title=&quot;</span><span class="cp">{{</span><span class="nv">share_title</span><span class="cp">}}</span><span class="x">&quot; href=&quot;</span><span class="cp">{{</span><span class="nv">share_url</span><span class="cp">}}</span><span class="x">&quot;</span>
<span class="x">    data-flattr-uid=&quot;</span><span class="cp">{{</span><span class="nv">FLATTR_USERNAME</span><span class="cp">}}</span><span class="x">&quot;</span>
<span class="x">    </span><span class="cp">{%</span> <span class="k">if</span> <span class="nv">article</span> <span class="k">and</span> <span class="nv">article.tags</span> <span class="cp">%}</span><span class="x">data-flattr-tags=&quot;</span><span class="cp">{{</span><span class="nv">article.tags</span><span class="o">|</span><span class="nf">join</span><span class="o">(</span><span class="s1">','</span><span class="o">)</span><span class="cp">}}</span><span class="x">&quot;</span><span class="cp">{%</span> <span class="k">endif</span> <span class="cp">%}</span><span class="x"></span>
<span class="x">    </span><span class="cp">{%</span> <span class="k">if</span> <span class="nv">FLATTR_LANG</span>  <span class="cp">%}</span><span class="x">data-flattr-language=&quot;</span><span class="cp">{{</span><span class="nv">FLATTR_LANG</span><span class="cp">}}</span><span class="x">&quot;</span><span class="cp">{%</span> <span class="k">endif</span> <span class="cp">%}</span><span class="x"></span>
<span class="x">    data-flattr-button=&quot;compact&quot;</span>
<span class="x">    data-flattr-category=&quot;text&quot;&gt;</span>
<span class="x">    </span><span class="cp">{{</span><span class="nv">share_title</span><span class="cp">}}</span><span class="x"></span>
<span class="x">&lt;/a&gt;</span>
<span class="cp">{%</span> <span class="k">endif</span> <span class="cp">%}</span><span class="x"></span>
</pre></div>
</blockquote>
</li>
</ul>
</li>
<li><p class="first">Créez les templates des scripts de chargement&nbsp;asynchrone:</p>
<ul>
<li><p class="first"><tt class="docutils literal">theme/templates/plusone_script.html</tt>:</p>
<blockquote>
<div class="highlight"><pre><span class="cp">{%</span> <span class="k">if</span> <span class="nv">PLUS_ONE</span> <span class="k">and</span> <span class="nv"><span class="caps">SITEURL</span></span> <span class="cp">%}</span><span class="x"></span>
<span class="x">&lt;script type=&quot;text/javascript&quot;&gt;</span>
<span class="x">  window.___gcfg = {lang: '</span><span class="cp">{{</span><span class="nv">PLUS_ONE_LANG</span><span class="cp">}}</span><span class="x">'};</span>
<span class="x">  (function() {</span>
<span class="x">    var po = document.createElement('script'); po.type = 'text/javascript'; po.async = true;</span>
<span class="x">    po.src = 'https://apis.google.com/js/plusone.js';</span>
<span class="x">    var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(po, s);</span>
<span class="x">  })();</span>
<span class="x">&lt;/script&gt;</span>
<span class="cp">{%</span> <span class="k">endif</span> <span class="cp">%}</span><span class="x"></span>
</pre></div>
</blockquote>
</li>
<li><p class="first"><tt class="docutils literal">theme/templates/twitter_script.html</tt>:</p>
<blockquote>
<div class="highlight"><pre><span class="cp">{%</span> <span class="k">if</span> <span class="nv">TWITTER_USERNAME</span> <span class="k">and</span> <span class="nv"><span class="caps">SITEURL</span></span> <span class="cp">%}</span><span class="x"></span>
<span class="x">&lt;script&gt;!function(d,s,id){var js,fjs=d.getElementsByTagName(s)[0];if(!d.getElementById(id)){js=d.createElement(s);js.id=id;js.src=&quot;https://platform.twitter.com/widgets.js&quot;;fjs.parentNode.insertBefore(js,fjs);}}(document,&quot;script&quot;,&quot;twitter-wjs&quot;);&lt;/script&gt;</span>
<span class="cp">{%</span> <span class="k">endif</span> <span class="cp">%}</span><span class="x"></span>
</pre></div>
</blockquote>
</li>
<li><p class="first"><tt class="docutils literal">theme/templates/flattr_script.html</tt>:</p>
<blockquote>
<div class="highlight"><pre><span class="cp">{%</span> <span class="k">if</span> <span class="nv">FLATTR_USERNAME</span> <span class="k">and</span> <span class="nv"><span class="caps">SITEURL</span></span> <span class="cp">%}</span><span class="x"></span>
<span class="x">&lt;script type=&quot;text/javascript&quot;&gt;</span>
<span class="x">(function() {</span>
<span class="x">    var s = document.createElement('script');</span>
<span class="x">    var t = document.getElementsByTagName('script')[0];</span>
<span class="x">    s.type = 'text/javascript';</span>
<span class="x">    s.async = true;</span>
<span class="x">    s.src = '//api.flattr.com/js/0.6/load.js?mode=auto';</span>
<span class="x">    t.parentNode.insertBefore(s, t);</span>
<span class="x">})();</span>
<span class="x">&lt;/script&gt;</span>
<span class="cp">{%</span> <span class="k">endif</span> <span class="cp">%}</span><span class="x"></span>
</pre></div>
</blockquote>
</li>
</ul>
</li>
<li><p class="first">A chaque emplacement où vous désirez voir apparaitre ces boutons, insérez le bloc&nbsp;suivant:</p>
<div class="highlight"><pre><span class="cp">{%</span> <span class="k">if</span> <span class="nv"><span class="caps">SITEURL</span></span> <span class="cp">%}</span><span class="x"></span>
<span class="x">    </span><span class="cp">{%</span>- <span class="k">set</span> <span class="nv">share_url</span> <span class="o">=</span> <span class="o">[</span><span class="nv"><span class="caps">SITEURL</span></span><span class="o">,</span> <span class="nv">article.url</span><span class="o">]|</span><span class="nf">join</span><span class="o">(</span><span class="s1">'/'</span><span class="o">)</span> -<span class="cp">%}</span><span class="x"></span>
<span class="x">    </span><span class="cp">{%</span>- <span class="k">set</span> <span class="nv">share_title</span> <span class="o">=</span> <span class="nv">article.title</span> -<span class="cp">%}</span><span class="x"></span>
<span class="x">    </span><span class="cp">{%</span> <span class="k">include</span> <span class="s1">'twitter.html'</span> <span class="cp">%}</span><span class="x"></span>
<span class="x">    </span><span class="cp">{%</span> <span class="k">include</span> <span class="s1">'plusone.html'</span> <span class="cp">%}</span><span class="x"></span>
<span class="x">    </span><span class="cp">{%</span> <span class="k">include</span> <span class="s1">'flattr.html'</span> <span class="cp">%}</span><span class="x"></span>
<span class="cp">{%</span> <span class="k">endif</span> <span class="cp">%}</span><span class="x"></span>
</pre></div>
<p>Modifiez biensur les variable <tt class="docutils literal">share_url</tt> et <tt class="docutils literal">share_title</tt> en fonction du&nbsp;contexte.</p>
</li>
<li><p class="first">Modifiez votre template <tt class="docutils literal">theme/templates/base.html</tt> pour y insérer à la fin du bloc <tt class="docutils literal">&lt;body&gt;</tt>:</p>
<div class="highlight"><pre><span class="cp">{%</span> <span class="k">if</span> <span class="nv"><span class="caps">SITEURL</span></span> <span class="cp">%}</span><span class="x"></span>
<span class="x">    </span><span class="cp">{%</span> <span class="k">include</span> <span class="s1">'twitter_script.html'</span> <span class="cp">%}</span><span class="x"></span>
<span class="x">    </span><span class="cp">{%</span> <span class="k">include</span> <span class="s1">'plusone_script.html'</span> <span class="cp">%}</span><span class="x"></span>
<span class="x">    </span><span class="cp">{%</span> <span class="k">include</span> <span class="s1">'flattr_script.html'</span> <span class="cp">%}</span><span class="x"></span>
<span class="cp">{%</span> <span class="k">endif</span> <span class="cp">%}</span><span class="x"></span>
</pre></div>
</li>
<li><p class="first">Renseignez vos identifiant et paramètres dans votre configuration de publication <tt class="docutils literal">pelicanconf.py</tt>:</p>
<div class="highlight"><pre><span class="n">TWITTER_USERNAME</span> <span class="o">=</span> <span class="s">'me'</span>

<span class="n">FLATTR_USERNAME</span> <span class="o">=</span> <span class="s">&quot;me&quot;</span>
<span class="n">FLATTR_LANG</span> <span class="o">=</span> <span class="s">&quot;fr_FR&quot;</span>

<span class="n">PLUS_ONE</span> <span class="o">=</span> <span class="bp">True</span>
<span class="n">PLUS_ONE_LANG</span> <span class="o">=</span> <span class="s">'fr'</span>
</pre></div>
</li>
</ol>
<p>Pour adapter les paramètres à vos besoins, consultez les documentations&nbsp;officielles:</p>
<ul class="simple">
<li><a class="reference external" href="https://dev.twitter.com/docs/tweet-button">Documentation officielle du bouton&nbsp;Twitter</a></li>
<li><a class="reference external" href="https://developers.google.com/+/plugins/+1button/">Documentation officielle du bouton Google&nbsp;+1</a></li>
<li><a class="reference external" href="http://developers.flattr.net/button/">Documentation officielle du bouton&nbsp;Flattr</a></li>
</ul>
</div>
<div class="section" id="filtre-urlencode-pour-jinja">
<h2>Filtre &#8220;urlencode&#8221; pour&nbsp;Jinja</h2>
<p>Jinja 2 ne propose plus de filtre <tt class="docutils literal">urlencode</tt> par défaut
(<a class="reference external" href="https://github.com/mitsuhiko/jinja2/issues/17">Issue #17</a>
et <a class="reference external" href="https://github.com/mitsuhiko/jinja2/pull/62">Pull Request #62</a>).</p>
<p>Si pour une raison ou une autre vous en avez besoin,
vous pouvez le rajouter vous même dans votre fichier <tt class="docutils literal">pelicanconf.py</tt>:</p>
<div class="highlight"><pre><span class="kn">import</span> <span class="nn">urllib</span>
<span class="kn">from</span> <span class="nn">jinja2.utils</span> <span class="kn">import</span> <span class="n">Markup</span>

<span class="k">def</span> <span class="nf">urlencode_filter</span><span class="p">(</span><span class="n">s</span><span class="p">):</span>
    <span class="k">if</span> <span class="nb">type</span><span class="p">(</span><span class="n">s</span><span class="p">)</span> <span class="o">==</span> <span class="s">'Markup'</span><span class="p">:</span>
        <span class="n">s</span> <span class="o">=</span> <span class="n">s</span><span class="o">.</span><span class="n">unescape</span><span class="p">()</span>
    <span class="n">s</span> <span class="o">=</span> <span class="n">s</span><span class="o">.</span><span class="n">encode</span><span class="p">(</span><span class="s">'utf8'</span><span class="p">)</span>
    <span class="n">s</span> <span class="o">=</span> <span class="n">urllib</span><span class="o">.</span><span class="n">quote_plus</span><span class="p">(</span><span class="n">s</span><span class="p">)</span>
    <span class="k">return</span> <span class="n">Markup</span><span class="p">(</span><span class="n">s</span><span class="p">)</span>

<span class="n">JINJA_FILTERS</span> <span class="o">=</span> <span class="p">{</span>
    <span class="s">'urlencode'</span><span class="p">:</span> <span class="n">urlencode_filter</span><span class="p">,</span>
<span class="p">}</span>
</pre></div>
</div>
<div class="section" id="marquer-la-page-active-dans-le-menu">
<h2>Marquer la page active dans le&nbsp;menu</h2>
<p>Si vous desirez marquer la page active dans le menu,
vous pouvez vous appuyer sur l&#8217;astuce <a class="reference external" href="http://jinja.pocoo.org/docs/tricks/#highlighting-active-menu-items">Highlighting Active Menu Items</a>.</p>
<p>Voici ma recette adaptée à&nbsp;Pelican:</p>
<ol class="arabic">
<li><p class="first">Modifier votre template <tt class="docutils literal">theme/templates/base.html</tt> pour y ajouter le menu
et y déclarer la variable du context <tt class="docutils literal">active_page</tt>:</p>
<div class="highlight"><pre><span class="cp">{%</span>- <span class="k">set</span> <span class="nv">active_page</span> <span class="o">=</span> <span class="nv">active_page</span><span class="o">|</span><span class="nf">default</span><span class="o">(</span><span class="kp">None</span><span class="o">)</span> -<span class="cp">%}</span><span class="x"></span>
<span class="x">&lt;nav id=&quot;menu&quot;&gt;</span>
<span class="x">    &lt;ul&gt;</span>
<span class="x">        &lt;li </span><span class="cp">{%</span> <span class="k">if</span> <span class="nv">active_page</span> <span class="o">==</span> <span class="s1">'index'</span> <span class="cp">%}</span><span class="x">class=&quot;active&quot;</span><span class="cp">{%</span> <span class="k">endif</span> <span class="cp">%}</span><span class="x">&gt;</span>
<span class="x">            &lt;a href=&quot;</span><span class="cp">{{</span> <span class="nv"><span class="caps">SITEURL</span></span> <span class="cp">}}</span><span class="x">/&quot;&gt;Accueil&lt;/a&gt;</span>
<span class="x">        &lt;/li&gt;</span>
<span class="x">        &lt;li </span><span class="cp">{%</span> <span class="k">if</span> <span class="nv">active_page</span> <span class="o">==</span> <span class="s1">'category'</span> <span class="cp">%}</span><span class="x">class=&quot;active&quot;</span><span class="cp">{%</span> <span class="k">endif</span> <span class="cp">%}</span><span class="x">&gt;&lt;a href=&quot;</span><span class="cp">{{</span> <span class="nv"><span class="caps">SITEURL</span></span> <span class="cp">}}</span><span class="x">/categories&quot;&gt;Catégories&lt;/a&gt;</span>
<span class="x">            &lt;ul&gt;</span>
<span class="x">                </span><span class="cp">{%</span> <span class="k">for</span> <span class="nv">cat</span><span class="o">,</span> <span class="nv">null</span> <span class="k">in</span> <span class="nv">categories</span> <span class="cp">%}</span><span class="x"></span>
<span class="x">                &lt;li&gt;&lt;a href=&quot;</span><span class="cp">{{</span> <span class="nv"><span class="caps">SITEURL</span></span> <span class="cp">}}</span><span class="x">/</span><span class="cp">{{</span> <span class="nv">cat.url</span> <span class="cp">}}</span><span class="x">&quot;&gt;</span><span class="cp">{{</span> <span class="nv">cat</span> <span class="cp">}}</span><span class="x">&lt;/a&gt;&lt;/li&gt;</span>
<span class="x">                </span><span class="cp">{%</span> <span class="k">endfor</span> <span class="cp">%}</span><span class="x"></span>
<span class="x">            &lt;/ul&gt;</span>
<span class="x">        &lt;/li&gt;</span>
<span class="x">        </span><span class="cp">{%</span> <span class="k">if</span> <span class="nv">DISPLAY_PAGES_ON_MENU</span> <span class="cp">%}</span><span class="x"></span>
<span class="x">        </span><span class="cp">{%</span> <span class="k">for</span> <span class="nv">pag</span> <span class="k">in</span> <span class="nv"><span class="caps">PAGES</span></span> <span class="cp">%}</span><span class="x"></span>
<span class="x">            &lt;li </span><span class="cp">{%</span> <span class="k">if</span> <span class="nv">page</span> <span class="o">==</span> <span class="nv">pag</span> <span class="cp">%}</span><span class="x">class=&quot;active&quot;</span><span class="cp">{%</span> <span class="k">endif</span> <span class="cp">%}</span><span class="x">&gt;</span>
<span class="x">                &lt;a href=&quot;</span><span class="cp">{{</span> <span class="nv"><span class="caps">SITEURL</span></span> <span class="cp">}}</span><span class="x">/</span><span class="cp">{{</span> <span class="nv">pag.url</span> <span class="cp">}}</span><span class="x">&quot;&gt;</span><span class="cp">{{</span> <span class="nv">pag.title</span> <span class="cp">}}</span><span class="x">&lt;/a&gt;</span>
<span class="x">            &lt;/li&gt;</span>
<span class="x">        </span><span class="cp">{%</span> <span class="k">endfor</span> <span class="cp">%}</span><span class="x"></span>
<span class="x">        </span><span class="cp">{%</span> <span class="k">endif</span> <span class="cp">%}</span><span class="x"></span>
<span class="x">    &lt;/ul&gt;</span>
<span class="x">&lt;/nav&gt;</span>
</pre></div>
</li>
<li><p class="first">Modifiez les pages qui doivent déclarer un nom de page, dans mon&nbsp;cas:</p>
<ul>
<li><p class="first"><tt class="docutils literal">theme/templates/index.html</tt>:</p>
<blockquote>
<div class="highlight"><pre><span class="cp">{%</span> <span class="k">extends</span> <span class="s2">&quot;base.html&quot;</span> <span class="cp">%}</span><span class="x"></span>
<span class="cp">{%</span> <span class="k">set</span> <span class="nv">active_page</span> <span class="o">=</span> <span class="s1">'index'</span> -<span class="cp">%}</span><span class="x"></span>
</pre></div>
</blockquote>
</li>
<li><p class="first"><tt class="docutils literal">theme/templates/categories.html</tt>:</p>
<blockquote>
<div class="highlight"><pre><span class="cp">{%</span> <span class="k">extends</span> <span class="s2">&quot;base.html&quot;</span> <span class="cp">%}</span><span class="x"></span>
<span class="cp">{%</span> <span class="k">set</span> <span class="nv">active_page</span> <span class="o">=</span> <span class="s2">&quot;category&quot;</span> <span class="cp">%}</span><span class="x"></span>
</pre></div>
</blockquote>
</li>
<li><p class="first"><tt class="docutils literal">theme/templates/category.html</tt>:</p>
<blockquote>
<div class="highlight"><pre><span class="cp">{%</span> <span class="k">extends</span> <span class="s2">&quot;base.html&quot;</span> <span class="cp">%}</span><span class="x"></span>
<span class="cp">{%</span> <span class="k">set</span> <span class="nv">active_page</span> <span class="o">=</span> <span class="s1">'category'</span> -<span class="cp">%}</span><span class="x"></span>
</pre></div>
</blockquote>
</li>
</ul>
<p>Adaptez-le suivant votre hierarchie de templates et de&nbsp;menu.</p>
</li>
</ol>
</div>
<div class="section" id="balises-meta">
<h2>Balises&nbsp;<span class="caps">META</span></h2>
<p>Cette recette permet déclarer les balises <tt class="docutils literal">&lt;meta&gt;</tt> dans le fichier de&nbsp;configuration.</p>
<ol class="arabic">
<li><p class="first">Déclarez un dictionnaire <tt class="docutils literal"><span class="caps">META</span></tt> dans votre fichier <tt class="docutils literal">pelicanconf.py</tt>:</p>
<div class="highlight"><pre><span class="kn">from</span> <span class="nn">pelican</span> <span class="kn">import</span> <span class="n">__version__</span> <span class="k">as</span> <span class="n">PELICAN_VERSION</span>
<span class="n"><span class="caps">METAS</span></span> <span class="o">=</span> <span class="p">{</span>
    <span class="s">'author'</span><span class="p">:</span> <span class="s">u'Me'</span><span class="p">,</span>
    <span class="s">'description'</span><span class="p">:</span> <span class="s">u&quot;My blog description&quot;</span><span class="p">,</span>
    <span class="s">'keywords'</span><span class="p">:</span> <span class="s">u'some, keywords, for, seo'</span><span class="p">,</span>
    <span class="s">'generator'</span><span class="p">:</span> <span class="s">u'Pelican </span><span class="si">%s</span><span class="s">'</span> <span class="o">%</span> <span class="n">PELICAN_VERSION</span><span class="p">,</span>
<span class="p">}</span>
</pre></div>
</li>
<li><p class="first">Ajoutez dans le bloc <tt class="docutils literal">&lt;head&gt;</tt> du template <tt class="docutils literal">theme/templates/base.html</tt>:</p>
<div class="highlight"><pre><span class="cp">{%</span> <span class="k">for</span> <span class="nv">name</span><span class="o">,</span> <span class="nv">content</span> <span class="k">in</span> <span class="nv"><span class="caps">METAS</span>.iteritems</span><span class="o">()</span> <span class="cp">%}</span><span class="x"></span>
<span class="x">&lt;meta name=&quot;</span><span class="cp">{{</span><span class="nv">name</span><span class="cp">}}</span><span class="x">&quot; content=&quot;</span><span class="cp">{{</span><span class="nv">content</span><span class="cp">}}</span><span class="x">&quot; /&gt;</span>
<span class="cp">{%</span> <span class="k">endfor</span> <span class="cp">%}</span><span class="x"></span>
</pre></div>
</li>
</ol>
</div>
<div class="section" id="page-d-erreur-404">
<h2>Page d&#8217;erreur&nbsp;404</h2>
<p>Cette recette fourni une page d&#8217;erreur 404 personnalisée avec le thème de votre blog.
Elle fonctionne avec NGinx mais est adaptable à tout autre&nbsp;serveur.</p>
<ol class="arabic">
<li><p class="first">Créez le template de votre page d&#8217;erreur 404, dans mon cas <tt class="docutils literal">extras/404.html</tt>:</p>
<div class="highlight"><pre><span class="cp">{%</span> <span class="k">extends</span> <span class="s2">&quot;base.html&quot;</span> <span class="cp">%}</span><span class="x"></span>
<span class="cp">{%</span> <span class="k">block</span> <span class="nv">content_title</span> <span class="cp">%}</span><span class="x">Erreur 404</span><span class="cp">{%</span> <span class="k">endblock</span> <span class="cp">%}</span><span class="x"></span>
<span class="cp">{%</span> <span class="k">block</span> <span class="nv">content</span> <span class="cp">%}</span><span class="x"></span>
<span class="x">&lt;section class=&quot;body page&quot;&gt;</span>
<span class="x">    &lt;h1 class=&quot;page-title&quot;&gt;Erreur 404&lt;/h1&gt;</span>
<span class="x">    La page que vous cherchez n'existe pas.</span>
<span class="x">&lt;/section&gt;</span>
<span class="cp">{%</span> <span class="k">endblock</span> <span class="nv">content</span> <span class="cp">%}</span><span class="x"></span>
</pre></div>
</li>
<li><p class="first">Déclarez cette page dans votre configuration <tt class="docutils literal">pelicanconf.py</tt>:</p>
<div class="highlight"><pre><span class="n">TEMPLATE_PAGES</span> <span class="o">=</span> <span class="p">{</span><span class="s">'extras/404.html'</span><span class="p">:</span> <span class="s">'404.html'</span><span class="p">}</span>
</pre></div>
</li>
<li><p class="first">Indiquez à NGinx où trouver cette&nbsp;page:</p>
<div class="highlight"><pre><span class="k">server</span> <span class="p">{</span>
    <span class="c1"># ...</span>
    <span class="kn">error_page</span> <span class="mi">404</span> <span class="s">/404.html</span><span class="p">;</span>
    <span class="c1"># ...</span>
<span class="p">}</span>
</pre></div>
</li>
</ol>
</div>
<div class="section" id="publication-par-git-post-receive-hook">
<h2>Publication par git post-receive&nbsp;hook</h2>
<p>Cette recette me permet de déployer mon blog dès que je push sur mon&nbsp;serveur.</p>
<p>A la fin de chaque déploiement, je ping les moteurs de recherche
pour les notifier des modification du fichier <tt class="docutils literal">sitemap.xml</tt>
généré par le <a class="reference external" href="http://docs.getpelican.com/en/3.1.1/plugins.html#sitemap">plugin Sitemap</a>.</p>
<p>La configuration de NGinx et les dépendances Python sont elles aussi stoquées
dans mon dépôt git, respectivement dans les fichiers <tt class="docutils literal">nginx.conf</tt> et <tt class="docutils literal">requirements.pip</tt>.
Dans mon cas, j&#8217;utilise <tt class="docutils literal">virtualenv</tt> mais vous pouvez l&#8217;adapter à votre&nbsp;configuration.</p>
<ol class="arabic">
<li><p class="first">Sur votre serveur, modifiez ou créez le fichier <tt class="docutils literal"><span class="pre">blog.git/hooks/post-receive</span></tt>:</p>
<div class="highlight"><pre><span class="c">#!/bin/sh</span>

<span class="nv"><span class="caps">STAGING</span></span><span class="o">=</span><span class="s2">&quot;/home/me/staging/blog&quot;</span>

<span class="nv">GIT_WORK_TREE</span><span class="o">=</span><span class="nv">$<span class="caps">STAGING</span></span> git checkout -f
<span class="nb">cd</span> <span class="nv">$<span class="caps">STAGING</span></span>
./publish.sh
</pre></div>
<p>Ce fichier doit être&nbsp;exécutable.</p>
</li>
<li><p class="first">Dans votre dépôt git, ajoutez le fichier <tt class="docutils literal">publish.sh</tt> à la&nbsp;racine:</p>
<div class="highlight"><pre><span class="c">#!/bin/sh</span>

<span class="nv"><span class="caps">VENV</span></span><span class="o">=</span><span class="s2">&quot;venv&quot;</span>
<span class="nv"><span class="caps">PUBLIC</span></span><span class="o">=</span><span class="s2">&quot;/path/to/your/public/blog&quot;</span>
<span class="nv"><span class="caps">SITEMAP</span></span><span class="o">=</span>http://your.blog/sitemap.xml.gz

<span class="c"># Setup virtualenv</span>
<span class="k">if</span> <span class="o">[</span> ! -d <span class="s2">&quot;$<span class="caps">VENV</span>&quot;</span> <span class="o">]</span>; <span class="k">then</span>
<span class="k">    </span>virtualenv --distribute <span class="nv">$<span class="caps">VENV</span></span>
<span class="k">fi</span>
. <span class="nv">$<span class="caps">VENV</span></span>/bin/activate
pip install -r requirements.pip --use-mirrors

<span class="c"># Deploy blog</span>
pelican -v -o <span class="nv">$<span class="caps">PUBLIC</span></span> -s publishconf.py
cp -f nginx.conf /etc/nginx/sites-available/your.blog
sudo service nginx reload

<span class="c"># Ping sitemap</span>
<span class="nv"><span class="caps">GOOGLE</span></span><span class="o">=</span>http://www.google.com/webmasters/tools/ping?sitemap<span class="o">=</span><span class="nv">$<span class="caps">SITEMAP</span></span>
<span class="nv"><span class="caps">BING</span></span><span class="o">=</span>http://www.bing.com/webmaster/ping.aspx?siteMap<span class="o">=</span><span class="nv">$<span class="caps">SITEMAP</span></span>

<span class="k">for </span>url in <span class="nv">$<span class="caps">GOOGLE</span></span> <span class="nv">$<span class="caps">BING</span></span>; <span class="k">do</span>
<span class="k">    </span>curl -s -w <span class="s2">&quot;%{http_code} %{url_effective}\\n&quot;</span> <span class="s2">&quot;$url&quot;</span> -o /dev/null
<span class="k">done</span>
</pre></div>
<p>Pour ne pas perdre de temps à chaque push,
je commente la ligne qui installe les dépendances python
et je la décommente uniquement lorsque je les&nbsp;modifie.</p>
</li>
<li><p class="first">Donnez les droits pour recharger NGinx via sudo à votre&nbsp;utilisateur:</p>
<pre class="literal-block">
# Reload nginx command
Cmnd_Alias NGINX_RELOAD = /usr/sbin/service nginx reload

# User privilege specification
me  ALL=NGINX_RELOAD, NOPASSWD: NGINX_RELOAD
</pre>
</li>
</ol>
</div>]]></description>
    <link><![CDATA[http://noirbizarre.info/2013/03/24/huit-recettes-pour-pelican/]]></link>
    <pubDate>2013-03-24 13:30:00</pubDate>
    <category>pelican</category>
    <category>python</category>
    <category>jinja</category>
    <category>nginx</category>
    <category>git</category>
    <category>hook</category>
  </item>
  <item>
    <title><![CDATA[[Biologeek] Éduquer à la joie]]></title>
    <description><![CDATA[<blockquote>
<p>Ce livre est un appel aux parents, aux enseignants, aux maîtres et maîtresses d'école, aux adultes qui ne savent plus rêver, pour qu'ils mettent de côté, le temps de cette lecture, la masse des connaissances intellectuelles, de savoirs et de théories éducatives qu'ils ont appris sur leurs enfants, ce qu'ils croient « être bon » pour eux et pour leur développement. C'est une proposition impertinente de ne pas « faire les devoirs » que l'éducation nous a imposés tout au long de notre vie (réussir, aller vite, être les premiers…), mais de se laisser plutôt embarquer par le vent du changement qui parcourt notre monde aujourd'hui, y compris celui de l'école. Pour en finir avec le mépris systémique de ce qui, avec la pensée, nous distingue des animaux — à savoir notre capacité de rêver —, nous nous devons de redonner à l'éducation son rôle d'éveil. Chacun de nous recèle un don, un talent inné qui, s'il est respecté et honoré, nous transformera en être humain heureux, en contact avec sa joie de vivre, en accord avec soi-même et les autres. L'éducation a le devoir de reconnaître ce trésor, de le révéler et d'aider tout individu à le développer : l'enjeu n'est rien d'autre que l'accomplissement d'un monde en paix.</p>
<p><cite>Antonella Verdiani, <em>Ces écoles qui rendent les enfants heureux</em></cite></p>
</blockquote>
<p>Je suis en train de pas mal me renseigner sur les pédagogies et méthodes d'éducation dites <em>alternatives</em> suite à mes recherches sur l'<a href="https://larlet.fr/david/blog/2012/nef-anthroposophie/">Anthroposophie</a> et sur ce qui distingue une secte d'une approche non traditionnelle (Montessori, Steiner, Freinet, modèle scandinave, éducation démocratique, éducation lente, école à la maison, etc).</p>
<p>Le point commun de ces écoles est qu'elles sont animées par des valeurs et des notions communes avant tout intérêt financier :</p>
<ul>
<li>Reliance</li>
<li>Ouverture au questionnement existentiel</li>
<li>Transdisciplinarité</li>
<li>Complexité</li>
<li>(In)novation</li>
<li>Autorisation</li>
<li>Incertitude</li>
<li>Planter là où c'est fertile</li>
</ul>
<p>Aussi lorsque j'apprends l'ouverture d'<a href="http://www.42.fr/">une nouvelle école</a> dédiée à l'<em>excellence</em> de la France en matière de numérique (avec tout un vocabulaire anglophone mais passons), je ne peux que me réjouir et chercher les valeurs associées à cette méthode d'éducation qui se veut atypique et innovante. Malheureusement, la recherche est vaine. Le seul fil rouge du site est financier :</p>
<ul>
<li>comment vous rendre « rapidement opérationnel » pour l'entreprise ?</li>
<li>comment devenir un « informaticien aux compétences recherchées » ?</li>
<li>comment « coller à des réalités économiques » ?</li>
</ul>
<p>J'ose espérer que mon métier ne se résume pas à une telle approche. Que mes futurs collaborateurs n'auront pas été formés comme des « <a href="http://www.42.fr/faqs">commandos de <em>Marines</em></a> » pour sélectionner les meilleurs d'entre eux. <em>Le rêve de ce regroupement d'entrepreneurs est finalement de créer une armée de mercenaires pour concrétiser leurs idées à bas prix.</em> Et ce sans poser de questions car si ce n'est pas toi, <em>ressource</em>, ce sera ton voisin qui a reçu la même formation industrialisée basée sur la performance.</p>
<p><strong>Né pour coder ? Non. Né pour prendre du plaisir à coder. Nuance.</strong></p>
<p><strong>[Mise à jour]</strong> : Éric D. trouve que <a href="http://n.survol.fr/n/42-pour-une-seule-ecole-ca-fait-41-de-trop">42 pour une seule école ? ça fait 41 de trop</a></p>]]></description>
    <link><![CDATA[https://larlet.fr/david/blog/2013/eduquer-joie/]]></link>
    <pubDate>2013-03-23 23:00:00</pubDate>
  </item>
  <item>
    <title><![CDATA[[Biologeek] Accompagnement EMI]]></title>
    <description><![CDATA[<blockquote>
<p>Accompagner, c'est célébrer, c'est manifester l'incroyable splendeur qu'est chacun(e).</p>
<p><cite>Marie Milis</cite></p>
</blockquote>
<p>J'intervenais cette semaine à l'<a href="http://www.emi-cfd.com/">EMI CFD</a> comme <a href="https://larlet.fr/david/blog/2013/conferences-diversite/">précédemment mentionné</a>. L'occasion de rencontrer une promotion orientée graphisme et rédaction bien éloignée de la sphère des développeurs.</p>
<p>L'objet était de produire une petite application en une semaine qui utilise des données sur la thématique du <em>Festival de Cannes</em>, ce qui s'est avéré être un challenge intéressant autant du point de vue de l'accompagnement en lui-même que de celui du sociologue qui sommeille en moi.</p>
<p>Il y a eu 2 problèmes transverses aux équipes accompagnées sur lesquels je voudrais revenir :</p>
<ul>
<li><em>le manque de démarche expérimentale</em> : lorsque l'on commence à « s'imbiber de données » (sic), il est important de formuler des hypothèses qui doivent être vérifiée <strong>par</strong> les données et non énoncer des points de vues que l'on cherche absolument à prouver <strong>avec</strong> des données. Il ne faut pas avoir peur de produire des esquisses de données — à travers des graphes tout simples — avant de penser en termes d'infographies. Pressés par le temps, nous n'avons pas pu développer la discussion sur <strong>la rigueur scientifique des journalistes</strong> mais cela aurait pu être intéressant, il est trop tentant de vouloir raconter une histoire pour sa simple <a href="https://larlet.fr/david/blog/2012/opinion-audience/">audience</a>.</li>
<li><em>le manque de <a href="https://larlet.fr/david/blog/2013/autoritaire-cooperatif/">coopération</a></em> : il y avait 3 équipes qui sont restées dans leur bulle tout le long de la semaine, quel dommage ! Chaque équipe — même dans le cadre d'une <em>coopétition</em> — aurait gagnée à mutualiser des données ou à se challenger sur les résultats obtenus. Cela aurait encouragé également une démarche itérative pour que ces échanges soient rendus possibles. <em>Note : on nous a récemment qualifiés d'« intégristes de la coopération » avec <a href="http://scopyleft.fr">scopyleft</a> et j'assume pleinement ce rôle.</em></li>
</ul>
<p>En recherche d'efficacité optimale, j'ai introduit la méthode <a href="https://en.wikipedia.org/wiki/MoSCoW_Method">MoSCoW</a> la dernière journée en improvisant un ersatz de <em>backlog</em> priorisé. Le résultat s'est révélé être au-delà de mes espérances en terme de fluidité des cartes au sein de l'équipe pour effectuer les dernières tâches rapidement.</p>
<p>Il était assez frustrant de ne pas avoir le temps d'expliquer la vision que j'ai de mon métier et de transmettre une façon de travailler permettant de garder un rythme soutenable et procurant du plaisir. Frustration également (salutaire cette fois) de se retenir de trop encadrer, certaines expériences devant se faire par soi-même. Cela étant dit, mon seul vrai regret sur la semaine aura été de ne pas avoir pu rester à la fin lors de la rétro accompagnateurs et du pot qui aurait pu permettre de parler d'autre chose que de données :-).</p>
<p>Quelques idées pour améliorer la formation :</p>
<ul>
<li>donner quelques rudiments méthodologiques pour faire fonctionner les groupes, au moins en leur expliquant les différents axes possibles d'organisation (centralisé, démocratique, <a href="https://larlet.fr/david/blog/2012/consentement-sociocratie/">sociocratique</a>, etc), la méthode à <a href="http://www.la-rache.com/">la RACHE</a> ayant montré ses limites ;</li>
<li>lisser la pression, elle ne devrait pas être croissante comme j'ai pu l'observer quel que soit le groupe, l'approche itérative permet d'y arriver efficacement ;</li>
<li>donner davantage de temps pour affiner la vision initiale en leur proposant le thème plus tôt, beaucoup de temps a été perdu par simple manque de réflexion en amont.</li>
</ul>
<blockquote>
<p>L'éducation authentique ne se fait pas de A vers B, ni de A sur B, mais par A avec B, par l'intermédiaire du monde.</p>
<p><cite>Paulo Freire</cite></p>
</blockquote>
<p>J'ai au moins autant appris que les « étudiants » cette semaine en ayant eu la possibilité d'observer des groupes de travail à l'ouvrage et en constatant une fois de plus la difficulté à travailler ensemble même en poursuivant un but commun. <strong>La concrétisation d'un projet ne doit jamais se faire au détriment de la communication dans l'équipe.</strong> Auquel cas on perd non seulement le projet, mais également l'équipe…</p>
<p>Pour finir, je voudrais remercier <a href="http://yohanboniface.me/">Yohan</a> qui m'a permis de participer à cette formation (et de dormir sur son bateau pendant une semaine !).</p>]]></description>
    <link><![CDATA[https://larlet.fr/david/blog/2013/accompagnement-emi/]]></link>
    <pubDate>2013-03-21 23:00:00</pubDate>
  </item>
  <item>
    <title><![CDATA[[novapost] Django : Comment booster ses tests en 1 minute]]></title>
    <description><![CDATA[<div class="section" id="introduction">
<h2>Introduction</h2>
<p>Hier, Boris, en stage ici pour 6 mois, me parle d'une astuce pour
Django ultra simple, mais qui a tout bonnement divisé par deux notre
temps de tests :O</p>
</div>
<div class="section" id="connexion-views-et-tests">
<h2>Connexion, views et tests</h2>
<p>Lors des tests, on doit souvent se connecter pour tester les droits de
l'utilisateur ou encore accéder à des certaines pages de l'application
Django décorée par un <tt class="docutils literal">user_passes_test</tt> ou un <tt class="docutils literal">login_required</tt>.</p>
<p>Si vous n'utilisez pas encore les techniques de tests unitaires des
views proposée la semaine dernière par Benoît Bryon, cette solution
simple va déjà vous faire gagner un temps fou.</p>
</div>
<div class="section" id="a-un-probleme-une-solution">
<h2>À un problème, une solution</h2>
<p>Grâce à <a class="reference external" href="http://igorsobreira.com/2012/09/19/improving-performance-of-django-test-suite.html">l'article de Igor Sobreira</a> <a class="footnote-reference" href="http://tech.novapost.fr/feeds/Python.atom.xml#id1" id="id2">[1]</a>, on s'est rendu compte que le
temps passé dans le hachage des mots de passe était significatif sur
la durée totale des tests.</p>
<p>Nous avons donc essayé la solution proposée par l'article (utiliser un
<cite>UnsaltedMD5PasswordHasher</cite> pour les tests) et avons effectivement
constaté une amélioration du temps total des tests.</p>
<p>En regardant le code de <cite>UnsaltedMD5PasswordHasher</cite> on s'est dit qu'on
pouvait gagner encore plus de temps à la fois sur le hachage du mot de
passe et sur la vérification de celui-ci.</p>
<p>En effet, tous les hasher Django utilisent une méthode de comparaison
des mots de passe en temps constant afin d'éviter <a class="reference external" href="http://codahale.com/a-lesson-in-timing-attacks/">les problèmes de
timing attaques</a> <a class="footnote-reference" href="http://tech.novapost.fr/feeds/Python.atom.xml#id3" id="id4">[2]</a>.</p>
</div>
<div class="section" id="et-en-pratique">
<h2>Et en pratique ?</h2>
<p>Il suffit de définir un <tt class="docutils literal">PASSWORD_HASHER</tt> simplifié pour les tests
pour gagner un temps fou lors de l'exécution des tests.</p>
<p>C'est aussi simple que ça.</p>
<p>La première étape consiste à définir un PASSWORD_HASHER minimaliste</p>
<div class="highlight"><pre><span class="c"># -*- coding: utf-8 -*-</span>
<span class="kn">from</span> <span class="nn">django.contrib.auth.hashers</span> <span class="kn">import</span> <span class="n">BasePasswordHasher</span><span class="p">,</span> <span class="n">mask_hash</span>
<span class="kn">from</span> <span class="nn">django.utils.datastructures</span> <span class="kn">import</span> <span class="n">SortedDict</span>


<span class="k">class</span> <span class="nc">PlainPasswordHasher</span><span class="p">(</span><span class="n">BasePasswordHasher</span><span class="p">):</span>
    <span class="sd">&quot;&quot;&quot;</span>
<span class="sd">    The plain password hashing algorithm for test (DO NOT USE in production)</span>
<span class="sd">    &quot;&quot;&quot;</span>
    <span class="n">algorithm</span> <span class="o">=</span> <span class="s">&quot;plain&quot;</span>

    <span class="k">def</span> <span class="nf">salt</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
        <span class="k">return</span> <span class="s">''</span>

    <span class="k">def</span> <span class="nf">encode</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">password</span><span class="p">,</span> <span class="n">salt</span><span class="p">):</span>
        <span class="k">return</span> <span class="s">'</span><span class="si">%s</span><span class="s">$$</span><span class="si">%s</span><span class="s">'</span> <span class="o">%</span> <span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">algorithm</span><span class="p">,</span> <span class="n">password</span><span class="p">)</span>

    <span class="k">def</span> <span class="nf">verify</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">password</span><span class="p">,</span> <span class="n">encoded</span><span class="p">):</span>
        <span class="n">algorithm</span><span class="p">,</span> <span class="nb">hash</span> <span class="o">=</span> <span class="n">encoded</span><span class="o">.</span><span class="n">split</span><span class="p">(</span><span class="s">'$$'</span><span class="p">,</span> <span class="mi">1</span><span class="p">)</span>
        <span class="k">assert</span> <span class="n">algorithm</span> <span class="o">==</span> <span class="bp">self</span><span class="o">.</span><span class="n">algorithm</span>
        <span class="k">return</span> <span class="n">password</span> <span class="o">==</span> <span class="nb">hash</span>

    <span class="k">def</span> <span class="nf">safe_summary</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">encoded</span><span class="p">):</span>
        <span class="k">return</span> <span class="n">SortedDict</span><span class="p">([</span>
            <span class="p">(</span><span class="s">'algorithm'</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">algorithm</span><span class="p">),</span>
            <span class="p">(</span><span class="s">'hash'</span><span class="p">,</span> <span class="n">mask_hash</span><span class="p">(</span><span class="n">encoded</span><span class="p">,</span> <span class="n">show</span><span class="o">=</span><span class="mi">3</span><span class="p">)),</span>
        <span class="p">])</span>
</pre></div>
<p>Ensuite dans le fichier de settings propre aux tests, on modifie la liste des PASSWORD_HASHERS.</p>
<p><strong>settings_test.py</strong></p>
<div class="highlight"><pre><span class="kn">from</span> <span class="nn">myapp.settings</span> <span class="kn">import</span> <span class="o">*</span>
<span class="n">PASSWORD_HASHERS</span> <span class="o">=</span> <span class="p">(</span>
    <span class="s">'myapp.hashers.PlainPasswordHasher'</span><span class="p">,</span>
<span class="p">),</span>
</pre></div>
<p>Attention à ne pas utiliser cela en production, car tous les mots de
passes seraient stockés en clair dans la base de données.</p>
<p>Je vous invite à tester cette astuce sur votre base de tests pour voir
vous-même la différence de temps et nous faire un retour en
commentaire du post.</p>
<p>Si vous utilisez des fixtures de user, vous pouvez également utiliser
la solution ci-dessous dans votre settings de test afin que les tests
puissent décoder les mots de passe déjà stockés dans la base avec un
autre algorithme de mot de passe.</p>
<p><strong>settings_test.py</strong></p>
<div class="highlight"><pre><span class="kn">from</span> <span class="nn">myapp.settings</span> <span class="kn">import</span> <span class="o">*</span>
<span class="n">PASSWORD_HASHERS</span> <span class="o">=</span> <span class="p">[</span><span class="s">'myapp.hashers.PlainPasswordHasher'</span><span class="p">]</span> <span class="o">+</span> <span class="nb">list</span><span class="p">(</span><span class="n">PASSWORD_HASHERS</span><span class="p">)</span>
</pre></div>
</div>
<div class="section" id="conclusion">
<h2>Conclusion</h2>
<p>Cette astuce commence à être connue dans le monde Django, il y a même
<a class="reference external" href="https://code.djangoproject.com/ticket/18157">un ticket de Django qui en parle</a> <a class="footnote-reference" href="http://tech.novapost.fr/feeds/Python.atom.xml#id5" id="id6">[3]</a> et elle est maintenant décrite <a class="reference external" href="https://docs.djangoproject.com/en/1.4/topics/testing/#speeding-up-the-tests">dans la documentation officielle de Django</a> <a class="footnote-reference" href="http://tech.novapost.fr/feeds/Python.atom.xml#id7" id="id8">[4]</a>.</p>
<p>Notre hasher personnalisé permet juste de gagner encore un peu plus de
temps. Sur notre base de tests nous avons gagné 10 minutes sur 50
minutes soit 20% de temps de tests en passant du
<tt class="docutils literal">UnsaltedMD5PasswordHasher</tt> à notre <tt class="docutils literal">PlainTextPasswordHasher</tt></p>
<p>Nous avions initialement divisé par deux notre temps de tests en
passant à <tt class="docutils literal">UnsaltedMD5PasswordHasher</tt></p>
</div>
<div class="section" id="references">
<h2>Références</h2>
<table class="docutils footnote" frame="void" id="id1" rules="none">
<colgroup><col class="label" /><col /></colgroup>
<tbody valign="top">
<tr><td class="label"><a class="fn-backref" href="http://tech.novapost.fr/feeds/Python.atom.xml#id2">[1]</a></td><td><a class="reference external" href="http://igorsobreira.com/2012/09/19/improving-performance-of-django-test-suite.html">http://igorsobreira.com/2012/09/19/improving-performance-of-django-test-suite.html</a></td></tr>
</tbody>
</table>
<table class="docutils footnote" frame="void" id="id3" rules="none">
<colgroup><col class="label" /><col /></colgroup>
<tbody valign="top">
<tr><td class="label"><a class="fn-backref" href="http://tech.novapost.fr/feeds/Python.atom.xml#id4">[2]</a></td><td><a class="reference external" href="http://codahale.com/a-lesson-in-timing-attacks/">http://codahale.com/a-lesson-in-timing-attacks/</a></td></tr>
</tbody>
</table>
<table class="docutils footnote" frame="void" id="id5" rules="none">
<colgroup><col class="label" /><col /></colgroup>
<tbody valign="top">
<tr><td class="label"><a class="fn-backref" href="http://tech.novapost.fr/feeds/Python.atom.xml#id6">[3]</a></td><td><a class="reference external" href="https://code.djangoproject.com/ticket/18157">https://code.djangoproject.com/ticket/18157</a></td></tr>
</tbody>
</table>
<table class="docutils footnote" frame="void" id="id7" rules="none">
<colgroup><col class="label" /><col /></colgroup>
<tbody valign="top">
<tr><td class="label"><a class="fn-backref" href="http://tech.novapost.fr/feeds/Python.atom.xml#id8">[4]</a></td><td><a class="reference external" href="https://docs.djangoproject.com/en/1.4/topics/testing/#speeding-up-the-tests">https://docs.djangoproject.com/en/1.4/topics/testing/#speeding-up-the-tests</a></td></tr>
</tbody>
</table>
</div>]]></description>
    <link><![CDATA[http://tech.novapost.fr/django-comment-booster-ses-tests-en-1-minute.html]]></link>
    <pubDate>2013-03-20 09:16:00</pubDate>
    <category>python</category>
    <category>django</category>
    <category>django-fr</category>
    <category>testing</category>
  </item>
  <item>
    <title><![CDATA[[Inspyration] Python.org : Visualisez le nouveau site]]></title>
    <description><![CDATA[Python.org fait peau neuve, le nouveau site est en prévisualisation pour test, avant le PyCON.]]></description>
    <link><![CDATA[http://www.inspyration.org/news/python.org-visualisez-le-nouveau-site]]></link>
    <pubDate>2013-03-18 10:25:36</pubDate>
    <category>Python</category>
  </item>
  <item>
    <title><![CDATA[[novapost] Python decorators made easy]]></title>
    <description><![CDATA[<p>A Python decorator is, basically, a function that take a function as argument
and return a function. This is a powerful feature. But it has some drawbacks:</p>
<ul class="simple">
<li><strong>decorators are quite tricky to develop</strong>. Of course they are for Python
newbies. But, as an experienced Python developer, I must admit I also have to
think every time I code a decorator. I often feel the decorator pattern is a
bit complex.</li>
<li><strong>decorators that take arguments are even more tricky to develop</strong>. They are
decorator factories, aka &quot;functions that return a function that take a
function and return a function&quot;. Inception WTF?</li>
<li><strong>decorators without arguments are used without parentheses, whereas
decorators with arguments require parentheses</strong>, even if you pass empty
arguments. So when using a decorator, you have to wonder whether it takes
arguments or not. A bit more to think about everytime you use a decorator.</li>
<li>last but not least, <strong>function-based decorators are hard to test</strong>, because
they return functions and you can't easily check internals. How can you
check the state of the decorator after it decorated the function, but before
you actually run it? Classes are really helpful for that.</li>
</ul>
<p>This article introduces class-based decorators for Python, as a convenient way
to develop and use Python decorators.</p>
<p>The examples described below are available as a Python file at
<a class="reference external" href="https://gist.github.com/benoitbryon/5168914">https://gist.github.com/benoitbryon/5168914</a></p>
<div class="section" id="decorators-are-tricky-to-develop-and-use">
<h2>Decorators are tricky to develop and use</h2>
<p>As a reminder of drawbacks of decorators, here are some examples. If you are
aware of those facts, feel free to <a class="reference external" href="http://tech.novapost.fr/feeds/Python.atom.xml#hello-world-example">jump to the next section</a>.</p>
<p>Here is a simple decorator which prints &quot;moo&quot; then executes input function:</p>
<div class="highlight"><pre><span class="k">def</span> <span class="nf">moo</span><span class="p">(</span><span class="n">func</span><span class="p">):</span>
    <span class="k">def</span> <span class="nf">decorated</span><span class="p">(</span><span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
        <span class="k">print</span> <span class="s">'moo'</span>
        <span class="k">return</span> <span class="n">func</span><span class="p">(</span><span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">)</span>  <span class="c"># Run decorated function.</span>
    <span class="k">return</span> <span class="n">decorator</span>
</pre></div>
<p>You use it like this:</p>
<div class="highlight"><pre><span class="o">&gt;&gt;&gt;</span> <span class="nd">@moo</span>
<span class="o">...</span> <span class="k">def</span> <span class="nf">i_am_a</span><span class="p">(</span><span class="n">kind</span><span class="p">):</span>
<span class="o">...</span>     <span class="k">print</span> <span class="s">&quot;I am a {kind}&quot;</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">kind</span><span class="o">=</span><span class="n">kind</span><span class="p">)</span>
<span class="o">&gt;&gt;&gt;</span> <span class="n">i_am_a</span><span class="p">(</span><span class="s">&quot;duck&quot;</span><span class="p">)</span>
<span class="s">'moo'</span>
<span class="s">'I am a duck'</span>
</pre></div>
<p>Here is the same decorator, which allows you configure the value to print:</p>
<div class="highlight"><pre><span class="k">def</span> <span class="nf">speak</span><span class="p">(</span><span class="n">word</span><span class="o">=</span><span class="s">'moo'</span><span class="p">):</span>
    <span class="k">def</span> <span class="nf">decorator</span><span class="p">(</span><span class="n">func</span><span class="p">):</span>
        <span class="k">def</span> <span class="nf">decorated</span><span class="p">(</span><span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
            <span class="k">print</span> <span class="n">word</span>
            <span class="k">return</span> <span class="n">func</span><span class="p">(</span><span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">)</span>
        <span class="k">return</span> <span class="n">decorated</span>
    <span class="k">return</span> <span class="n">decorator</span>
</pre></div>
<p>You use it like that:</p>
<div class="highlight"><pre><span class="o">&gt;&gt;&gt;</span> <span class="nd">@speak</span><span class="p">(</span><span class="s">'quack'</span><span class="p">)</span>
<span class="o">...</span> <span class="k">def</span> <span class="nf">i_am_a</span><span class="p">(</span><span class="n">kind</span><span class="p">):</span>
<span class="o">...</span>     <span class="k">print</span> <span class="s">&quot;I am a {kind}&quot;</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">kind</span><span class="o">=</span><span class="n">kind</span><span class="p">)</span>
<span class="o">&gt;&gt;&gt;</span> <span class="n">i_am_a</span><span class="p">(</span><span class="s">&quot;duck&quot;</span><span class="p">)</span>
<span class="s">'quack'</span>
<span class="s">'I am a duck'</span>
</pre></div>
<p>If you want to use default arguments for &quot;speak&quot;, you have to use empty
parentheses, i.e. you can't write <tt class="docutils literal">&#64;speak</tt> like you used <tt class="docutils literal">&#64;moo</tt>, you have
to write <tt class="docutils literal">&#64;speak()</tt> instead.</p>
<p>I won't tell more about decorators here, there are plenty of articles about
them on the web. I just wanted to highlight the fact that even simplest
decorators are not as simple as they pretend.</p>
<p>But they could be! Let's introduce class-based decorators...</p>
</div>
<div class="section" id="hello-world-example">
<h2>Hello world example</h2>
<p>Here is a sample usage of the <a class="reference external" href="http://tech.novapost.fr/feeds/Python.atom.xml#the-decorator-class">Decorator</a> class:</p>
<div class="highlight"><pre><span class="k">class</span> <span class="nc">Greeter</span><span class="p">(</span><span class="n">Decorator</span><span class="p">):</span>
    <span class="sd">&quot;&quot;&quot;Greet return value of decorated function.&quot;&quot;&quot;</span>
    <span class="k">def</span> <span class="nf">setup</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">greeting</span><span class="o">=</span><span class="s">'hello'</span><span class="p">):</span>
        <span class="bp">self</span><span class="o">.</span><span class="n">greeting</span> <span class="o">=</span> <span class="n">greeting</span>

    <span class="k">def</span> <span class="nf">run</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
        <span class="n">name</span> <span class="o">=</span> <span class="nb">super</span><span class="p">(</span><span class="n">Greeter</span><span class="p">,</span> <span class="bp">self</span><span class="p">)</span><span class="o">.</span><span class="n">run</span><span class="p">(</span><span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">)</span>
        <span class="k">return</span> <span class="s">'{greeting} {name}!'</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">greeting</span><span class="o">=</span><span class="bp">self</span><span class="o">.</span><span class="n">greeting</span><span class="p">,</span> <span class="n">name</span><span class="o">=</span><span class="n">name</span><span class="p">)</span>
</pre></div>
<p>The implementation is pretty simple, isn't it? So is the usage!</p>
<p>As a Decorator, you can use it without options.</p>
<div class="highlight"><pre><span class="gp">&gt;&gt;&gt; </span><span class="nd">@Greeter</span>
<span class="gp">... </span><span class="k">def</span> <span class="nf">world</span><span class="p">():</span>
<span class="gp">... </span>    <span class="k">return</span> <span class="s">'world'</span>
<span class="gp">&gt;&gt;&gt; </span><span class="n">world</span><span class="p">()</span>
<span class="go">'hello world!'</span>
</pre></div>
<p>The example above is the same as providing empty options.</p>
<div class="highlight"><pre><span class="gp">&gt;&gt;&gt; </span><span class="nd">@Greeter</span><span class="p">()</span>
<span class="gp">... </span><span class="k">def</span> <span class="nf">world</span><span class="p">():</span>
<span class="gp">... </span>    <span class="k">return</span> <span class="s">'world'</span>
<span class="gp">&gt;&gt;&gt; </span><span class="n">world</span><span class="p">()</span>
<span class="go">'hello world!'</span>
</pre></div>
<p>It accepts one <tt class="docutils literal">greeting</tt> option:</p>
<div class="highlight"><pre><span class="gp">&gt;&gt;&gt; </span><span class="nd">@Greeter</span><span class="p">(</span><span class="n">greeting</span><span class="o">=</span><span class="s">'goodbye'</span><span class="p">)</span>
<span class="gp">... </span><span class="k">def</span> <span class="nf">world</span><span class="p">():</span>
<span class="gp">... </span>    <span class="k">return</span> <span class="s">'world'</span>
<span class="gp">&gt;&gt;&gt; </span><span class="n">world</span><span class="p">()</span>
<span class="go">'goodbye world!'</span>
</pre></div>
<p><tt class="docutils literal">greeting</tt> option defaults to <tt class="docutils literal">'hello'</tt>:</p>
<div class="highlight"><pre><span class="gp">&gt;&gt;&gt; </span><span class="n">my_greeter</span> <span class="o">=</span> <span class="n">Greeter</span><span class="p">()</span>
<span class="gp">&gt;&gt;&gt; </span><span class="n">my_greeter</span><span class="o">.</span><span class="n">greeting</span>
<span class="go">'hello'</span>
</pre></div>
<p>You can create a Greeter instance for later use:</p>
<div class="highlight"><pre><span class="gp">&gt;&gt;&gt; </span><span class="n">my_greeter</span> <span class="o">=</span> <span class="n">Greeter</span><span class="p">(</span><span class="n">greeting</span><span class="o">=</span><span class="s">'hi'</span><span class="p">)</span>
<span class="gp">&gt;&gt;&gt; </span><span class="nd">@my_greeter</span>
<span class="gp">... </span><span class="k">def</span> <span class="nf">world</span><span class="p">():</span>
<span class="gp">... </span>    <span class="k">return</span> <span class="s">'world'</span>
<span class="gp">&gt;&gt;&gt; </span><span class="n">world</span><span class="p">()</span>
<span class="go">'hi world!'</span>
</pre></div>
<p>Which gives you an opportunity to setup the greeter yourself:</p>
<div class="highlight"><pre><span class="gp">&gt;&gt;&gt; </span><span class="n">my_greeter</span> <span class="o">=</span> <span class="n">Greeter</span><span class="p">()</span>
<span class="gp">&gt;&gt;&gt; </span><span class="n">my_greeter</span><span class="o">.</span><span class="n">greeting</span> <span class="o">=</span> <span class="s">'bonjour'</span>
<span class="gp">&gt;&gt;&gt; </span><span class="nd">@my_greeter</span>
<span class="gp">... </span><span class="k">def</span> <span class="nf">world</span><span class="p">():</span>
<span class="gp">... </span>    <span class="k">return</span> <span class="s">'world'</span>
<span class="gp">&gt;&gt;&gt; </span><span class="n">world</span><span class="p">()</span>
<span class="go">'bonjour world!'</span>
</pre></div>
<p>In this example, all arguments are proxied to the decorated function:</p>
<div class="highlight"><pre><span class="gp">&gt;&gt;&gt; </span><span class="nd">@Greeter</span>
<span class="gp">... </span><span class="k">def</span> <span class="nf">name</span><span class="p">(</span><span class="n">value</span><span class="p">):</span>
<span class="gp">... </span>    <span class="k">return</span> <span class="n">value</span>
<span class="gp">&gt;&gt;&gt; </span><span class="n">name</span><span class="p">(</span><span class="s">'world'</span><span class="p">)</span>
<span class="go">'hello world!'</span>

<span class="gp">&gt;&gt;&gt; </span><span class="nd">@Greeter</span><span class="p">(</span><span class="n">greeting</span><span class="o">=</span><span class="s">'goodbye'</span><span class="p">)</span>
<span class="gp">... </span><span class="k">def</span> <span class="nf">names</span><span class="p">(</span><span class="o">*</span><span class="n">args</span><span class="p">):</span>
<span class="gp">... </span>    <span class="k">return</span> <span class="s">' and '</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="n">args</span><span class="p">)</span>
<span class="gp">&gt;&gt;&gt; </span><span class="n">names</span><span class="p">(</span><span class="s">'Laurel'</span><span class="p">,</span> <span class="s">'Hardy'</span><span class="p">)</span>
<span class="go">'goodbye Laurel and Hardy!'</span>
</pre></div>
</div>
<div class="section" id="wrapping-functions-with-functools">
<h2>Wrapping functions with functools</h2>
<p><a class="reference external" href="http://docs.python.org/2.7/library/functools.html">functools</a> <a class="footnote-reference" href="http://tech.novapost.fr/feeds/Python.atom.xml#id3" id="id4">[1]</a> provides utilities to &quot;wrap&quot; a function, i.e. make the decorator
return value look like the original function.</p>
<p>Here is another class-based decorator sample. It adds
&quot;functools.update_wrapper&quot; features to <a class="reference external" href="http://tech.novapost.fr/feeds/Python.atom.xml#the-decorator-class">Decorator</a>:</p>
<div class="highlight"><pre><span class="kn">import</span> <span class="nn">functools</span>

<span class="k">class</span> <span class="nc">Chameleon</span><span class="p">(</span><span class="n">Decorator</span><span class="p">):</span>
    <span class="sd">&quot;&quot;&quot;A Decorator that looks like decorated function.</span>

<span class="sd">    It uses ``functools.update_wrapper``.</span>

<span class="sd">    This is a base class which acts as a transparent proxy for the</span>
<span class="sd">    decorated function. Consider overriding the ``run()`` method.</span>

<span class="sd">    .. warning::</span>

<span class="sd">       Take care of what you pass in ``assigned`` or ``updated``: you could</span>
<span class="sd">       break the Chameleon itself. As an example, you should not pass &quot;assigned&quot;,</span>
<span class="sd">       &quot;run&quot; or &quot;__call__&quot; in ``assigned``, except you know what you are doing.</span>

<span class="sd">    &quot;&quot;&quot;</span>
    <span class="k">def</span> <span class="nf">setup</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span>
              <span class="n">assigned</span><span class="o">=</span><span class="n">functools</span><span class="o">.</span><span class="n">WRAPPER_ASSIGNMENTS</span><span class="p">,</span>
              <span class="n">updated</span><span class="o">=</span><span class="n">functools</span><span class="o">.</span><span class="n">WRAPPER_UPDATES</span><span class="p">):</span>
        <span class="bp">self</span><span class="o">.</span><span class="n">assigned</span> <span class="o">=</span> <span class="n">assigned</span>
        <span class="bp">self</span><span class="o">.</span><span class="n">updated</span> <span class="o">=</span> <span class="n">updated</span>

    <span class="k">def</span> <span class="nf">decorate</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">func</span><span class="p">):</span>
        <span class="sd">&quot;&quot;&quot;Make self wrap the decorated function.&quot;&quot;&quot;</span>
        <span class="nb">super</span><span class="p">(</span><span class="n">Chameleon</span><span class="p">,</span> <span class="bp">self</span><span class="p">)</span><span class="o">.</span><span class="n">decorate</span><span class="p">(</span><span class="n">func</span><span class="p">)</span>
        <span class="n">functools</span><span class="o">.</span><span class="n">update_wrapper</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">func</span><span class="p">,</span>
                                 <span class="n">assigned</span><span class="o">=</span><span class="bp">self</span><span class="o">.</span><span class="n">assigned</span><span class="p">,</span>
                                 <span class="n">updated</span><span class="o">=</span><span class="bp">self</span><span class="o">.</span><span class="n">updated</span><span class="p">)</span>
</pre></div>
<p>Again, the implementation is pretty simple.</p>
<p>Let's look at the result...</p>
<div class="highlight"><pre><span class="gp">&gt;&gt;&gt; </span><span class="nd">@Chameleon</span>
<span class="gp">... </span><span class="k">def</span> <span class="nf">documented</span><span class="p">():</span>
<span class="gp">... </span>    <span class="sd">'''Fake function with a docstring.'''</span>
<span class="gp">&gt;&gt;&gt; </span><span class="n">documented</span><span class="o">.</span><span class="n">__doc__</span>
<span class="go">'Fake function with a docstring.'</span>
</pre></div>
<p>It accepts options <tt class="docutils literal">assigned</tt> and <tt class="docutils literal">updated</tt>, that are proxied to
<tt class="docutils literal">functools.update_wrapper</tt>.</p>
<p>Default values are <tt class="docutils literal">functools.WRAPPER_ASSIGNMENTS</tt> for <tt class="docutils literal">assigned</tt> and
empty tuple for <tt class="docutils literal">updated</tt>.</p>
<div class="highlight"><pre><span class="gp">&gt;&gt;&gt; </span><span class="k">def</span> <span class="nf">hello</span><span class="p">():</span>
<span class="gp">... </span>   <span class="sd">'''Hello world!'''</span>
<span class="gp">&gt;&gt;&gt; </span><span class="n">wrapped</span> <span class="o">=</span> <span class="n">Chameleon</span><span class="p">(</span><span class="n">hello</span><span class="p">)</span>
<span class="gp">&gt;&gt;&gt; </span><span class="n">wrapped</span><span class="o">.</span><span class="n">assigned</span>
<span class="go">('__module__', '__name__', '__doc__')</span>
<span class="gp">&gt;&gt;&gt; </span><span class="n">wrapped</span><span class="o">.</span><span class="n">updated</span>
<span class="go">('__dict__',)</span>
<span class="gp">&gt;&gt;&gt; </span><span class="n">wrapped</span><span class="o">.</span><span class="n">__doc__</span> <span class="o">==</span> <span class="n">hello</span><span class="o">.</span><span class="n">__doc__</span>
<span class="go">True</span>
<span class="gp">&gt;&gt;&gt; </span><span class="n">wrapped</span><span class="o">.</span><span class="n">__name__</span> <span class="o">==</span> <span class="n">hello</span><span class="o">.</span><span class="n">__name__</span>
<span class="go">True</span>

<span class="gp">&gt;&gt;&gt; </span><span class="n">only_doc_wrapped</span> <span class="o">=</span> <span class="n">Chameleon</span><span class="p">(</span><span class="n">hello</span><span class="p">,</span> <span class="n">assigned</span><span class="o">=</span><span class="p">[</span><span class="s">'__doc__'</span><span class="p">])</span>
<span class="gp">&gt;&gt;&gt; </span><span class="n">only_doc_wrapped</span><span class="o">.</span><span class="n">__doc__</span> <span class="o">==</span> <span class="n">hello</span><span class="o">.</span><span class="n">__doc__</span>
<span class="go">True</span>
<span class="gp">&gt;&gt;&gt; </span><span class="n">only_doc_wrapped</span><span class="o">.</span><span class="n">__name__</span> <span class="o">==</span> <span class="n">hello</span><span class="o">.</span><span class="n">__name__</span>  <span class="c"># Doctest: +ELLIPSIS</span>
<span class="gt">Traceback (most recent call last):</span>
    <span class="o">...</span>
<span class="gr">AttributeError</span>: <span class="n">'Chameleon' object has no attribute '__name__'</span>

<span class="gp">&gt;&gt;&gt; </span><span class="n">hello</span><span class="o">.</span><span class="n">__dict__</span> <span class="o">=</span> <span class="p">{</span><span class="s">'some_attribute'</span><span class="p">:</span> <span class="s">'some value'</span><span class="p">}</span>  <span class="c"># Best on an object.</span>
<span class="gp">&gt;&gt;&gt; </span><span class="n">attr_wrapped</span> <span class="o">=</span> <span class="n">Chameleon</span><span class="p">(</span><span class="n">hello</span><span class="p">,</span> <span class="n">updated</span><span class="o">=</span><span class="p">[</span><span class="s">'__dict__'</span><span class="p">])</span>
<span class="gp">&gt;&gt;&gt; </span><span class="n">attr_wrapped</span><span class="o">.</span><span class="n">updated</span>
<span class="go">['__dict__']</span>
<span class="gp">&gt;&gt;&gt; </span><span class="n">attr_wrapped</span><span class="o">.</span><span class="n">some_attribute</span>
<span class="go">'some value'</span>
</pre></div>
<p>Here we have a good replacement for decorators using <tt class="docutils literal">functools.wraps</tt>.</p>
<p>As an example, the django-traditional-style decorator shown in
<a class="reference external" href="http://tech.novapost.fr/django-testing-view-decorators-en.html">Testing Django view decorators</a> article...</p>
<div class="highlight"><pre><span class="kn">from</span> <span class="nn">functools</span> <span class="kn">import</span> <span class="n">wraps</span>
<span class="kn">from</span> <span class="nn">django.utils.decorators</span> <span class="kn">import</span> <span class="n">available_attrs</span>

<span class="k">def</span> <span class="nf">authenticated_user_passes_test</span><span class="p">(</span><span class="n">test_func</span><span class="p">,</span>
                                   <span class="n">unauthorized</span><span class="o">=</span><span class="n">UnauthorizedView</span><span class="o">.</span><span class="n">as_view</span><span class="p">(),</span>
                                   <span class="n">forbidden</span><span class="o">=</span><span class="n">ForbiddenView</span><span class="o">.</span><span class="n">as_view</span><span class="p">()):</span>
    <span class="sd">&quot;&quot;&quot;Make sure user is authenticated and passes test.&quot;&quot;&quot;</span>
    <span class="k">def</span> <span class="nf">decorator</span><span class="p">(</span><span class="n">view_func</span><span class="p">):</span>
        <span class="nd">@wraps</span><span class="p">(</span><span class="n">view_func</span><span class="p">,</span> <span class="n">assigned</span><span class="o">=</span><span class="n">available_attrs</span><span class="p">(</span><span class="n">view_func</span><span class="p">))</span>
        <span class="k">def</span> <span class="nf">_wrapped_view</span><span class="p">(</span><span class="n">request</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
            <span class="k">if</span> <span class="ow">not</span> <span class="n">request</span><span class="o">.</span><span class="n">user</span><span class="o">.</span><span class="n">is_authenticated</span><span class="p">():</span>
                <span class="k">return</span> <span class="n">unauthorized</span><span class="p">(</span><span class="n">request</span><span class="p">)</span>
            <span class="k">if</span> <span class="ow">not</span> <span class="n">test_func</span><span class="p">(</span><span class="n">request</span><span class="o">.</span><span class="n">user</span><span class="p">):</span>
                <span class="k">return</span> <span class="n">forbidden</span><span class="p">(</span><span class="n">request</span><span class="p">)</span>
            <span class="k">return</span> <span class="n">view_func</span><span class="p">(</span><span class="n">request</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">)</span>
</pre></div>
<p>... would be written like this with class-based-style:</p>
<div class="highlight"><pre><span class="k">class</span> <span class="nc">authenticated_user_passes_test</span><span class="p">(</span><span class="n">Chameleon</span><span class="p">):</span>
    <span class="sd">&quot;&quot;&quot;Make sure user is authenticated and passes test.&quot;&quot;&quot;</span>
    <span class="k">def</span> <span class="nf">setup</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
        <span class="k">try</span><span class="p">:</span>
            <span class="bp">self</span><span class="o">.</span><span class="n">test_func</span> <span class="o">=</span> <span class="n">kwargs</span><span class="o">.</span><span class="n">pop</span><span class="p">(</span><span class="s">'test_func'</span><span class="p">)</span>
        <span class="k">except</span> <span class="ne">KeyError</span><span class="p">:</span>
            <span class="k">raise</span> <span class="ne">TypeError</span><span class="p">(</span><span class="s">'decorator requires &quot;test_func&quot; keyword argument'</span><span class="p">)</span>
        <span class="bp">self</span><span class="o">.</span><span class="n">unauthorized</span> <span class="o">=</span> <span class="n">kwargs</span><span class="o">.</span><span class="n">pop</span><span class="p">(</span><span class="s">'unauthorized'</span><span class="p">,</span> <span class="n">UnauthorizedView</span><span class="o">.</span><span class="n">as_view</span><span class="p">())</span>
        <span class="bp">self</span><span class="o">.</span><span class="n">forbidden</span> <span class="o">=</span> <span class="n">kwargs</span><span class="o">.</span><span class="n">pop</span><span class="p">(</span><span class="s">'forbidden'</span><span class="p">,</span> <span class="n">ForbiddenView</span><span class="o">.</span><span class="n">as_view</span><span class="p">())</span>
        <span class="nb">super</span><span class="p">(</span><span class="n">authenticated_user_passes_test</span><span class="p">,</span> <span class="bp">self</span><span class="p">)</span><span class="o">.</span><span class="n">setup</span><span class="p">(</span><span class="o">**</span><span class="n">kwargs</span><span class="p">)</span>

    <span class="k">def</span> <span class="nf">run</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">request</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
        <span class="k">if</span> <span class="ow">not</span> <span class="n">request</span><span class="o">.</span><span class="n">user</span><span class="o">.</span><span class="n">is_authenticated</span><span class="p">():</span>
            <span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">unauthorized</span><span class="p">(</span><span class="n">request</span><span class="p">)</span>
        <span class="k">if</span> <span class="ow">not</span> <span class="bp">self</span><span class="o">.</span><span class="n">test_func</span><span class="p">(</span><span class="n">request</span><span class="o">.</span><span class="n">user</span><span class="p">):</span>
            <span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">forbidden</span><span class="p">(</span><span class="n">request</span><span class="p">)</span>
        <span class="k">return</span> <span class="o">=</span> <span class="nb">super</span><span class="p">(</span><span class="n">authenticated_user_passes_test</span><span class="p">,</span> <span class="bp">self</span><span class="p">)</span><span class="o">.</span><span class="n">run</span><span class="p">(</span><span class="n">request</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">)</span>
</pre></div>
<p>The class-based way is a bit longer because we have to handle the required
<tt class="docutils literal">test_func</tt> manually. But it clearly separates setup from run. It makes the
code readable, easy to test, and easy to extend.</p>
</div>
<div class="section" id="the-decorator-class">
<h2>The Decorator class</h2>
<p>Here is the base class. Little magic inside.</p>
<div class="highlight"><pre><span class="k">class</span> <span class="nc">Decorator</span><span class="p">(</span><span class="nb">object</span><span class="p">):</span>
    <span class="sd">&quot;&quot;&quot;Base class to easily create convenient decorators.</span>

<span class="sd">    Override :py:meth:`setup`, :py:meth:`run` or :py:meth:`decorate` to create</span>
<span class="sd">    custom decorators.</span>

<span class="sd">    Decorator instances are callables. The :py:meth:`__call__` method has a</span>
<span class="sd">    special implementation in Decorator. Generally, consider overriding</span>
<span class="sd">    :py:meth:`run` instead of :py:meth:`__call__`.</span>

<span class="sd">    &quot;&quot;&quot;</span>
    <span class="k">def</span> <span class="nf">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">func</span><span class="o">=</span><span class="bp">None</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
        <span class="sd">&quot;&quot;&quot;Constructor.</span>

<span class="sd">        Accepts one optional positional argument: the function to decorate.</span>

<span class="sd">        Other arguments **must** be keyword arguments.</span>

<span class="sd">        And beware passing ``func`` as keyword argument: it would be used as</span>
<span class="sd">        the function to decorate.</span>

<span class="sd">        &quot;&quot;&quot;</span>
        <span class="bp">self</span><span class="o">.</span><span class="n">setup</span><span class="p">(</span><span class="o">**</span><span class="n">kwargs</span><span class="p">)</span>
        <span class="k">if</span> <span class="n">func</span> <span class="ow">is</span> <span class="ow">not</span> <span class="bp">None</span><span class="p">:</span>
            <span class="bp">self</span><span class="o">.</span><span class="n">decorate</span><span class="p">(</span><span class="n">func</span><span class="p">)</span>

    <span class="k">def</span> <span class="nf">decorate</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">func</span><span class="p">):</span>
        <span class="sd">&quot;&quot;&quot;Remember the function to decorate.&quot;&quot;&quot;</span>
        <span class="bp">self</span><span class="o">.</span><span class="n">decorated</span> <span class="o">=</span> <span class="n">func</span>

    <span class="k">def</span> <span class="nf">setup</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
        <span class="sd">&quot;&quot;&quot;Store decorator's options.&quot;&quot;&quot;</span>
        <span class="bp">self</span><span class="o">.</span><span class="n">options</span> <span class="o">=</span> <span class="n">kwargs</span>

    <span class="k">def</span> <span class="nf">__call__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
        <span class="sd">&quot;&quot;&quot;Run decorated function if available, else decorate first arg.&quot;&quot;&quot;</span>
        <span class="k">try</span><span class="p">:</span>
            <span class="bp">self</span><span class="o">.</span><span class="n">decorated</span>
        <span class="k">except</span> <span class="ne">AttributeError</span><span class="p">:</span>
            <span class="n">func</span> <span class="o">=</span> <span class="n">args</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span>
            <span class="bp">self</span><span class="o">.</span><span class="n">decorate</span><span class="p">(</span><span class="n">func</span><span class="p">)</span>
            <span class="k">return</span> <span class="bp">self</span>
        <span class="k">else</span><span class="p">:</span>
            <span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">run</span><span class="p">(</span><span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">)</span>

    <span class="k">def</span> <span class="nf">run</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
        <span class="sd">&quot;&quot;&quot;Actually run the decorator.</span>

<span class="sd">        This base implementation is a transparent proxy to the decorated</span>
<span class="sd">        function: it passes positional and keyword arguments as is, and returns</span>
<span class="sd">        result.</span>

<span class="sd">        &quot;&quot;&quot;</span>
        <span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">decorated</span><span class="p">(</span><span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">)</span>
</pre></div>
<p>This base class transparently proxies to decorated function:</p>
<div class="highlight"><pre><span class="gp">&gt;&gt;&gt; </span><span class="nd">@Decorator</span>
<span class="gp">... </span><span class="k">def</span> <span class="nf">return_args</span><span class="p">(</span><span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
<span class="gp">... </span>   <span class="k">return</span> <span class="p">(</span><span class="n">args</span><span class="p">,</span> <span class="n">kwargs</span><span class="p">)</span>
<span class="gp">&gt;&gt;&gt; </span><span class="n">return_args</span><span class="p">()</span>
<span class="go">((), {})</span>
<span class="gp">&gt;&gt;&gt; </span><span class="n">return_args</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="n">three</span><span class="o">=</span><span class="mi">3</span><span class="p">)</span>
<span class="go">((1, 2), {'three': 3})</span>
</pre></div>
<p>This base class stores decorator's options in <tt class="docutils literal">options</tt> dictionary. But
it doesn't use it... it's just a convenient mechanism for subclasses.</p>
<div class="highlight"><pre><span class="gp">&gt;&gt;&gt; </span><span class="nd">@Decorator</span>
<span class="gp">... </span><span class="k">def</span> <span class="nf">nothing</span><span class="p">():</span>
<span class="gp">... </span>   <span class="k">pass</span>
<span class="gp">&gt;&gt;&gt; </span><span class="n">nothing</span><span class="o">.</span><span class="n">options</span>
<span class="go">{}</span>

<span class="gp">&gt;&gt;&gt; </span><span class="nd">@Decorator</span><span class="p">()</span>
<span class="gp">... </span><span class="k">def</span> <span class="nf">nothing</span><span class="p">():</span>
<span class="gp">... </span>   <span class="k">pass</span>
<span class="gp">&gt;&gt;&gt; </span><span class="n">nothing</span><span class="o">.</span><span class="n">options</span>
<span class="go">{}</span>

<span class="gp">&gt;&gt;&gt; </span><span class="nd">@Decorator</span><span class="p">(</span><span class="n">one</span><span class="o">=</span><span class="mi">1</span><span class="p">)</span>
<span class="gp">... </span><span class="k">def</span> <span class="nf">nothing</span><span class="p">():</span>
<span class="gp">... </span>   <span class="k">pass</span>
<span class="gp">&gt;&gt;&gt; </span><span class="n">nothing</span><span class="o">.</span><span class="n">options</span>
<span class="go">{'one': 1}</span>
</pre></div>
</div>
<div class="section" id="limitations">
<h2>Limitations</h2>
<p>This <a class="reference external" href="http://tech.novapost.fr/feeds/Python.atom.xml#the-decorated-class">Decorated</a> implementation has some
limitations:</p>
<ul class="simple">
<li>you can't use positional arguments to configure the decorator itself.</li>
<li>required decorator arguments must be handled explicitely, because you can't
use positional arguments.</li>
<li>you have to remember the <tt class="docutils literal">Decorator</tt> API, mainly <tt class="docutils literal">setup()</tt> and <tt class="docutils literal">run()</tt>
methods.</li>
</ul>
<p>Are there other limitations? Right now, I don't know...</p>
</div>
<div class="section" id="benefits">
<h2>Benefits</h2>
<ul class="simple">
<li>As a decorator author, you focus on <tt class="docutils literal">setup()</tt> and <tt class="docutils literal">run()</tt>.
It is easy to remember. It produces readable code.</li>
<li>As a decorator user, you don't bother with parentheses. You just use the
decorator depending on your needs, and it works.</li>
<li>As a test writer, you can write tests for decorators internals:
what is the state of the decorator after its own initialization,
what is its state after a run...</li>
</ul>
<p>Would you use it?</p>
</div>
<div class="section" id="see-also">
<h2>See also</h2>
<ul class="simple">
<li><a class="reference external" href="http://gillesfabio.com/blog/2010/12/16/python-et-les-decorateurs/">&quot;Python et les décorateurs&quot;, by Gilles Fabio</a>
is a good article (in french). It ends with an list of useful links
(most in english). It also provides a function-based implementation of
decorators that work with or without arguments.</li>
<li><a class="reference external" href="http://tech.novapost.fr/django-testing-view-decorators-en.html">Testing Django view decorators</a></li>
</ul>
<table class="docutils footnote" frame="void" id="id3" rules="none">
<colgroup><col class="label" /><col /></colgroup>
<tbody valign="top">
<tr><td class="label"><a class="fn-backref" href="http://tech.novapost.fr/feeds/Python.atom.xml#id4">[1]</a></td><td><a class="reference external" href="http://docs.python.org/2.7/library/functools.html">http://docs.python.org/2.7/library/functools.html</a></td></tr>
</tbody>
</table>
</div>]]></description>
    <link><![CDATA[http://tech.novapost.fr/python-class-based-decorators-en.html]]></link>
    <pubDate>2013-03-15 11:00:00</pubDate>
    <category>python</category>
  </item>
  <item>
    <title><![CDATA[[novapost] Testing Django decorators]]></title>
    <description><![CDATA[<p>How to test view decorators of Django applications? Here are some tips.</p>
<p>In a post before, I recommended to avoid decorating views in place (i.e. not
in views.py). Once decorators and views are separated, we can <a class="reference external" href="http://tech.novapost.fr/feeds/|filename|django-testing-view-decorators.rst">unit test the
views</a>. That was the topic of
the post before. This article focuses on testing decorators.</p>
<p>The examples described below are available as a Python file at
<a class="reference external" href="https://gist.github.com/benoitbryon/5156512">https://gist.github.com/benoitbryon/5156512</a></p>
<div class="section" id="use-unittest-mock">
<h2>Use unittest.mock</h2>
<p>Learn about <a class="reference external" href="http://docs.python.org/3/library/unittest.mock.html">unittest.mock</a> <a class="footnote-reference" href="http://tech.novapost.fr/feeds/Python.atom.xml#id3" id="id4">[2]</a> (or backward-compatible <a class="reference external" href="https://pypi.python.org/pypi/mock">mock</a> <a class="footnote-reference" href="http://tech.novapost.fr/feeds/Python.atom.xml#id1" id="id2">[1]</a>) library!
This article makes heavy use of those wonderful features.</p>
<p>In <tt class="docutils literal">tests.py</tt>:</p>
<div class="highlight"><pre><span class="k">try</span><span class="p">:</span>
    <span class="kn">from</span> <span class="nn">unittest</span> <span class="kn">import</span> <span class="n">mock</span>
<span class="k">except</span> <span class="ne">ImportError</span><span class="p">:</span>
    <span class="kn">import</span> <span class="nn">mock</span>
</pre></div>
<p>And in project's or app's <tt class="docutils literal">setup.py</tt>:</p>
<div class="highlight"><pre><span class="c"># ...</span>
<span class="n">requirements</span> <span class="o">=</span> <span class="p">[]</span>
<span class="k">try</span><span class="p">:</span>
    <span class="kn">from</span> <span class="nn">unittest</span> <span class="kn">import</span> <span class="n">mock</span>
<span class="k">except</span> <span class="ne">ImportError</span><span class="p">:</span>
    <span class="n">requirements</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="s">'mock'</span><span class="p">)</span>
<span class="c"># ...</span>
<span class="n">setup</span><span class="p">(</span>
    <span class="c"># ...</span>
    <span class="n">install_requires</span><span class="p">(</span><span class="n">requirements</span><span class="p">),</span>
    <span class="c"># ...</span>
<span class="p">)</span>
</pre></div>
</div>
<div class="section" id="fake-the-request">
<h2>Fake the request</h2>
<p>We want to focus on the decorator. Does it rely on the request to perform
some actions? Fake/stub/mock all and only what you need.</p>
<p><a class="reference external" href="https://docs.djangoproject.com/en/1.5/topics/testing/advanced/#django.test.client.RequestFactory">django.test.RequestFactory</a> <a class="footnote-reference" href="http://tech.novapost.fr/feeds/Python.atom.xml#id6" id="id7">[3]</a> can be useful.
But sometimes it is overkill and <a class="reference external" href="https://docs.djangoproject.com/en/1.5/ref/request-response/#django.http.HttpRequest">django.http.HttpRequest</a> <a class="footnote-reference" href="http://tech.novapost.fr/feeds/Python.atom.xml#id8" id="id9">[4]</a> is enough.</p>
<p>In the <a class="reference external" href="http://tech.novapost.fr/feeds/Python.atom.xml#hello-world-decorator">hello world example</a> below, we'll use a
completely fake request:</p>
<div class="highlight"><pre><span class="n">request</span> <span class="o">=</span> <span class="s">'fake request'</span>
</pre></div>
<p>In the <a class="reference external" href="http://tech.novapost.fr/feeds/Python.atom.xml#authenticated-user-passes-test-decorator">authenticated_user_passes_test example</a> below, we use mocks to support
<tt class="docutils literal">request.user.is_authenticated()</tt>:</p>
<div class="highlight"><pre><span class="n">request</span> <span class="o">=</span> <span class="n">mock</span><span class="o">.</span><span class="n">MagicMock</span><span class="p">()</span>
<span class="n">request</span><span class="o">.</span><span class="n">user</span><span class="o">.</span><span class="n">is_authenticated</span> <span class="o">=</span> <span class="n">mock</span><span class="o">.</span><span class="n">MagicMock</span><span class="p">(</span><span class="n">return_value</span><span class="o">=</span><span class="bp">True</span><span class="p">)</span>
</pre></div>
</div>
<div class="section" id="stub-the-decorated-view">
<h2>Stub the decorated view</h2>
<p>We want to focus on the decorator. We don't care about decorated view
implementation. But we care about how the decorator handles the view.
Let's use <a class="reference external" href="http://docs.python.org/3/library/unittest.mock.html">unittest.mock</a> <a class="footnote-reference" href="http://tech.novapost.fr/feeds/Python.atom.xml#id3" id="id5">[2]</a>.</p>
<p>Decorated views are functions (or callables).
We can instantiate and check a mocked-view like this:</p>
<div class="highlight"><pre><span class="kn">import</span> <span class="nn">unittest</span>

<span class="k">class</span> <span class="nc">MockViewTestCase</span><span class="p">(</span><span class="n">unittest</span><span class="o">.</span><span class="n">TestCase</span><span class="p">):</span>
    <span class="k">def</span> <span class="nf">test_stub</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
        <span class="c"># Setup.</span>
        <span class="n">request</span> <span class="o">=</span> <span class="s">'fake request'</span>
        <span class="n">view</span> <span class="o">=</span> <span class="n">mock</span><span class="o">.</span><span class="n">MagicMock</span><span class="p">(</span><span class="n">return_value</span><span class="o">=</span><span class="s">'fake response'</span><span class="p">)</span>
        <span class="c"># Run.</span>
        <span class="n">response</span> <span class="o">=</span> <span class="n">view</span><span class="p">(</span><span class="n">request</span><span class="p">)</span>
        <span class="c"># Check.</span>
        <span class="n">view</span><span class="o">.</span><span class="n">assert_called_once_with</span><span class="p">(</span><span class="n">request</span><span class="p">)</span>
        <span class="bp">self</span><span class="o">.</span><span class="n">assertEqual</span><span class="p">(</span><span class="n">response</span><span class="p">,</span> <span class="n">view</span><span class="o">.</span><span class="n">return_value</span><span class="p">)</span>
</pre></div>
</div>
<div class="section" id="hello-world-decorator">
<h2>hello_world decorator</h2>
<p>Before we dive into a real-life example, let's consider a really simple one.</p>
<p>Here is the decorator:</p>
<div class="highlight"><pre><span class="kn">from</span> <span class="nn">django.http</span> <span class="kn">import</span> <span class="n">HttpResponse</span>

<span class="k">def</span> <span class="nf">hello_world</span><span class="p">(</span><span class="n">view_func</span><span class="p">):</span>
    <span class="sd">&quot;&quot;&quot;Run the decorated view, but return &quot;Hello world!&quot;.&quot;&quot;&quot;</span>
    <span class="k">def</span> <span class="nf">decorated_view</span><span class="p">(</span><span class="n">request</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
        <span class="n">view_func</span><span class="p">(</span><span class="n">request</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">)</span>
        <span class="k">return</span> <span class="n">HttpResponse</span><span class="p">(</span><span class="s">u'Hello world!'</span><span class="p">)</span>
    <span class="k">return</span> <span class="n">decorated_view</span>
</pre></div>
<p>Here is the test case:</p>
<div class="highlight"><pre><span class="kn">import</span> <span class="nn">unittest</span>

<span class="k">class</span> <span class="nc">HelloWorldTestCase</span><span class="p">(</span><span class="n">unittest</span><span class="o">.</span><span class="n">TestCase</span><span class="p">):</span>
    <span class="k">def</span> <span class="nf">test_hello_decorator</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
        <span class="sd">&quot;&quot;&quot;hello_world decorator runs view and returns greetings.&quot;&quot;&quot;</span>
        <span class="c"># Setup.</span>
        <span class="n">request</span> <span class="o">=</span> <span class="s">'fake request'</span>
        <span class="n">request_args</span> <span class="o">=</span> <span class="p">(</span><span class="s">'foo'</span><span class="p">,</span> <span class="p">)</span>
        <span class="n">request_kwargs</span> <span class="o">=</span> <span class="p">{</span><span class="s">'bar'</span><span class="p">:</span> <span class="s">'baz'</span><span class="p">}</span>
        <span class="n">view</span> <span class="o">=</span> <span class="n">mock</span><span class="o">.</span><span class="n">MagicMock</span><span class="p">(</span><span class="n">return_value</span><span class="o">=</span><span class="s">'fake response'</span><span class="p">)</span>
        <span class="c"># Run.</span>
        <span class="n">decorated</span> <span class="o">=</span> <span class="n">hello_world</span><span class="p">(</span><span class="n">view</span><span class="p">)</span>
        <span class="n">response</span> <span class="o">=</span> <span class="n">decorated</span><span class="p">(</span><span class="n">request</span><span class="p">,</span> <span class="o">*</span><span class="n">request_args</span><span class="p">,</span> <span class="o">**</span><span class="n">request_kwargs</span><span class="p">)</span>
        <span class="c"># Check.</span>
        <span class="c"># View was called.</span>
        <span class="n">view</span><span class="o">.</span><span class="n">assert_called_once_with</span><span class="p">(</span><span class="n">request</span><span class="p">,</span> <span class="o">*</span><span class="n">request_args</span><span class="p">,</span> <span class="o">**</span><span class="n">request_kwargs</span><span class="p">)</span>
        <span class="c"># But response is &quot;Hello world!&quot;.</span>
        <span class="bp">self</span><span class="o">.</span><span class="n">assertEqual</span><span class="p">(</span><span class="n">response</span><span class="o">.</span><span class="n">status_code</span><span class="p">,</span> <span class="mi">200</span><span class="p">)</span>
        <span class="bp">self</span><span class="o">.</span><span class="n">assertEqual</span><span class="p">(</span><span class="n">response</span><span class="o">.</span><span class="n">content</span><span class="p">,</span> <span class="s">u&quot;Hello world!&quot;</span><span class="p">)</span>
</pre></div>
<p>The test looks like a documentation for the decorator :)</p>
<p>Some noticeable points:</p>
<ul class="simple">
<li>we haven't used Django's builtin test client. We haven't needed to setup
URLconfs, settings, &quot;real&quot; views...</li>
<li>there is no database transaction involved, so we used <tt class="docutils literal">unittest.TestCase</tt>.</li>
</ul>
</div>
<div class="section" id="authenticated-user-passes-test-decorator">
<h2>authenticated_user_passes_test decorator</h2>
<p>Now let's consider a real-life example, with a custom decorator:</p>
<div class="highlight"><pre><span class="kn">from</span> <span class="nn">functools</span> <span class="kn">import</span> <span class="n">wraps</span>

<span class="kn">from</span> <span class="nn">django.utils.decorators</span> <span class="kn">import</span> <span class="n">available_attrs</span>

<span class="k">def</span> <span class="nf">authenticated_user_passes_test</span><span class="p">(</span><span class="n">test_func</span><span class="p">,</span>
                                   <span class="n">unauthorized</span><span class="o">=</span><span class="n">UnauthorizedView</span><span class="o">.</span><span class="n">as_view</span><span class="p">(),</span>
                                   <span class="n">forbidden</span><span class="o">=</span><span class="n">ForbiddenView</span><span class="o">.</span><span class="n">as_view</span><span class="p">()):</span>
    <span class="sd">&quot;&quot;&quot;Make sure user is authenticated and passes test.</span>

<span class="sd">    This is an adaptation of</span>
<span class="sd">    ``django.contrib.auth.decorators.user_passes_test`` where:</span>

<span class="sd">    * if user is anonymous, the request is routed to ``unauthorized`` view.</span>
<span class="sd">      No additional tests are performed in that case.</span>

<span class="sd">    * if user is authenticated and doesn't pass ``test_func ``test, the</span>
<span class="sd">      request is routed to ``forbidden`` view.</span>

<span class="sd">    * else, request and arguments are passed to decorated view.</span>

<span class="sd">    Typical ``unauthorized`` view returns HTTP 401 status code and gives the</span>
<span class="sd">    user an opportunity to log in: access may be granted after</span>
<span class="sd">    authentication.</span>

<span class="sd">    Typical ``forbidden`` view returns HTTP 403 status code: with active</span>
<span class="sd">    user account, access is refused. As explained in rfc2616, 401 and 403</span>
<span class="sd">    status codes could be suitable.</span>

<span class="sd">    .. seealso::</span>

<span class="sd">       * http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4</span>
<span class="sd">       * https://en.wikipedia.org/wiki/List_of_HTTP_status_codes</span>

<span class="sd">    &quot;&quot;&quot;</span>
    <span class="k">def</span> <span class="nf">decorator</span><span class="p">(</span><span class="n">view_func</span><span class="p">):</span>
        <span class="nd">@wraps</span><span class="p">(</span><span class="n">view_func</span><span class="p">,</span> <span class="n">assigned</span><span class="o">=</span><span class="n">available_attrs</span><span class="p">(</span><span class="n">view_func</span><span class="p">))</span>
        <span class="k">def</span> <span class="nf">_wrapped_view</span><span class="p">(</span><span class="n">request</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
            <span class="k">if</span> <span class="ow">not</span> <span class="n">request</span><span class="o">.</span><span class="n">user</span><span class="o">.</span><span class="n">is_authenticated</span><span class="p">():</span>
                <span class="k">return</span> <span class="n">unauthorized</span><span class="p">(</span><span class="n">request</span><span class="p">)</span>
            <span class="k">if</span> <span class="ow">not</span> <span class="n">test_func</span><span class="p">(</span><span class="n">request</span><span class="o">.</span><span class="n">user</span><span class="p">):</span>
                <span class="k">return</span> <span class="n">forbidden</span><span class="p">(</span><span class="n">request</span><span class="p">)</span>
            <span class="k">return</span> <span class="n">view_func</span><span class="p">(</span><span class="n">request</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">)</span>
        <span class="k">return</span> <span class="n">_wrapped_view</span>
    <span class="k">return</span> <span class="n">decorator</span>
</pre></div>
<p>This decorator depends on some additional stuff:</p>
<div class="highlight"><pre><span class="kn">from</span> <span class="nn">django.http</span> <span class="kn">import</span> <span class="n">HttpResponse</span><span class="p">,</span> <span class="n">HttpResponseForbidden</span>
<span class="kn">from</span> <span class="nn">django.views.generic</span> <span class="kn">import</span> <span class="n">TemplateView</span>

<span class="k">class</span> <span class="nc">HttpResponseUnauthorized</span><span class="p">(</span><span class="n">HttpResponse</span><span class="p">):</span>
    <span class="n">status_code</span> <span class="o">=</span> <span class="mi">401</span>

<span class="k">class</span> <span class="nc">UnauthorizedView</span><span class="p">(</span><span class="n">TemplateView</span><span class="p">):</span>
    <span class="n">response_class</span> <span class="o">=</span> <span class="n">HttpResponseUnauthorized</span>
    <span class="n">template_name</span> <span class="o">=</span> <span class="s">'401.html'</span>

<span class="k">class</span> <span class="nc">ForbiddenView</span><span class="p">(</span><span class="n">TemplateView</span><span class="p">):</span>
    <span class="n">response_class</span> <span class="o">=</span> <span class="n">HttpResponseForbidden</span>
    <span class="n">template_name</span> <span class="o">=</span> <span class="s">'403.html'</span>
</pre></div>
<p>And, here is the test case!
It seems quite long, but isn't it readable? The whole test lives in the
TestCase: no external URLconf, no external views...</p>
<ul class="simple">
<li>First we setup fakes or mocks for all dependencies: <tt class="docutils literal">request.user</tt>,
<tt class="docutils literal">test_func</tt>, <tt class="docutils literal">unauthorized</tt> view, <tt class="docutils literal">forbidden</tt> view, and the view to be
decorated.</li>
<li>Then we declare a <tt class="docutils literal">run_decorated_view</tt> function to avoid repeating code.</li>
<li>Finally we test the 3 main situations: unauthorized, forbidden, authorized.</li>
</ul>
<div class="highlight"><pre><span class="kn">import</span> <span class="nn">unittest</span>

<span class="k">class</span> <span class="nc">AuthenticatedUserPassesTestTestCase</span><span class="p">(</span><span class="n">unittest</span><span class="o">.</span><span class="n">TestCase</span><span class="p">):</span>
    <span class="k">def</span> <span class="nf">setUp</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
        <span class="sd">&quot;&quot;&quot;Common setup: fake request, stub views, stub user test function.&quot;&quot;&quot;</span>
        <span class="nb">super</span><span class="p">(</span><span class="n">AuthenticatedUserPassesTestTestCase</span><span class="p">,</span> <span class="bp">self</span><span class="p">)</span><span class="o">.</span><span class="n">setUp</span><span class="p">()</span>
        <span class="c"># Fake request and its positional and keywords arguments.</span>
        <span class="bp">self</span><span class="o">.</span><span class="n">request</span> <span class="o">=</span> <span class="n">mock</span><span class="o">.</span><span class="n">MagicMock</span><span class="p">()</span>
        <span class="bp">self</span><span class="o">.</span><span class="n">request</span><span class="o">.</span><span class="n">user</span><span class="o">.</span><span class="n">is_authenticated</span> <span class="o">=</span> <span class="n">mock</span><span class="o">.</span><span class="n">MagicMock</span><span class="p">()</span>
        <span class="bp">self</span><span class="o">.</span><span class="n">request_args</span> <span class="o">=</span> <span class="p">[</span><span class="s">'fake_arg'</span><span class="p">]</span>
        <span class="bp">self</span><span class="o">.</span><span class="n">request_kwargs</span> <span class="o">=</span> <span class="p">{</span><span class="s">'fake'</span><span class="p">:</span> <span class="s">'kwarg'</span><span class="p">}</span>
        <span class="c"># Mock user test function.</span>
        <span class="bp">self</span><span class="o">.</span><span class="n">test_func</span> <span class="o">=</span> <span class="n">mock</span><span class="o">.</span><span class="n">MagicMock</span><span class="p">()</span>
        <span class="c"># Mock unauthorized and forbidden views.</span>
        <span class="bp">self</span><span class="o">.</span><span class="n">unauthorized_view</span> <span class="o">=</span> <span class="n">mock</span><span class="o">.</span><span class="n">MagicMock</span><span class="p">(</span>
            <span class="n">return_value</span><span class="o">=</span><span class="s">u&quot;401 - You may log in.&quot;</span><span class="p">)</span>
        <span class="bp">self</span><span class="o">.</span><span class="n">forbidden_view</span> <span class="o">=</span> <span class="n">mock</span><span class="o">.</span><span class="n">MagicMock</span><span class="p">(</span>
            <span class="n">return_value</span><span class="o">=</span><span class="s">u&quot;403 - Insufficient privileges.&quot;</span><span class="p">)</span>
        <span class="c"># Mock the view to decorate.</span>
        <span class="bp">self</span><span class="o">.</span><span class="n">authorized_view</span> <span class="o">=</span> <span class="n">mock</span><span class="o">.</span><span class="n">MagicMock</span><span class="p">(</span>
            <span class="n">return_value</span><span class="o">=</span><span class="s">u&quot;200 - Greetings, Professor Falken.&quot;</span><span class="p">)</span>

    <span class="k">def</span> <span class="nf">run_decorated_view</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">is_authenticated</span><span class="o">=</span><span class="bp">True</span><span class="p">,</span> <span class="n">user_passes_test</span><span class="o">=</span><span class="bp">True</span><span class="p">):</span>
        <span class="sd">&quot;&quot;&quot;Setup, decorate and call view, then return response.&quot;&quot;&quot;</span>
        <span class="c"># Custom setup.</span>
        <span class="bp">self</span><span class="o">.</span><span class="n">request</span><span class="o">.</span><span class="n">user</span><span class="o">.</span><span class="n">is_authenticated</span><span class="o">.</span><span class="n">return_value</span> <span class="o">=</span> <span class="n">is_authenticated</span>
        <span class="bp">self</span><span class="o">.</span><span class="n">test_func</span><span class="o">.</span><span class="n">return_value</span> <span class="o">=</span> <span class="n">user_passes_test</span>
        <span class="c"># Get decorator.</span>
        <span class="n">decorator</span> <span class="o">=</span> <span class="n">authenticated_user_passes_test</span><span class="p">(</span>
            <span class="bp">self</span><span class="o">.</span><span class="n">test_func</span><span class="p">,</span>
            <span class="n">unauthorized</span><span class="o">=</span><span class="bp">self</span><span class="o">.</span><span class="n">unauthorized_view</span><span class="p">,</span>
            <span class="n">forbidden</span><span class="o">=</span><span class="bp">self</span><span class="o">.</span><span class="n">forbidden_view</span><span class="p">)</span>
        <span class="c"># Decorate view.</span>
        <span class="n">decorated_view</span> <span class="o">=</span> <span class="n">decorator</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">authorized_view</span><span class="p">)</span>
        <span class="c"># Return response.</span>
        <span class="k">return</span> <span class="n">decorated_view</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">request</span><span class="p">,</span>
                              <span class="o">*</span><span class="bp">self</span><span class="o">.</span><span class="n">request_args</span><span class="p">,</span>
                              <span class="o">**</span><span class="bp">self</span><span class="o">.</span><span class="n">request_kwargs</span><span class="p">)</span>

    <span class="k">def</span> <span class="nf">test_unauthorized</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
        <span class="sd">&quot;&quot;&quot;authenticated_user_passes_test first tests user authentication.&quot;&quot;&quot;</span>
        <span class="n">response</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">run_decorated_view</span><span class="p">(</span><span class="n">is_authenticated</span><span class="o">=</span><span class="bp">False</span><span class="p">)</span>
        <span class="c"># Check: unauthorized view was called with request as unique positional</span>
        <span class="c"># argument.</span>
        <span class="bp">self</span><span class="o">.</span><span class="n">unauthorized_view</span><span class="o">.</span><span class="n">assert_called_once_with</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">request</span><span class="p">)</span>
        <span class="bp">self</span><span class="o">.</span><span class="n">assertEqual</span><span class="p">(</span><span class="n">response</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">unauthorized_view</span><span class="o">.</span><span class="n">return_value</span><span class="p">)</span>
        <span class="c"># Test func was not called.</span>
        <span class="bp">self</span><span class="o">.</span><span class="n">assertFalse</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">test_func</span><span class="o">.</span><span class="n">called</span><span class="p">)</span>
        <span class="c"># Of course, authorized and forbidden views were not called.</span>
        <span class="bp">self</span><span class="o">.</span><span class="n">assertFalse</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">authorized_view</span><span class="o">.</span><span class="n">called</span><span class="p">)</span>
        <span class="bp">self</span><span class="o">.</span><span class="n">assertFalse</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">forbidden_view</span><span class="o">.</span><span class="n">called</span><span class="p">)</span>

    <span class="k">def</span> <span class="nf">test_test_func_args</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
        <span class="sd">&quot;&quot;&quot;authenticated_user_passes_test passes user instance to test func.&quot;&quot;&quot;</span>
        <span class="bp">self</span><span class="o">.</span><span class="n">run_decorated_view</span><span class="p">(</span><span class="n">is_authenticated</span><span class="o">=</span><span class="bp">True</span><span class="p">)</span>
        <span class="c"># Check: test_func was called with one argument: user instance.</span>
        <span class="bp">self</span><span class="o">.</span><span class="n">test_func</span><span class="o">.</span><span class="n">assert_called_once_with</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">request</span><span class="o">.</span><span class="n">user</span><span class="p">)</span>

    <span class="k">def</span> <span class="nf">test_forbidden</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
        <span class="sd">&quot;&quot;&quot;authenticated_user_passes_test runs forbidden view if user fails.&quot;&quot;&quot;</span>
        <span class="n">response</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">run_decorated_view</span><span class="p">(</span><span class="n">is_authenticated</span><span class="o">=</span><span class="bp">True</span><span class="p">,</span>
                                           <span class="n">user_passes_test</span><span class="o">=</span><span class="bp">False</span><span class="p">)</span>
        <span class="c"># Check: forbidden view was called with request as unique positional</span>
        <span class="c"># argument.</span>
        <span class="bp">self</span><span class="o">.</span><span class="n">forbidden_view</span><span class="o">.</span><span class="n">assert_called_once_with</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">request</span><span class="p">)</span>
        <span class="bp">self</span><span class="o">.</span><span class="n">assertEqual</span><span class="p">(</span><span class="n">response</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">forbidden_view</span><span class="o">.</span><span class="n">return_value</span><span class="p">)</span>
        <span class="c"># Of course, authorized and unauthorized views were not triggered.</span>
        <span class="bp">self</span><span class="o">.</span><span class="n">assertFalse</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">authorized_view</span><span class="o">.</span><span class="n">called</span><span class="p">)</span>
        <span class="bp">self</span><span class="o">.</span><span class="n">assertFalse</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">unauthorized_view</span><span class="o">.</span><span class="n">called</span><span class="p">)</span>

    <span class="k">def</span> <span class="nf">test_authorized</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
        <span class="sd">&quot;&quot;&quot;authenticated_user_passes_test runs view if user passes test.&quot;&quot;&quot;</span>
        <span class="n">response</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">run_decorated_view</span><span class="p">(</span><span class="n">is_authenticated</span><span class="o">=</span><span class="bp">True</span><span class="p">,</span>
                                           <span class="n">user_passes_test</span><span class="o">=</span><span class="bp">True</span><span class="p">)</span>
        <span class="c"># Check: decorated view has been called, request and other arguments</span>
        <span class="c"># were proxied as is, response was not altered.</span>
        <span class="bp">self</span><span class="o">.</span><span class="n">authorized_view</span><span class="o">.</span><span class="n">assert_called_once_with</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">request</span><span class="p">,</span>
                                                     <span class="o">*</span><span class="bp">self</span><span class="o">.</span><span class="n">request_args</span><span class="p">,</span>
                                                     <span class="o">**</span><span class="bp">self</span><span class="o">.</span><span class="n">request_kwargs</span><span class="p">)</span>
        <span class="bp">self</span><span class="o">.</span><span class="n">assertEqual</span><span class="p">(</span><span class="n">response</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">authorized_view</span><span class="o">.</span><span class="n">return_value</span><span class="p">)</span>
        <span class="c"># Of course, forbidden and unauthorized views were not triggered.</span>
        <span class="bp">self</span><span class="o">.</span><span class="n">assertFalse</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">forbidden_view</span><span class="o">.</span><span class="n">called</span><span class="p">)</span>
        <span class="bp">self</span><span class="o">.</span><span class="n">assertFalse</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">unauthorized_view</span><span class="o">.</span><span class="n">called</span><span class="p">)</span>
</pre></div>
<p>Would you trust the <tt class="docutils literal">authenticated_user_passes_test</tt> decorator?</p>
</div>
<div class="section" id="references">
<h2>References</h2>
<table class="docutils footnote" frame="void" id="id1" rules="none">
<colgroup><col class="label" /><col /></colgroup>
<tbody valign="top">
<tr><td class="label"><a class="fn-backref" href="http://tech.novapost.fr/feeds/Python.atom.xml#id2">[1]</a></td><td><a class="reference external" href="https://pypi.python.org/pypi/mock">https://pypi.python.org/pypi/mock</a></td></tr>
</tbody>
</table>
<table class="docutils footnote" frame="void" id="id3" rules="none">
<colgroup><col class="label" /><col /></colgroup>
<tbody valign="top">
<tr><td class="label">[2]</td><td><em>(<a class="fn-backref" href="http://tech.novapost.fr/feeds/Python.atom.xml#id4">1</a>, <a class="fn-backref" href="http://tech.novapost.fr/feeds/Python.atom.xml#id5">2</a>)</em> <a class="reference external" href="http://docs.python.org/3/library/unittest.mock.html">http://docs.python.org/3/library/unittest.mock.html</a></td></tr>
</tbody>
</table>
<table class="docutils footnote" frame="void" id="id6" rules="none">
<colgroup><col class="label" /><col /></colgroup>
<tbody valign="top">
<tr><td class="label"><a class="fn-backref" href="http://tech.novapost.fr/feeds/Python.atom.xml#id7">[3]</a></td><td><a class="reference external" href="https://docs.djangoproject.com/en/1.5/topics/testing/advanced/#django.test.client.RequestFactory">https://docs.djangoproject.com/en/1.5/topics/testing/advanced/#django.test.client.RequestFactory</a></td></tr>
</tbody>
</table>
<table class="docutils footnote" frame="void" id="id8" rules="none">
<colgroup><col class="label" /><col /></colgroup>
<tbody valign="top">
<tr><td class="label"><a class="fn-backref" href="http://tech.novapost.fr/feeds/Python.atom.xml#id9">[4]</a></td><td><a class="reference external" href="https://docs.djangoproject.com/en/1.5/ref/request-response/#django.http.HttpRequest">https://docs.djangoproject.com/en/1.5/ref/request-response/#django.http.HttpRequest</a></td></tr>
</tbody>
</table>
</div>]]></description>
    <link><![CDATA[http://tech.novapost.fr/django-testing-view-decorators-en.html]]></link>
    <pubDate>2013-03-14 09:00:00</pubDate>
    <category>django</category>
    <category>testing</category>
  </item>
  <item>
    <title><![CDATA[[novapost] A Django testing challenge]]></title>
    <description><![CDATA[<p>Here at Novapost, we have quite large Django projects. Big projects mean big
maintenance. Hopefully, those projects are covered by tests. Thousands of
tests. We are quite proud of this fact. Tests save our lives. But we also have
some worries and need to improve... Here is our testing challenge!</p>
<div class="section" id="some-issues">
<h2>Some issues...</h2>
<p>Here are some problems we encounter...</p>
<div class="section" id="performance">
<h3>Performance</h3>
<p>In the biggest project, our continuous integration service can't run a full
test suite in less than one hour. That's embarrassing, because we can't wait
for global test results before we commit.</p>
<p>We'd like a collection of tests that run fast and give us the status of most of
the project. So that we can run it before we commit. Then the continuous
integration service would cover the rest.</p>
</div>
<div class="section" id="maintenance">
<h3>Maintenance</h3>
<p>When we change a feature, it sometimes has side effects on some tests which
seem unrelated. It looks like our tests involve much more than necessary.
Not only it affects performance, but it also affects maintenance: sometimes
we know something is wrong because tests fail, but we cannot tell exactly
what's wrong. And finally, we have to adapt several tests for only one feature.</p>
<p>We'd like tests to focus on small units, so that, when something goes wrong,
tests help us figure out what's exactly wrong. And we'd like to change only
what's really related.</p>
</div>
<div class="section" id="tests-pass-deployment-fail">
<h3>Tests pass, deployment fail</h3>
<p>Sometimes tests are not enough: we fix things, but we can't tell if the fix
works after an upgrade. As an example, changes that involve settings are hard
to cover with tests: once the fix is committed and pushed, the story cannot be
closed before we make sure the configuration has been updated and everything is
ok on servers. We are doing some manual checks...</p>
<p>We'd like to involve more into deployment, supervision and monitoring, so that
we can automate some checks related to the issues or features we develop.</p>
</div>
</div>
<div class="section" id="some-clues">
<h2>Some clues...</h2>
<p>We already have some ideas about what's wrong with our testing strategy. Here
are some hypotheses:</p>
<ul class="simple">
<li>one cause of those long test suites is the heavy use of <tt class="docutils literal">self.client.get()</tt>
aka functional/system test for the unaware ;</li>
<li>we could mock/fake more things ;</li>
<li>we could separate tests that run fast from tests that run slow ;</li>
<li>we could split our big all-in-one test suite into smaller parts: unit tests,
functional tests, integration tests, health checks...</li>
</ul>
</div>
<div class="section" id="a-challenge">
<h2>A challenge</h2>
<p>We are aware of some issues with our testing strategy and have some ideas...
<strong>Let's improve!</strong></p>
<p>We started teamwork as a challenge:</p>
<ul class="simple">
<li>try various improvements when we implement features or fix bugs ;</li>
<li>adopt improvements that work, measure how it improves things ;</li>
<li>give feedback on the blog!</li>
</ul>
<p>We are to share experience on this blog, through several articles with the
<a class="reference external" href="http://tech.novapost.fr/tag/testing.html">testing tag</a>.</p>
<p>Here are articles we already posted:</p>
<ul class="simple">
<li><a class="reference external" href="http://tech.novapost.fr/feeds/|filename|django-unit-test-your-views.rst">unit test your Django views</a></li>
<li><a class="reference external" href="http://tech.novapost.fr/feeds/|filename|django-testing-view-decorators.rst">testing Django view decorators</a></li>
<li>... to be continued...</li>
</ul>
</div>
<div class="section" id="see-also">
<h2>See also</h2>
<ul class="simple">
<li>The <a class="reference external" href="https://www.youtube.com/watch?v=RAxiiRPHS9k">&quot;Fast test, slow test&quot; presentation by Gary Bernhardt</a> is a must see!</li>
</ul>
</div>]]></description>
    <link><![CDATA[http://tech.novapost.fr/django-testing-challenge-en.html]]></link>
    <pubDate>2013-03-12 11:00:00</pubDate>
    <category>django</category>
    <category>testing</category>
  </item>
  <item>
    <title><![CDATA[[Inspyration] Les décorateurs]]></title>
    <description><![CDATA[Lorsqu'on les croise pour la première fois, les décorateurs revêtent cet aspect magique. On ne sait pas trop comment çà fonctionne, mais on trouve çà classe. C'est facile à utiliser, çà demande peut d'efforts et c'est très puissant. Que demande le peuple ? Par contre, lorsque l'on souhaite en créer, c'est une autre paire de manches. Comme dirait un coach de rugby : "On va tout mettre sur la table et repartir des fondamentaux".]]></description>
    <link><![CDATA[http://www.inspyration.org/tutoriels/les-decorateurs]]></link>
    <pubDate>2013-03-10 23:00:00</pubDate>
    <category>Python</category>
    <category>tutoriel</category>
    <category>Décorateur</category>
    <category>Motif de conception</category>
  </item>
  <item>
    <title><![CDATA[[novapost] Unit test your Django views]]></title>
    <description><![CDATA[<p>How to test views of a Django application?</p>
<p>Django's builtin test client is not suitable for unit testing! It performs
system tests: it handles your views as a black box in a project's environment.</p>
<p>This article provides a recipe for developers to replace Django's builtin test
client by smaller, fine-grained, view-centric tests.</p>
<div class="section" id="self-client-get-system-tests-for-the-unaware">
<h2>self.client.get(): system tests for the unaware</h2>
<p>Here are some reasons why Django's builtin client performs system tests:</p>
<ul class="simple">
<li>it resolves URLs,</li>
<li>it traverses middlewares,</li>
<li>it traverses decorators,</li>
<li>it uses template context processors,</li>
<li>it relies on settings,</li>
<li>... and perhaps more. Who knows? Do you really want to know?</li>
</ul>
<p>All the stuff above is not the view, it is the environment surrounding the
view.</p>
<p>It means that, <strong>by using the test client, you don't test the view itself, but
the system the view is part of</strong>. And the environment is quite hard (and
boring) to control.</p>
<p>Here, we want to focus on the view, so let's emancipate from all those third
party mechanisms.</p>
</div>
<div class="section" id="testing-view-functions">
<h2>Testing view functions</h2>
<p>Let's consider this simple view:</p>
<div class="highlight"><pre><span class="kn">from</span> <span class="nn">django.http</span> <span class="kn">import</span> <span class="n">Http404</span><span class="p">,</span> <span class="n">HttpResponse</span>

<span class="k">def</span> <span class="nf">hello</span><span class="p">(</span><span class="n">request</span><span class="p">,</span> <span class="n">name</span><span class="p">):</span>
    <span class="k">if</span> <span class="n">name</span> <span class="o">==</span> <span class="s">u'Waldo'</span>
        <span class="k">raise</span> <span class="n">Http404</span><span class="p">(</span><span class="s">&quot;Where's Waldo?&quot;</span><span class="p">)</span>
    <span class="k">return</span> <span class="n">HttpResponse</span><span class="p">(</span><span class="s">u'Hello {name}!'</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">name</span><span class="o">=</span><span class="n">name</span><span class="p">))</span>
</pre></div>
<p>Then test it:</p>
<div class="highlight"><pre><span class="kn">import</span> <span class="nn">unittest</span>

<span class="k">class</span> <span class="nc">HelloTestCase</span><span class="p">(</span><span class="n">unittest</span><span class="o">.</span><span class="n">TestCase</span><span class="p">):</span>
    <span class="k">def</span> <span class="nf">test_get</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
        <span class="sd">&quot;&quot;&quot;hello view actually tells 'Hello'.&quot;&quot;&quot;</span>
        <span class="c"># Setup.</span>
        <span class="n">request</span> <span class="o">=</span> <span class="s">'fake request'</span>
        <span class="n">name</span> <span class="o">=</span> <span class="s">'world'</span>
        <span class="c"># Run.</span>
        <span class="n">response</span> <span class="o">=</span> <span class="n">hello</span><span class="p">(</span><span class="n">request</span><span class="p">,</span> <span class="n">name</span><span class="p">)</span>
        <span class="c"># Check.</span>
        <span class="bp">self</span><span class="o">.</span><span class="n">assertEqual</span><span class="p">(</span><span class="n">response</span><span class="o">.</span><span class="n">status_code</span><span class="p">,</span> <span class="mi">200</span><span class="p">)</span>
        <span class="bp">self</span><span class="o">.</span><span class="n">assertEqual</span><span class="p">(</span><span class="n">response</span><span class="o">.</span><span class="n">content</span><span class="p">,</span> <span class="s">u'Hello world!'</span><span class="p">)</span>

    <span class="k">def</span> <span class="nf">test_waldo</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
        <span class="sd">&quot;&quot;&quot;Cannot find Waldo to tell him 'Hello'.&quot;&quot;&quot;</span>
        <span class="c"># Setup.</span>
        <span class="n">request</span> <span class="o">=</span> <span class="s">'fake request'</span>
        <span class="n">name</span> <span class="o">=</span> <span class="s">'Waldo'</span>
        <span class="c"># Run and check.</span>
        <span class="bp">self</span><span class="o">.</span><span class="n">assertRaises</span><span class="p">(</span><span class="n">Http404</span><span class="p">,</span> <span class="n">hello</span><span class="p">,</span> <span class="n">request</span><span class="p">,</span> <span class="n">name</span><span class="p">)</span>
</pre></div>
<p>Pretty simple isn't it?</p>
<p>Really, you don't need Django's builtin test client to write such tests!</p>
</div>
<div class="section" id="use-unittest-or-simpletestcase-wherever-you-can">
<h2>Use unittest or SimpleTestCase wherever you can</h2>
<p>In the <a class="reference external" href="http://tech.novapost.fr/feeds/#testing-view-functions">example above</a>, we didn't hit the database,
so there were no reasons to use <a class="reference external" href="https://docs.djangoproject.com/en/1.5/topics/testing/overview/#transactiontestcase">django.test.TransactionTestCase</a> <a class="footnote-reference" href="http://tech.novapost.fr/feeds/Python.atom.xml#id1" id="id2">[1]</a> or
derivatives.</p>
<p>With such a configuration, tests run really fast!</p>
<div class="note">
<p class="first admonition-title">Note</p>
<p class="last">Performance is another reason you should avoid Django's builtin test client.
But that's another story.</p>
</div>
<p>Wherever you can, use unittest, or <a class="reference external" href="https://docs.djangoproject.com/en/1.5/topics/testing/overview/#simpletestcase">django.test.SimpleTestCase</a> <a class="footnote-reference" href="http://tech.novapost.fr/feeds/Python.atom.xml#id3" id="id4">[2]</a>.</p>
</div>
<div class="section" id="don-t-decorate-views-in-place">
<h2>Don't decorate views in place</h2>
<p>The <a class="reference external" href="http://tech.novapost.fr/feeds/Python.atom.xml#testing-view-functions">&quot;hello&quot; example above</a> would have been broken if
the view were decorated in place. As an example:</p>
<div class="highlight"><pre><span class="kn">from</span> <span class="nn">django.http</span> <span class="kn">import</span> <span class="n">Http404</span><span class="p">,</span> <span class="n">HttpResponse</span>
<span class="kn">from</span> <span class="nn">django.contrib.auth.decorators</span> <span class="kn">import</span> <span class="n">login_required</span>

<span class="nd">@login_required</span>
<span class="k">def</span> <span class="nf">hello</span><span class="p">(</span><span class="n">request</span><span class="p">,</span> <span class="n">name</span><span class="p">):</span>
    <span class="k">if</span> <span class="n">name</span> <span class="o">==</span> <span class="s">u'Waldo'</span>
        <span class="k">raise</span> <span class="n">Http404</span><span class="p">(</span><span class="s">&quot;Where's Waldo?&quot;</span><span class="p">)</span>
    <span class="k">return</span> <span class="n">HttpResponse</span><span class="p">(</span><span class="s">u'Hello {name}!'</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">name</span><span class="o">=</span><span class="n">name</span><span class="p">))</span>
</pre></div>
<p>How can we test <tt class="docutils literal">hello()</tt> view now?</p>
<p>We would have to perform (mock) a login, and we would have to check the
response with or without authentication. As a consequence, our tests would
become longer, less readable, less efficient... Moreover, what if the <tt class="docutils literal">login</tt>
decorator has bugs or changes? It would break <tt class="docutils literal">hello</tt>'s tests even if
<tt class="docutils literal">hello</tt> itself doesn't change. How bad!</p>
<p>So, <strong>don't decorate views in place</strong>.</p>
<p>Instead:</p>
<ul class="simple">
<li>decorate views somewhere related to URLconfs (urls.py), not related to views.</li>
<li>have specific tests for decorators, i.e. validate <tt class="docutils literal">login_required</tt> works.</li>
<li>have specific tests for URLconfs, i.e. validate <tt class="docutils literal">login_required</tt> is applied
to <tt class="docutils literal">hello</tt> in project's configuration (this is a system test).</li>
</ul>
</div>
<div class="section" id="use-request-factories">
<h2>Use request factories</h2>
<p><a class="reference external" href="https://github.com/django/django/blob/56e54727661bc34bd2b6f9fa6a75f5370149256e/django/test/client.py#L345">Django's builtin test client is a special kind of request factory</a> <a class="footnote-reference" href="http://tech.novapost.fr/feeds/Python.atom.xml#id5" id="id6">[3]</a>, which
uses URL resolution to trigger the views (deep inside the system).
Now we have isolated views from system. But a view still takes a request as
argument. How to get a request?</p>
<p>In the <a class="reference external" href="http://tech.novapost.fr/feeds/Python.atom.xml#testing-view-functions">function-based example above</a>, we used a
completely fake request.
But sometimes you can't do that and need a <tt class="docutils literal">HttpRequest</tt>.</p>
<p>Django provides <a class="reference external" href="https://docs.djangoproject.com/en/1.5/topics/testing/advanced/#django.test.client.RequestFactory">django.test.RequestFactory</a> <a class="footnote-reference" href="http://tech.novapost.fr/feeds/Python.atom.xml#id7" id="id8">[4]</a> to mock requests.</p>
<p>With a request factory, you get a request instance you can pass as argument
to views' methods such as <tt class="docutils literal">dispatch()</tt>.</p>
<div class="highlight"><pre><span class="kn">from</span> <span class="nn">django.test</span> <span class="kn">import</span> <span class="n">RequestFactory</span>

<span class="n">request_factory</span> <span class="o">=</span> <span class="n">RequestFactory</span><span class="p">()</span>
<span class="n">request</span> <span class="o">=</span> <span class="n">request_factory</span><span class="o">.</span><span class="n">post</span><span class="p">(</span><span class="s">'/fake-path'</span><span class="p">,</span> <span class="n">data</span><span class="o">=</span><span class="p">{</span><span class="s">'name'</span><span class="p">:</span> <span class="s">u'Waldo'</span><span class="p">})</span>
</pre></div>
<div class="note">
<p class="first admonition-title">Note</p>
<p>Some notes about request factories, which could make a full article...</p>
<ul class="last simple">
<li>Django's builtin RequestFactory requires one positional argument: path.
But, in the scope of tests of this article, we really don't care about
the path. The path is mandatory for the test client to resolve URLs...
So, unless your view actually uses the <tt class="docutils literal">path</tt> argument, you can safely
use a fake value.</li>
<li>If your view uses the messages framework, you'll need to setup (or mock)
<tt class="docutils literal">request._messages</tt>. Notice that's a feature, since messages should
be tested too ;)</li>
<li>Idem about session: you may need to mock <tt class="docutils literal">request.session</tt> if your
view depends on the session.</li>
<li>Yes, you are getting aware of your view's dependencies :)</li>
</ul>
</div>
</div>
<div class="section" id="testing-class-based-views">
<h2>Testing class-based views</h2>
<p>Once we got rid of Django's builtin test client, we can consider views
themselves. How do they look like?</p>
<p>Function-based views look like black boxes: things that take a request and
return a response. No way to test internals.</p>
<p>With class-based views, we have various methods and attributes. So we can write
fine-grained tests!</p>
<p>The idea here is to <strong>test every custom method or attribute of the class-based
views you write</strong>.</p>
<p>Let's consider the following view:</p>
<div class="highlight"><pre><span class="k">class</span> <span class="nc">HelloView</span><span class="p">(</span><span class="n">TemplateView</span><span class="p">):</span>
    <span class="k">def</span> <span class="nf">get_context_data</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
        <span class="n">kwargs</span> <span class="o">=</span> <span class="nb">super</span><span class="p">(</span><span class="n">HelloView</span><span class="p">,</span> <span class="bp">self</span><span class="p">)</span><span class="o">.</span><span class="n">get_context_data</span><span class="p">(</span><span class="o">**</span><span class="n">kwargs</span><span class="p">)</span>
        <span class="n">kwargs</span><span class="o">.</span><span class="n">update</span><span class="p">(</span><span class="s">'name'</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">kwargs</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s">'name'</span><span class="p">))</span>
        <span class="k">return</span> <span class="n">kwargs</span>
</pre></div>
<p>And let's consider we'd like to reproduce this URLconf scenario:</p>
<ul class="simple">
<li>view: <tt class="docutils literal">hello = <span class="pre">HelloView.as_view(template_name='hello.html')</span></tt></li>
<li>URL: <tt class="docutils literal"><span class="pre">url(r'(?P&lt;name&gt;\w+)',</span> hello)</tt></li>
</ul>
<div class="section" id="as-view-is-not-enough">
<h3><tt class="docutils literal">as_view()</tt> is not enough</h3>
<p>Testing class-based views using <tt class="docutils literal">as_view()</tt> and <tt class="docutils literal">RequestFactory</tt> is now
described in Django's documentation along with <a class="reference external" href="https://docs.djangoproject.com/en/1.5/topics/testing/advanced/#django.test.client.RequestFactory">django.test.RequestFactory</a> <a class="footnote-reference" href="http://tech.novapost.fr/feeds/Python.atom.xml#id7" id="id9">[4]</a>:</p>
<div class="highlight"><pre><span class="kn">import</span> <span class="nn">unittest</span>
<span class="kn">from</span> <span class="nn">django.test</span> <span class="kn">import</span> <span class="n">RequestFactory</span>

<span class="k">class</span> <span class="nc">HelloViewTestCase</span><span class="p">(</span><span class="n">unittest</span><span class="o">.</span><span class="n">TestCase</span><span class="p">):</span>
    <span class="k">def</span> <span class="nf">test_get</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
        <span class="sd">&quot;&quot;&quot;HelloView.get() sets 'name' in response context.&quot;&quot;&quot;</span>
        <span class="c"># Setup name.</span>
        <span class="n">name</span> <span class="o">=</span> <span class="s">'peter'</span>
        <span class="c"># Setup request and view.</span>
        <span class="n">request</span> <span class="o">=</span> <span class="n">RequestFactory</span><span class="p">()</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s">'/fake-path'</span><span class="p">)</span>
        <span class="n">view</span> <span class="o">=</span> <span class="n">HelloView</span><span class="o">.</span><span class="n">as_view</span><span class="p">(</span><span class="n">template_name</span><span class="o">=</span><span class="s">'hello.html'</span><span class="p">)</span>
        <span class="c"># Run.</span>
        <span class="n">response</span> <span class="o">=</span> <span class="n">view</span><span class="p">(</span><span class="n">request</span><span class="p">,</span> <span class="n">name</span><span class="o">=</span><span class="n">name</span><span class="p">)</span>
        <span class="c"># Check.</span>
        <span class="bp">self</span><span class="o">.</span><span class="n">assertEqual</span><span class="p">(</span><span class="n">response</span><span class="o">.</span><span class="n">status_code</span><span class="p">,</span> <span class="mi">200</span><span class="p">)</span>
        <span class="bp">self</span><span class="o">.</span><span class="n">assertEqual</span><span class="p">(</span><span class="n">response</span><span class="o">.</span><span class="n">template_name</span><span class="p">[</span><span class="mi">0</span><span class="p">],</span> <span class="s">'home.html'</span><span class="p">)</span>
        <span class="bp">self</span><span class="o">.</span><span class="n">assertEqual</span><span class="p">(</span><span class="n">response</span><span class="o">.</span><span class="n">context_data</span><span class="p">[</span><span class="s">'name'</span><span class="p">],</span> <span class="n">name</span><span class="p">)</span>
</pre></div>
<p>Ok, it works. But, in the <tt class="docutils literal">HelloView</tt> above, I just overrid the
<tt class="docutils literal">get_context_data()</tt> method. So I'd like to test only that. I mean, status
code and template name are features inherited from TemplateView, and they are
covered by TemplateView's test suite.</p>
<p>We can't use <tt class="docutils literal">as_view()</tt> to perform fine-grained testing.</p>
<p>One issue with <tt class="docutils literal">as_view()</tt> is that it returns a function, not an instance
of the view class. And this callable is a proxy to view's <tt class="docutils literal">dispatch()</tt>, which
involves almost all view's methods, depending on the arguments.</p>
<p>Using <tt class="docutils literal">as_view()</tt> in tests is the same as having a function-based view.
You don't really take advantage of the class-based view.</p>
<p>Alright, let's get rid of <tt class="docutils literal">as_view()</tt> and focus on <tt class="docutils literal">get_context_data()</tt>...</p>
</div>
<div class="section" id="mimic-as-view">
<h3>Mimic <tt class="docutils literal">as_view()</tt></h3>
<p>Here is a simple replacement for <tt class="docutils literal">as_view()</tt>:</p>
<div class="highlight"><pre><span class="k">def</span> <span class="nf">setup_view</span><span class="p">(</span><span class="n">view</span><span class="p">,</span> <span class="n">request</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
    <span class="sd">&quot;&quot;&quot;Mimic as_view() returned callable, but returns view instance.</span>

<span class="sd">    args and kwargs are the same you would pass to ``reverse()``</span>

<span class="sd">    &quot;&quot;&quot;</span>
    <span class="n">view</span><span class="o">.</span><span class="n">request</span> <span class="o">=</span> <span class="n">request</span>
    <span class="n">view</span><span class="o">.</span><span class="n">args</span> <span class="o">=</span> <span class="n">args</span>
    <span class="n">view</span><span class="o">.</span><span class="n">kwargs</span> <span class="o">=</span> <span class="n">kwargs</span>
    <span class="k">return</span> <span class="n">view</span>
</pre></div>
<p>Here is how to use it in a test:</p>
<div class="highlight"><pre><span class="kn">import</span> <span class="nn">unittest</span>
<span class="kn">from</span> <span class="nn">django.test</span> <span class="kn">import</span> <span class="n">RequestFactory</span>

<span class="k">class</span> <span class="nc">HelloViewTestCase</span><span class="p">(</span><span class="n">unittest</span><span class="o">.</span><span class="n">TestCase</span><span class="p">):</span>
    <span class="k">def</span> <span class="nf">test_context_data</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
        <span class="sd">&quot;&quot;&quot;HelloView.get_context_data() sets 'name' in context.&quot;&quot;&quot;</span>
        <span class="c"># Setup name.</span>
        <span class="n">name</span> <span class="o">=</span> <span class="s">'django'</span>
        <span class="c"># Setup request and view.</span>
        <span class="n">request</span> <span class="o">=</span> <span class="n">RequestFactory</span><span class="p">()</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s">'/fake-path'</span><span class="p">)</span>
        <span class="n">view</span> <span class="o">=</span> <span class="n">HelloView</span><span class="p">(</span><span class="n">template_name</span><span class="o">=</span><span class="s">'hello.html'</span><span class="p">)</span>
        <span class="n">view</span> <span class="o">=</span> <span class="n">setup_view</span><span class="p">(</span><span class="n">view</span><span class="p">,</span> <span class="n">request</span><span class="p">,</span> <span class="n">name</span><span class="o">=</span><span class="n">name</span><span class="p">)</span>
        <span class="c"># Run.</span>
        <span class="n">context</span> <span class="o">=</span> <span class="n">view</span><span class="o">.</span><span class="n">get_context_data</span><span class="p">()</span>
        <span class="c"># Check.</span>
        <span class="bp">self</span><span class="o">.</span><span class="n">assertEqual</span><span class="p">(</span><span class="n">context</span><span class="p">[</span><span class="s">'name'</span><span class="p">],</span> <span class="n">name</span><span class="p">)</span>
</pre></div>
<p>That's all. What happened?</p>
<ul class="simple">
<li>Just tested the <tt class="docutils literal">get_context_data</tt> method which we overrid. Other methods
inherited from <tt class="docutils literal">TemplateView</tt> are covered by <tt class="docutils literal">TemplateView</tt> test suite.</li>
<li>We used unittest since there is no transaction involved.</li>
</ul>
</div>
<div class="section" id="the-fairy-as-view-and-the-ugly-dispatch">
<h3>The fairy <tt class="docutils literal">as_view()</tt> and the ugly <tt class="docutils literal">dispatch()</tt></h3>
<p>Let's end with a story about <tt class="docutils literal">as_view()</tt> magic.</p>
<p>Using as_view() is quite elegant:</p>
<div class="highlight"><pre><span class="n">request</span> <span class="o">=</span> <span class="n">RequestFactory</span><span class="p">()</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s">'/fake-path'</span><span class="p">)</span>
<span class="n">view</span> <span class="o">=</span> <span class="n">HelloView</span><span class="o">.</span><span class="n">as_view</span><span class="p">(</span><span class="n">template_name</span><span class="o">=</span><span class="s">'hello.html'</span><span class="p">)</span>
<span class="n">response</span> <span class="o">=</span> <span class="n">view</span><span class="p">(</span><span class="n">request</span><span class="p">,</span> <span class="n">name</span><span class="o">=</span><span class="s">'bob'</span><span class="p">)</span>
</pre></div>
<p>Using <tt class="docutils literal">dispatch()</tt> is ugly:</p>
<div class="highlight"><pre><span class="n">request</span> <span class="o">=</span> <span class="n">RequestFactory</span><span class="p">()</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s">'/fake-path'</span><span class="p">)</span>
<span class="n">view</span> <span class="o">=</span> <span class="n">HelloView</span><span class="p">(</span><span class="n">template_name</span><span class="o">=</span><span class="s">'hello.html'</span><span class="p">)</span>
<span class="n">view</span> <span class="o">=</span> <span class="n">setup_view</span><span class="p">(</span><span class="n">view</span><span class="p">,</span> <span class="n">request</span><span class="p">,</span> <span class="n">name</span><span class="o">=</span><span class="s">'bob'</span><span class="p">)</span>
<span class="n">response</span> <span class="o">=</span> <span class="n">view</span><span class="o">.</span><span class="n">dispatch</span><span class="p">(</span><span class="n">view</span><span class="o">.</span><span class="n">request</span><span class="p">,</span> <span class="o">*</span><span class="n">view</span><span class="o">.</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">view</span><span class="o">.</span><span class="n">kwargs</span><span class="p">)</span>
</pre></div>
<p>Got it? <tt class="docutils literal">dispatch()</tt> receives arguments the instance already knows...</p>
<p>Diving into fine-grained tests on Django-style class-based views may awake
trolls. Billy-Thread-Safe, Kate-Instance and Frank-Class-Attribute may join the
party soon ;)</p>
<p>In fact, it looks like Django's class-based views haven't been designed to be
fine-grained tested.</p>
<p>If your are curious, have a look on Django's tests...</p>
<ul>
<li><p class="first">At
<a class="reference external" href="https://github.com/django/django/blob/1.5/tests/regressiontests/generic_views/base.py#L278">https://github.com/django/django/blob/1.5/tests/regressiontests/generic_views/base.py#L278</a>
nothing proves we are testing a <tt class="docutils literal">TemplateView</tt>. It relies on the URLconf.
Calling view's <tt class="docutils literal">get_context_data()</tt> may have been more efficient and
readable.</p>
</li>
<li><p class="first">At
<a class="reference external" href="https://github.com/django/django/blob/1.5/tests/regressiontests/generic_views/base.py#L67">https://github.com/django/django/blob/1.5/tests/regressiontests/generic_views/base.py#L67</a>
are we really testing the queryset? It seems we are testing the queryset and
the context data and the status code and the template name and the URL
configuration and... all in a row.</p>
<p>There could be one test around default <tt class="docutils literal">get_queryset()</tt> to check that it
returns <tt class="docutils literal">Author.objects.all()</tt>. Then another test around
<tt class="docutils literal">get_context_data()</tt> to check that the queryset (a fake queryset that
doesn't hit the database) is registered in context.</p>
</li>
<li><p class="first">and soooooo many tests that handle views (or system) as black boxes...</p>
</li>
</ul>
</div>
</div>
<div class="section" id="what-s-next">
<h2>What's next</h2>
<p>Since you test your views as isolated items, you have to test everything else:</p>
<ul class="simple">
<li>middlewares,</li>
<li><a class="reference external" href="http://tech.novapost.fr/feeds/|filename|django-testing-view-decorators.rst">decorators</a>,</li>
<li>context processors,</li>
<li>models...</li>
</ul>
<p>And you can fake/mock many things inside tests of views, so that you don't rely
on database, settings, ...</p>
</div>
<div class="section" id="references">
<h2>References</h2>
<table class="docutils footnote" frame="void" id="id1" rules="none">
<colgroup><col class="label" /><col /></colgroup>
<tbody valign="top">
<tr><td class="label"><a class="fn-backref" href="http://tech.novapost.fr/feeds/Python.atom.xml#id2">[1]</a></td><td><a class="reference external" href="https://docs.djangoproject.com/en/1.5/topics/testing/overview/#transactiontestcase">https://docs.djangoproject.com/en/1.5/topics/testing/overview/#transactiontestcase</a></td></tr>
</tbody>
</table>
<table class="docutils footnote" frame="void" id="id3" rules="none">
<colgroup><col class="label" /><col /></colgroup>
<tbody valign="top">
<tr><td class="label"><a class="fn-backref" href="http://tech.novapost.fr/feeds/Python.atom.xml#id4">[2]</a></td><td><a class="reference external" href="https://docs.djangoproject.com/en/1.5/topics/testing/overview/#simpletestcase">https://docs.djangoproject.com/en/1.5/topics/testing/overview/#simpletestcase</a></td></tr>
</tbody>
</table>
<table class="docutils footnote" frame="void" id="id5" rules="none">
<colgroup><col class="label" /><col /></colgroup>
<tbody valign="top">
<tr><td class="label"><a class="fn-backref" href="http://tech.novapost.fr/feeds/Python.atom.xml#id6">[3]</a></td><td><a class="reference external" href="https://github.com/django/django/blob/56e54727661bc34bd2b6f9fa6a75f5370149256e/django/test/client.py#L345">https://github.com/django/django/blob/56e54727661bc34bd2b6f9fa6a75f5370149256e/django/test/client.py#L345</a></td></tr>
</tbody>
</table>
<table class="docutils footnote" frame="void" id="id7" rules="none">
<colgroup><col class="label" /><col /></colgroup>
<tbody valign="top">
<tr><td class="label">[4]</td><td><em>(<a class="fn-backref" href="http://tech.novapost.fr/feeds/Python.atom.xml#id8">1</a>, <a class="fn-backref" href="http://tech.novapost.fr/feeds/Python.atom.xml#id9">2</a>)</em> <a class="reference external" href="https://docs.djangoproject.com/en/1.5/topics/testing/advanced/#django.test.client.RequestFactory">https://docs.djangoproject.com/en/1.5/topics/testing/advanced/#django.test.client.RequestFactory</a></td></tr>
</tbody>
</table>
</div>]]></description>
    <link><![CDATA[http://tech.novapost.fr/django-unit-test-your-views-en.html]]></link>
    <pubDate>2013-03-07 09:00:00</pubDate>
    <category>django</category>
    <category>testing</category>
  </item>
  <item>
    <title><![CDATA[[cubicweb] data.bnf.fr gets the Stanford Prize for Innovation in Research Libraries]]></title>
    <description><![CDATA[<p><a class="reference" href="http://data.bnf.fr">data.bnf.fr</a> and <a class="reference" href="http://gallica.bnf.fr">Gallica</a> just got awarded the <a class="reference" href="http://library.stanford.edu/projects/stanford-prize-innovation-research-libraries-spirl/2013-spirl-winners">Stanford Prize for Innovation in Research Libraries 2013</a>. The CubicWeb community is very pleased to see that data.bnf.fr, which is built with CubicWeb, is being recognized at the top international level as leading innovation its domain! Read the <a class="reference" href="http://library.stanford.edu/projects/stanford-prize-innovation-research-libraries-spirl/biblioth%C3%A8que-nationale-de-france-judges">comments of the judges</a> for more details.</p>]]></description>
    <link><![CDATA[http://feedproxy.google.com/~r/cubicweborg/~3/bkqZ2Gu6HL0/2727288]]></link>
    <pubDate>2013-03-01 18:01:00</pubDate>
  </item>
  <item>
    <title><![CDATA[[sciunto] Détection de cercles par une transformation de Hough dans scikit-image]]></title>
    <description><![CDATA[Au détour de quelques questions que je me posais que la détection de forme, j&#8217;ai découvert les transformations de Hough. Ces transformations permettent, en jouant avec la géométrie de détecter par exemple des droites ou des cercles dans des images. Le principe est basé sur un accumulateur construit dans un espace réciproque à l&#8217;image dont [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=sciunto.wordpress.com&blog=10984286&post=840&subd=sciunto&ref=&feed=1" width="1" height="1" />]]></description>
    <link><![CDATA[http://sciunto.wordpress.com/2013/03/01/detection-de-cercles-par-une-transformation-de-hough-dans-scikit-image/]]></link>
    <pubDate>2013-03-01 17:30:33</pubDate>
    <category>Contribution</category>
    <category>Developpement</category>
    <category>Planet-april</category>
    <category>Python</category>
    <category>Science</category>
    <category>bresenham</category>
    <category>cercle</category>
    <category>Hough</category>
    <category>scikit-image</category>
    <category>tranformation de hough</category>
  </item>
  <item>
    <title><![CDATA[[Inspyration] Dans vos kiosques : Du Python, du Python et ... encore du Python.]]></title>
    <description><![CDATA[Python est à l'honneur en mars : 3 magazines pour 3 articles sur Python plus un hors-série consacré entièrement à notre langage préféré]]></description>
    <link><![CDATA[http://www.inspyration.org/news/dans-vos-kiosques-du-python-du-python-et-...-encore-du-python]]></link>
    <pubDate>2013-03-01 12:23:18</pubDate>
    <category>Python</category>
  </item>
  <item>
    <title><![CDATA[[logilab] Release of PyLint 0.27 / logilab-astng 0.24.2]]></title>
    <description><![CDATA[<p>Hi there,</p>
<p>I'm very pleased to announce the release of pylint <a class="reference" href="http://www.logilab.org/project/pylint/0.27.0">0.27</a> and
logilab-astng <a class="reference" href="http://www.logilab.org/project/logilab-astng/0.24.2">0.24.2</a>. There has been a lot of enhancements and
bug fixes since the latest release, so you're strongly encouraged
to upgrade. Here is a detailed list of changes:</p>
<ul class="simple">
<li><a class="reference" href="http://www.logilab.org/ticket/20693">#20693</a>: replace pylint.el by Ian Eure version (patch by J.Kotta)</li>
<li><a class="reference" href="http://www.logilab.org/ticket/105327">#105327</a>: add support for --disable=all option and deprecate the
'disable-all' inline directive in favour of 'skip-file' (patch by
A.Fayolle)</li>
<li><a class="reference" href="http://www.logilab.org/ticket/110840">#110840</a>: add messages I0020 and I0021 for reporting of suppressed
messages and useless suppression pragmas. (patch by Torsten Marek)</li>
<li><a class="reference" href="http://www.logilab.org/ticket/112728">#112728</a>: add warning E0604 for non-string objects in __all__
(patch by Torsten Marek)</li>
<li><a class="reference" href="http://www.logilab.org/ticket/120657">#120657</a>: add warning W0110/deprecated-lambda when a map/filter
of a lambda could be a comprehension (patch by Martin Pool)</li>
<li><a class="reference" href="http://www.logilab.org/ticket/113231">#113231</a>: logging checker now looks at instances of Logger classes
in addition to the base logging module. (patch by Mike Bryant)</li>
<li><a class="reference" href="http://www.logilab.org/ticket/111799">#111799</a>: don't warn about octal escape sequence, but warn about o
which is not octal in Python (patch by Martin Pool)</li>
<li><a class="reference" href="http://www.logilab.org/ticket/110839">#110839</a>: bind &lt;F5&gt; to Run button in pylint-gui</li>
<li><a class="reference" href="http://www.logilab.org/ticket/115580">#115580</a>: fix erroneous W0212 (access to protected member) on super call
(patch by Martin Pool)</li>
<li><a class="reference" href="http://www.logilab.org/ticket/110853">#110853</a>: fix a crash when an __init__ method in a base class has been
created by assignment rather than direct function definition (patch by
Torsten Marek)</li>
<li><a class="reference" href="http://www.logilab.org/ticket/110838">#110838</a>: fix pylint-gui crash when include-ids is activated (patch by
Omega Weapon)</li>
<li><a class="reference" href="http://www.logilab.org/ticket/112667">#112667</a>: fix emission of reimport warnings for mixed imports and extend
the testcase (patch by Torsten Marek)</li>
<li><a class="reference" href="http://www.logilab.org/ticket/112698">#112698</a>: fix crash related to non-inferable __all__ attributes and
invalid __all__ contents (patch by Torsten Marek)</li>
<li>Python 3 related fixes:<ul>
<li><a class="reference" href="http://www.logilab.org/ticket/110213">#110213</a>: fix import of checkers broken with python 3.3, causing
&quot;No such message id W0704&quot; breakage</li>
<li><a class="reference" href="http://www.logilab.org/ticket/120635">#120635</a>: redefine cmp function used in pylint.reporters</li>
</ul>
</li>
<li>Include full warning id for I0020 and I0021 and make sure to flush
warnings after each module, not at the end of the pylint run.
(patch by Torsten Marek)</li>
<li>Changed the regular expression for inline options so that it must be
preceeded by a # (patch by Torsten Marek)</li>
<li>Make dot output for import graph predictable and not depend
on ordering of strings in hashes. (patch by Torsten Marek)</li>
<li>Add hooks for import path setup and move pylint's sys.path
modifications into them. (patch by Torsten Marek)</li>
<li>pylint-brain: more subprocess.Popen faking (see <a class="reference" href="http://www.logilab.org/ticket/46273">#46273</a>)</li>
<li><a class="reference" href="http://www.logilab.org/ticket/109562">#109562</a> [jython]: java modules have no __doc__, causing crash</li>
<li><a class="reference" href="http://www.logilab.org/ticket/120646">#120646</a> [py3]: fix for python3.3 _ast changes which may cause crash</li>
<li><a class="reference" href="http://www.logilab.org/ticket/109988">#109988</a> [py3]: test fixes</li>
</ul>
<p>Many thanks to all the people who contributed to this release!</p>
<p>Enjoy!</p>]]></description>
    <link><![CDATA[http://feedproxy.google.com/~r/logilaborg/~3/4WyBlM8EWXM/121327]]></link>
    <pubDate>2013-02-28 19:37:00</pubDate>
  </item>
</channel>
</rss>

