# articles

feed_id: http://sametmax.com/?p=16645
title: Aux couleurs du site 11
uri: Aux-couleurs-du-site-4-Recently-updated
url: http://sametmax.com/aux-couleurs-du-site/
content:
<p>On est en train de tweaker un peu <a href="http://multiboards.net/">multiboards.net</a> Max et moi, et il a voulu ajouter un feature rigolote : les flux sont affichés avec une palette de couleurs proche de celle du site.</p>
<p>Comme récupérer les couleurs d&#8217;un site est compliqué, on s&#8217;est rabattus sur les couleurs du favicon, qui en théorie doit résumer l&#8217;identité d&#8217;un site. Ce n&#8217;est pas parfait, mais c&#8217;est plus simple à implémenter.</p>
<p>Au début, on avait écrit une solution avec Pillow et numpy. Ça marchait très bien, mais des extensions compilées juste pour faire de la déco c&#8217;était un peu lourd, surtout si un jour on libère le code source (mais vu comme les sources sont dégueues et qu&#8217;on a pas de doc, pour le moment c&#8217;est pas gagné).</p>
<p>On a donc changé notre fusil d&#8217;épaule, et on a choisit une nouvelle technique : faire la moitié du taff côté client.</p>
<h2>Récupérer le favicon côté serveur</h2>
<p>On ne peut pas faire des requêtes cross domain en JS, donc on doit toujours récupérer l&#8217;image côté serveur. <code>get_favicon_url()</code> parse le site à coup de regex (c&#8217;est mal mais ça évite de rajouter BeautifulSoup en dépendance pour une balise) pour récupérer son URL sur la page donnée, ou à défaut, sur la page à la racine du site. On se donne plusieurs essais en cas d&#8217;erreurs de réseau :</p>

<div class="wp_syntax"><table><tr><td class="code"><pre class="python" style="font-family:monospace;">&nbsp;
<span style="color: #ff7700;font-weight:bold;">import</span> <span style="color: #dc143c;">urllib</span>
<span style="color: #ff7700;font-weight:bold;">import</span> <span style="color: #dc143c;">urlparse</span> 
&nbsp;
<span style="color: #ff7700;font-weight:bold;">def</span> get_favicon_url<span style="color: black;">&#40;</span>url<span style="color: #66cc66;">,</span> retry<span style="color: #66cc66;">=</span><span style="color: #ff4500;">3</span><span style="color: #66cc66;">,</span> try_home_page<span style="color: #66cc66;">=</span><span style="color: #008000;">True</span><span style="color: black;">&#41;</span>:
    <span style="color: #483d8b;">&quot;&quot;&quot; Try to find a favicon url on the given page &quot;&quot;&quot;</span>
&nbsp;
    parsed_url <span style="color: #66cc66;">=</span> <span style="color: #dc143c;">urlparse</span>.<span style="color: #dc143c;">urlparse</span><span style="color: black;">&#40;</span>url<span style="color: black;">&#41;</span>
    url_root <span style="color: #66cc66;">=</span> <span style="color: #483d8b;">&quot;%s://%s&quot;</span> % <span style="color: black;">&#40;</span>parsed_url.<span style="color: black;">scheme</span><span style="color: #66cc66;">,</span> parsed_url.<span style="color: black;">netloc</span><span style="color: black;">&#41;</span>
&nbsp;
    <span style="color: #ff7700;font-weight:bold;">try</span>:
        <span style="color: #808080; font-style: italic;"># try to get it using a regex on the current URL</span>
        html <span style="color: #66cc66;">=</span> fetch_url<span style="color: black;">&#40;</span>url<span style="color: #66cc66;">,</span> retry<span style="color: #66cc66;">=</span>retry<span style="color: black;">&#41;</span>
        html <span style="color: #66cc66;">=</span> html.<span style="color: black;">decode</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'ascii'</span><span style="color: #66cc66;">,</span> errors<span style="color: #66cc66;">=</span><span style="color: #483d8b;">'ignore'</span><span style="color: black;">&#41;</span>
        pattern <span style="color: #66cc66;">=</span> r<span style="color: #483d8b;">&quot;&quot;&quot;
                               href=(?:&quot;|')<span style="color: #000099; font-weight: bold;">\s</span>*
                               (?P&lt;favicon&gt;[^<span style="color: #000099; font-weight: bold;">\s</span>'&quot;]*favicon.[a-zA-Z]{3,4})
                               <span style="color: #000099; font-weight: bold;">\s</span>*(?:&quot;|')
                          &quot;&quot;&quot;</span>
&nbsp;
        match <span style="color: #66cc66;">=</span> <span style="color: #dc143c;">re</span>.<span style="color: black;">search</span><span style="color: black;">&#40;</span>pattern<span style="color: #66cc66;">,</span> html<span style="color: #66cc66;">,</span> <span style="color: #dc143c;">re</span>.<span style="color: black;">U</span>|<span style="color: #dc143c;">re</span>.<span style="color: black;">VERBOSE</span><span style="color: black;">&#41;</span>
        favicon_url <span style="color: #66cc66;">=</span> match.<span style="color: black;">groups</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span><span style="color: black;">&#91;</span><span style="color: #ff4500;">0</span><span style="color: black;">&#93;</span>
&nbsp;
    <span style="color: #ff7700;font-weight:bold;">except</span> <span style="color: #008000;">IOError</span>:
        <span style="color: #808080; font-style: italic;"># this is a network error so eventually, let it crash</span>
        <span style="color: #ff7700;font-weight:bold;">if</span> <span style="color: #ff7700;font-weight:bold;">not</span> try_home_page:
            <span style="color: #ff7700;font-weight:bold;">raise</span>
        <span style="color: #808080; font-style: italic;"># try with the home page, maybe this one is accessible</span>
        favicon_url <span style="color: #66cc66;">=</span> get_favicon_url<span style="color: black;">&#40;</span>url_root<span style="color: #66cc66;">,</span> retry<span style="color: #66cc66;">=</span>retry<span style="color: #66cc66;">,</span>
                                                       try_home_page<span style="color: #66cc66;">=</span><span style="color: #008000;">False</span><span style="color: black;">&#41;</span>
    <span style="color: #ff7700;font-weight:bold;">except</span> <span style="color: black;">&#40;</span><span style="color: #008000;">IndexError</span><span style="color: #66cc66;">,</span> <span style="color: #008000;">AttributeError</span><span style="color: black;">&#41;</span>:
        <span style="color: #808080; font-style: italic;"># url is not on this page, try the home page</span>
        <span style="color: #ff7700;font-weight:bold;">if</span> try_home_page:
            <span style="color: #ff7700;font-weight:bold;">return</span> get_favicon_url<span style="color: black;">&#40;</span>url_root<span style="color: #66cc66;">,</span> retry<span style="color: #66cc66;">=</span>retry<span style="color: #66cc66;">,</span> try_home_page<span style="color: #66cc66;">=</span><span style="color: #008000;">False</span><span style="color: black;">&#41;</span>
&nbsp;
        <span style="color: #808080; font-style: italic;"># can't find the favicon url, default to standard url</span>
        favicon_url <span style="color: #66cc66;">=</span> <span style="color: #483d8b;">'/favicon.ico'</span>
&nbsp;
    <span style="color: #808080; font-style: italic;"># make sure to have the domain of the original website in the favicon url</span>
    <span style="color: #ff7700;font-weight:bold;">if</span> url_root <span style="color: #ff7700;font-weight:bold;">not</span> <span style="color: #ff7700;font-weight:bold;">in</span> favicon_url:
        favicon_url <span style="color: #66cc66;">=</span> <span style="color: #483d8b;">&quot;%s/%s&quot;</span> % <span style="color: black;">&#40;</span>url_root<span style="color: #66cc66;">,</span> favicon_url.<span style="color: black;">lstrip</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'/'</span><span style="color: black;">&#41;</span><span style="color: black;">&#41;</span>
&nbsp;
    <span style="color: #ff7700;font-weight:bold;">return</span> favicon_url
&nbsp;
&nbsp;
<span style="color: #ff7700;font-weight:bold;">def</span> fetch_favicon<span style="color: black;">&#40;</span>url<span style="color: #66cc66;">,</span> retry<span style="color: #66cc66;">=</span><span style="color: #ff4500;">3</span><span style="color: black;">&#41;</span>:
    <span style="color: #483d8b;">&quot;&quot;&quot; Returns the bytes of the favicon of this site &quot;&quot;&quot;</span>
    favicon_url <span style="color: #66cc66;">=</span> get_favicon_url<span style="color: black;">&#40;</span>url<span style="color: #66cc66;">,</span> retry<span style="color: #66cc66;">=</span>retry<span style="color: black;">&#41;</span>
    <span style="color: #ff7700;font-weight:bold;">return</span> fetch_url<span style="color: black;">&#40;</span>favicon_url<span style="color: #66cc66;">,</span> retry<span style="color: #66cc66;">=</span>retry<span style="color: black;">&#41;</span></pre></td></tr></table></div>

<p><code>fetch_favicon()</code> télécharge l&#8217;image proprement dite et retourne les bits tels quels. Notez que tout ça est synchrone et bloquant, mais heureusement le traffic de multiboards est sans doute trop faible pour poser problème.</p>
<h2>Exposer le résultat au client</h2>
<p>Ensuite il faut faire le lien entre le client et le serveur. On fait donc un petit routing (c&#8217;est du bottle) qui va retourner tout ça en base64 afin qu&#8217;on puisse l&#8217;utiliser directement dans un objet <code>Image</code> JS :</p>

<div class="wp_syntax"><table><tr><td class="code"><pre class="python" style="font-family:monospace;">&nbsp;
<span style="color: #ff7700;font-weight:bold;">import</span> <span style="color: #dc143c;">base64</span>
<span style="color: #ff7700;font-weight:bold;">from</span> bottle <span style="color: #ff7700;font-weight:bold;">import</span> post<span style="color: #66cc66;">,</span> HTTPError
&nbsp;
<span style="color: #66cc66;">@</span>post<span style="color: black;">&#40;</span><span style="color: #483d8b;">'/favicon'</span><span style="color: black;">&#41;</span>
<span style="color: #ff7700;font-weight:bold;">def</span> fetch_favicon_base64<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>:
    <span style="color: #483d8b;">&quot;&quot;&quot; Return the favicon URL from a website &quot;&quot;&quot;</span>
    <span style="color: #ff7700;font-weight:bold;">try</span>:
        url <span style="color: #66cc66;">=</span> request.<span style="color: black;">POST</span><span style="color: black;">&#91;</span><span style="color: #483d8b;">'url'</span><span style="color: black;">&#93;</span>
    <span style="color: #ff7700;font-weight:bold;">except</span> <span style="color: #008000;">KeyError</span>:
        <span style="color: #ff7700;font-weight:bold;">raise</span> HTTPError<span style="color: black;">&#40;</span><span style="color: #ff4500;">400</span><span style="color: #66cc66;">,</span> <span style="color: #483d8b;">&quot;You must pass a site URL&quot;</span><span style="color: black;">&#41;</span>
    <span style="color: #ff7700;font-weight:bold;">try</span>:
        <span style="color: #808080; font-style: italic;"># return favicon as base64 url to be easily included in</span>
        <span style="color: #808080; font-style: italic;"># a img tag</span>
        favicon <span style="color: #66cc66;">=</span> fetch_favicon<span style="color: black;">&#40;</span>url<span style="color: black;">&#41;</span>
        <span style="color: #ff7700;font-weight:bold;">return</span> b<span style="color: #483d8b;">'data:image/x-icon;base64,'</span> + <span style="color: #dc143c;">base64</span>.<span style="color: black;">b64encode</span><span style="color: black;">&#40;</span>favicon<span style="color: black;">&#41;</span>
    <span style="color: #ff7700;font-weight:bold;">except</span> <span style="color: black;">&#40;</span><span style="color: #008000;">IOError</span><span style="color: black;">&#41;</span>:
        <span style="color: #ff7700;font-weight:bold;">raise</span> HTTPError<span style="color: black;">&#40;</span><span style="color: #ff4500;">400</span><span style="color: #66cc66;">,</span> <span style="color: #483d8b;">&quot;Unable to find any favicon URL&quot;</span><span style="color: black;">&#41;</span></pre></td></tr></table></div>

<h2>Récupérer la palette côté client</h2>
<p>Avec un peu d&#8217;AJAX sur notre vue, on récupère les bits de l&#8217;image, et on fout le tout dans un objet <code>Image</code> qu&#8217;on passe à la lib <a href="http://lokeshdhakar.com/projects/color-thief/">ColorThief</a>. Cette dernière nous renvoie un array avec la palette. On convertit tout ça en code hexadécimal, et on diminue la luminosité de certaines couleurs pour rendre le texte plus lisible.</p>

<div class="wp_syntax"><table><tr><td class="code"><pre class="javascript" style="font-family:monospace;"> $.<span style="color: #660066;">post</span><span style="color: #009900;">&#40;</span><span style="color: #3366CC;">'/favicon'</span><span style="color: #339933;">,</span> <span style="color: #009900;">&#123;</span>url<span style="color: #339933;">:</span> url<span style="color: #009900;">&#125;</span><span style="color: #009900;">&#41;</span>.<span style="color: #660066;">done</span><span style="color: #009900;">&#40;</span><span style="color: #000066; font-weight: bold;">function</span><span style="color: #009900;">&#40;</span>data<span style="color: #009900;">&#41;</span><span style="color: #009900;">&#123;</span>
&nbsp;
        <span style="color: #006600; font-style: italic;">// load an image with the base64 data of the favicon</span>
        <span style="color: #000066; font-weight: bold;">var</span> image <span style="color: #339933;">=</span> <span style="color: #000066; font-weight: bold;">new</span> Image<span style="color: #339933;">;</span>
        image.<span style="color: #660066;">src</span> <span style="color: #339933;">=</span> data<span style="color: #339933;">;</span>
        image.<span style="color: #660066;">onload</span> <span style="color: #339933;">=</span> <span style="color: #000066; font-weight: bold;">function</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
&nbsp;
            <span style="color: #006600; font-style: italic;">// ask ColorThief for the main favicon colors</span>
            <span style="color: #000066; font-weight: bold;">var</span> colorThief <span style="color: #339933;">=</span> <span style="color: #000066; font-weight: bold;">new</span> ColorThief<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
            <span style="color: #000066; font-weight: bold;">var</span> bc <span style="color: #339933;">=</span> colorThief.<span style="color: #660066;">getPalette</span><span style="color: #009900;">&#40;</span>image<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
&nbsp;
            <span style="color: #006600; font-style: italic;">// some tools to convert the RGB array into</span>
            <span style="color: #006600; font-style: italic;">// rgb hex string</span>
            <span style="color: #000066; font-weight: bold;">function</span> componentToHex<span style="color: #009900;">&#40;</span>c<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
                <span style="color: #000066; font-weight: bold;">var</span> hex <span style="color: #339933;">=</span> c.<span style="color: #660066;">toString</span><span style="color: #009900;">&#40;</span><span style="color: #CC0000;">16</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
                <span style="color: #000066; font-weight: bold;">return</span> hex.<span style="color: #660066;">length</span> <span style="color: #339933;">==</span> <span style="color: #CC0000;">1</span> <span style="color: #339933;">?</span> <span style="color: #3366CC;">&quot;0&quot;</span> <span style="color: #339933;">+</span> hex <span style="color: #339933;">:</span> hex<span style="color: #339933;">;</span>
            <span style="color: #009900;">&#125;</span>
&nbsp;
            <span style="color: #000066; font-weight: bold;">function</span> rgbToHex<span style="color: #009900;">&#40;</span>array<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
                <span style="color: #000066; font-weight: bold;">return</span> componentToHex<span style="color: #009900;">&#40;</span>array<span style="color: #009900;">&#91;</span><span style="color: #CC0000;">0</span><span style="color: #009900;">&#93;</span><span style="color: #009900;">&#41;</span> <span style="color: #339933;">+</span> componentToHex<span style="color: #009900;">&#40;</span>array<span style="color: #009900;">&#91;</span><span style="color: #CC0000;">1</span><span style="color: #009900;">&#93;</span><span style="color: #009900;">&#41;</span> <span style="color: #339933;">+</span> componentToHex<span style="color: #009900;">&#40;</span>array<span style="color: #009900;">&#91;</span><span style="color: #CC0000;">2</span><span style="color: #009900;">&#93;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
            <span style="color: #009900;">&#125;</span>
&nbsp;
            <span style="color: #006600; font-style: italic;">// make the color brigther</span>
            <span style="color: #000066; font-weight: bold;">function</span> increase_brightness<span style="color: #009900;">&#40;</span>hex<span style="color: #339933;">,</span> percent<span style="color: #009900;">&#41;</span><span style="color: #009900;">&#123;</span>
&nbsp;
                <span style="color: #006600; font-style: italic;">// convert 3 char codes --&gt; 6, e.g. `E0F` --&gt; `EE00FF`</span>
                <span style="color: #000066; font-weight: bold;">if</span><span style="color: #009900;">&#40;</span>hex.<span style="color: #660066;">length</span> <span style="color: #339933;">==</span> <span style="color: #CC0000;">3</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#123;</span>
                    hex <span style="color: #339933;">=</span> hex.<span style="color: #660066;">replace</span><span style="color: #009900;">&#40;</span><span style="color: #009966; font-style: italic;">/(.)/g</span><span style="color: #339933;">,</span> <span style="color: #3366CC;">'$1$1'</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
                <span style="color: #009900;">&#125;</span>
&nbsp;
                <span style="color: #000066; font-weight: bold;">var</span> r <span style="color: #339933;">=</span> parseInt<span style="color: #009900;">&#40;</span>hex.<span style="color: #660066;">substr</span><span style="color: #009900;">&#40;</span><span style="color: #CC0000;">0</span><span style="color: #339933;">,</span> <span style="color: #CC0000;">2</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">,</span> <span style="color: #CC0000;">16</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">,</span>
                    g <span style="color: #339933;">=</span> parseInt<span style="color: #009900;">&#40;</span>hex.<span style="color: #660066;">substr</span><span style="color: #009900;">&#40;</span><span style="color: #CC0000;">2</span><span style="color: #339933;">,</span> <span style="color: #CC0000;">2</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">,</span> <span style="color: #CC0000;">16</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">,</span>
                    b <span style="color: #339933;">=</span> parseInt<span style="color: #009900;">&#40;</span>hex.<span style="color: #660066;">substr</span><span style="color: #009900;">&#40;</span><span style="color: #CC0000;">4</span><span style="color: #339933;">,</span> <span style="color: #CC0000;">2</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">,</span> <span style="color: #CC0000;">16</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
&nbsp;
                <span style="color: #000066; font-weight: bold;">return</span> <span style="color: #3366CC;">''</span> <span style="color: #339933;">+</span>
                   <span style="color: #009900;">&#40;</span><span style="color: #009900;">&#40;</span><span style="color: #CC0000;">0</span><span style="color: #339933;">|</span><span style="color: #009900;">&#40;</span><span style="color: #CC0000;">1</span><span style="color: #339933;">&lt;&lt;</span><span style="color: #CC0000;">8</span><span style="color: #009900;">&#41;</span> <span style="color: #339933;">+</span> r <span style="color: #339933;">+</span> <span style="color: #009900;">&#40;</span><span style="color: #CC0000;">256</span> <span style="color: #339933;">-</span> r<span style="color: #009900;">&#41;</span> <span style="color: #339933;">*</span> percent <span style="color: #339933;">/</span> <span style="color: #CC0000;">100</span><span style="color: #009900;">&#41;</span>.<span style="color: #660066;">toString</span><span style="color: #009900;">&#40;</span><span style="color: #CC0000;">16</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span>.<span style="color: #660066;">substr</span><span style="color: #009900;">&#40;</span><span style="color: #CC0000;">1</span><span style="color: #009900;">&#41;</span> <span style="color: #339933;">+</span>
                   <span style="color: #009900;">&#40;</span><span style="color: #009900;">&#40;</span><span style="color: #CC0000;">0</span><span style="color: #339933;">|</span><span style="color: #009900;">&#40;</span><span style="color: #CC0000;">1</span><span style="color: #339933;">&lt;&lt;</span><span style="color: #CC0000;">8</span><span style="color: #009900;">&#41;</span> <span style="color: #339933;">+</span> g <span style="color: #339933;">+</span> <span style="color: #009900;">&#40;</span><span style="color: #CC0000;">256</span> <span style="color: #339933;">-</span> g<span style="color: #009900;">&#41;</span> <span style="color: #339933;">*</span> percent <span style="color: #339933;">/</span> <span style="color: #CC0000;">100</span><span style="color: #009900;">&#41;</span>.<span style="color: #660066;">toString</span><span style="color: #009900;">&#40;</span><span style="color: #CC0000;">16</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span>.<span style="color: #660066;">substr</span><span style="color: #009900;">&#40;</span><span style="color: #CC0000;">1</span><span style="color: #009900;">&#41;</span> <span style="color: #339933;">+</span>
                   <span style="color: #009900;">&#40;</span><span style="color: #009900;">&#40;</span><span style="color: #CC0000;">0</span><span style="color: #339933;">|</span><span style="color: #009900;">&#40;</span><span style="color: #CC0000;">1</span><span style="color: #339933;">&lt;&lt;</span><span style="color: #CC0000;">8</span><span style="color: #009900;">&#41;</span> <span style="color: #339933;">+</span> b <span style="color: #339933;">+</span> <span style="color: #009900;">&#40;</span><span style="color: #CC0000;">256</span> <span style="color: #339933;">-</span> b<span style="color: #009900;">&#41;</span> <span style="color: #339933;">*</span> percent <span style="color: #339933;">/</span> <span style="color: #CC0000;">100</span><span style="color: #009900;">&#41;</span>.<span style="color: #660066;">toString</span><span style="color: #009900;">&#40;</span><span style="color: #CC0000;">16</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span>.<span style="color: #660066;">substr</span><span style="color: #009900;">&#40;</span><span style="color: #CC0000;">1</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
            <span style="color: #009900;">&#125;</span>
&nbsp;
            <span style="color: #006600; font-style: italic;">/* Define board colors */</span>
            <span style="color: #000066; font-weight: bold;">var</span> header <span style="color: #339933;">=</span>  <span style="color: #3366CC;">'#'</span> <span style="color: #339933;">+</span> rgbToHex<span style="color: #009900;">&#40;</span>bc<span style="color: #009900;">&#91;</span><span style="color: #CC0000;">0</span><span style="color: #009900;">&#93;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
            <span style="color: #000066; font-weight: bold;">var</span> odd <span style="color: #339933;">=</span> <span style="color: #3366CC;">'#'</span> <span style="color: #339933;">+</span> increase_brightness<span style="color: #009900;">&#40;</span>rgbToHex<span style="color: #009900;">&#40;</span>bc<span style="color: #009900;">&#91;</span><span style="color: #CC0000;">1</span><span style="color: #009900;">&#93;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">,</span> <span style="color: #CC0000;">90</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
            <span style="color: #000066; font-weight: bold;">var</span> even <span style="color: #339933;">=</span> <span style="color: #3366CC;">'#'</span> <span style="color: #339933;">+</span> increase_brightness<span style="color: #009900;">&#40;</span>rgbToHex<span style="color: #009900;">&#40;</span>bc<span style="color: #009900;">&#91;</span><span style="color: #CC0000;">2</span><span style="color: #009900;">&#93;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">,</span> <span style="color: #CC0000;">90</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
&nbsp;
<span style="color: #009900;">&#125;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span></pre></td></tr></table></div>

<p>Est-ce que la feature est utile ? Je ne sais pas, c&#8217;est un peu gadget, mais c&#8217;était marrant à faire, et c&#8217;est vrai qu&#8217;on identifie bien chaque site sur la page du coup. </p>
<p>L&#8217;idée ici est que le serveur et le client travaillent en équipe, et que grâce au navigateur on a une stack de traitement image/audio/video à portée de code. D&#8217;ailleurs on a viré le plugin flash des radios pour le remplacer par du chteumeuleu 5.</p>
<p>On peut imaginer la même chose avec tous les outils côtés en JS, ceci incluant les préprocesseurs et minifieurs JS ou CSS : pas besoin d&#8217;installer Node pour faire tourner tout ça, il suffit de déléguer le boulot à un tab du browser. Pour améliorer la stack, on pourrait par ailleurs utiliser du RPC avec WAMP et même prévenir le navigateur quand un fichier est prêt avec PUB/SUB afin qu&#8217;il le recharge. Vous avez vu comme j&#8217;arrive toujours à placer crossbar ?</p>
<p>J&#8217;ai un pote qui va venir dans quelques jours pour creuser cette idée qui permettrait d&#8217;avoir en pur Python une sorte de livereload sans extension, sans gros GUI, qui transpile tout et rafraichit tout avec un simple fichier de config. Peut être aurons nous le temps de faire un proto.</p>
<p>C&#8217;était l&#8217;inspiration du jour, passez une excellente canicule !</p>


feed_id: http://sametmax.com/?p=16655
title: Le bon coup de pute du jeudi 7
uri: Le-bon-coup-de-pute-du-jeudi-6-Recently-updated
url: http://sametmax.com/le-bon-coup-de-pute-du-jeudi/
content:
<p>Le bureau est calme. Trop calme. Les projets avancent, vos clients sont heureux et &#8211; pire ! &#8211; vos collègues sourient.</p>
<p>Il est temps de changer tout ça.</p>
<p>Choisissez une victime qui est partie boire un café sans locker sa session (quitte à faire un exemple, autant choisir un être faible), et créez un petit fichier dans le chemin &#8220;~/au/fin/fond/de/ses/reps/de/travail/evil.py&#8221;.</p>
<p>Ensuite mettez ça dedans :</p>

<div class="wp_syntax"><table><tr><td class="code"><pre class="python" style="font-family:monospace;"><span style="color: #ff7700;font-weight:bold;">import</span> <span style="color: #dc143c;">random</span>
<span style="color: #ff7700;font-weight:bold;">import</span> functools
&nbsp;
<span style="color: #808080; font-style: italic;"># on garde une référence à l'ancienne fonction</span>
<span style="color: #808080; font-style: italic;"># pour éviter une récursion infinie</span>
old_range <span style="color: #66cc66;">=</span> <span style="color: #008000;">range</span>
&nbsp;
<span style="color: #808080; font-style: italic;"># on lui créé un remplaçant avec les mêmes métatadas</span>
<span style="color: #66cc66;">@</span>functools.<span style="color: black;">wraps</span><span style="color: black;">&#40;</span><span style="color: #008000;">range</span><span style="color: black;">&#41;</span>
<span style="color: #ff7700;font-weight:bold;">def</span> random_range<span style="color: black;">&#40;</span>start<span style="color: #66cc66;">,</span> stop<span style="color: #66cc66;">=</span><span style="color: #008000;">None</span><span style="color: #66cc66;">,</span> step<span style="color: #66cc66;">=</span><span style="color: #ff4500;">1</span><span style="color: black;">&#41;</span>:
&nbsp;
    <span style="color: #808080; font-style: italic;"># on mimic la signature un peu originale de range()</span>
    <span style="color: #ff7700;font-weight:bold;">if</span> <span style="color: #ff7700;font-weight:bold;">not</span> stop:
        stop <span style="color: #66cc66;">=</span> start
        start <span style="color: #66cc66;">=</span> <span style="color: #ff4500;">0</span>
&nbsp;
    <span style="color: #808080; font-style: italic;"># et on fait un range avec un tout petit peu de piment</span>
    <span style="color: #ff7700;font-weight:bold;">return</span> old_range<span style="color: black;">&#40;</span>start<span style="color: #66cc66;">,</span> stop + <span style="color: #dc143c;">random</span>.<span style="color: black;">randint</span><span style="color: black;">&#40;</span>-<span style="color: #ff4500;">1</span><span style="color: #66cc66;">,</span> <span style="color: #ff4500;">1</span><span style="color: black;">&#41;</span><span style="color: #66cc66;">,</span> step<span style="color: black;">&#41;</span>
&nbsp;
<span style="color: #808080; font-style: italic;"># on range (lol) tout comme si de rien était</span>
__builtins__.<span style="color: #008000;">range</span> <span style="color: #66cc66;">=</span> random_range</pre></td></tr></table></div>

<p>Et il n&#8217;y a plus qu&#8217;à faire pointer la variable d&#8217;environnement <code>PYTHONSTARTUP</code> dessus. Par exemple sous Unix, dans son <code>.bashrc</code>:</p>
<pre>export PYTHONSTARTUP=~/au/fin/fon/de/ses/reps/de/travail/evil.py</pre>
<p>Et maintenant, à chaque fois qu&#8217;il ou elle lancera son shell:</p>

<div class="wp_syntax"><table><tr><td class="code"><pre class="python" style="font-family:monospace;"><span style="color: #66cc66;">&gt;&gt;&gt;</span> <span style="color: #008000;">range</span>
    <span style="color: #66cc66;">&lt;</span>function builtins.<span style="color: black;">range</span><span style="color: #66cc66;">&gt;</span>
&nbsp;
<span style="color: #66cc66;">&gt;&gt;&gt;</span> <span style="color: #008000;">list</span><span style="color: black;">&#40;</span><span style="color: #008000;">range</span><span style="color: black;">&#40;</span><span style="color: #ff4500;">10</span><span style="color: black;">&#41;</span><span style="color: black;">&#41;</span>
    <span style="color: black;">&#91;</span><span style="color: #ff4500;">0</span><span style="color: #66cc66;">,</span> <span style="color: #ff4500;">1</span><span style="color: #66cc66;">,</span> <span style="color: #ff4500;">2</span><span style="color: #66cc66;">,</span> <span style="color: #ff4500;">3</span><span style="color: #66cc66;">,</span> <span style="color: #ff4500;">4</span><span style="color: #66cc66;">,</span> <span style="color: #ff4500;">5</span><span style="color: #66cc66;">,</span> <span style="color: #ff4500;">6</span><span style="color: #66cc66;">,</span> <span style="color: #ff4500;">7</span><span style="color: #66cc66;">,</span> <span style="color: #ff4500;">8</span><span style="color: #66cc66;">,</span> <span style="color: #ff4500;">9</span><span style="color: black;">&#93;</span>
&nbsp;
<span style="color: #66cc66;">&gt;&gt;&gt;</span> <span style="color: #008000;">list</span><span style="color: black;">&#40;</span><span style="color: #008000;">range</span><span style="color: black;">&#40;</span><span style="color: #ff4500;">10</span><span style="color: black;">&#41;</span><span style="color: black;">&#41;</span>
    <span style="color: black;">&#91;</span><span style="color: #ff4500;">0</span><span style="color: #66cc66;">,</span> <span style="color: #ff4500;">1</span><span style="color: #66cc66;">,</span> <span style="color: #ff4500;">2</span><span style="color: #66cc66;">,</span> <span style="color: #ff4500;">3</span><span style="color: #66cc66;">,</span> <span style="color: #ff4500;">4</span><span style="color: #66cc66;">,</span> <span style="color: #ff4500;">5</span><span style="color: #66cc66;">,</span> <span style="color: #ff4500;">6</span><span style="color: #66cc66;">,</span> <span style="color: #ff4500;">7</span><span style="color: #66cc66;">,</span> <span style="color: #ff4500;">8</span><span style="color: #66cc66;">,</span> <span style="color: #ff4500;">9</span><span style="color: #66cc66;">,</span> <span style="color: #ff4500;">10</span><span style="color: black;">&#93;</span>
&nbsp;
<span style="color: #66cc66;">&gt;&gt;&gt;</span> <span style="color: #008000;">list</span><span style="color: black;">&#40;</span><span style="color: #008000;">range</span><span style="color: black;">&#40;</span><span style="color: #ff4500;">10</span><span style="color: black;">&#41;</span><span style="color: black;">&#41;</span>
    <span style="color: black;">&#91;</span><span style="color: #ff4500;">0</span><span style="color: #66cc66;">,</span> <span style="color: #ff4500;">1</span><span style="color: #66cc66;">,</span> <span style="color: #ff4500;">2</span><span style="color: #66cc66;">,</span> <span style="color: #ff4500;">3</span><span style="color: #66cc66;">,</span> <span style="color: #ff4500;">4</span><span style="color: #66cc66;">,</span> <span style="color: #ff4500;">5</span><span style="color: #66cc66;">,</span> <span style="color: #ff4500;">6</span><span style="color: #66cc66;">,</span> <span style="color: #ff4500;">7</span><span style="color: #66cc66;">,</span> <span style="color: #ff4500;">8</span><span style="color: #66cc66;">,</span> <span style="color: #ff4500;">9</span><span style="color: #66cc66;">,</span> <span style="color: #ff4500;">10</span><span style="color: black;">&#93;</span>
&nbsp;
<span style="color: #66cc66;">&gt;&gt;&gt;</span> <span style="color: #008000;">list</span><span style="color: black;">&#40;</span><span style="color: #008000;">range</span><span style="color: black;">&#40;</span><span style="color: #ff4500;">10</span><span style="color: black;">&#41;</span><span style="color: black;">&#41;</span>
    <span style="color: black;">&#91;</span><span style="color: #ff4500;">0</span><span style="color: #66cc66;">,</span> <span style="color: #ff4500;">1</span><span style="color: #66cc66;">,</span> <span style="color: #ff4500;">2</span><span style="color: #66cc66;">,</span> <span style="color: #ff4500;">3</span><span style="color: #66cc66;">,</span> <span style="color: #ff4500;">4</span><span style="color: #66cc66;">,</span> <span style="color: #ff4500;">5</span><span style="color: #66cc66;">,</span> <span style="color: #ff4500;">6</span><span style="color: #66cc66;">,</span> <span style="color: #ff4500;">7</span><span style="color: #66cc66;">,</span> <span style="color: #ff4500;">8</span><span style="color: black;">&#93;</span></pre></td></tr></table></div>

<p>*Zoom final sur rire diabolique*</p>


feed_id: http://sametmax.com/?p=16670
title: Des fois ce que je trouve sur 0bin fait flipper 24
uri: Des-fois-ce-que-je-trouve-sur-0bin-fait-flipper-15-Recently-updated
url: http://sametmax.com/des-fois-ce-que-je-trouve-sur-0bin-fait-flipper/
content:
<p>On reçoit maintenant régulièrement des demandes de <a href="http://sametmax.com/0bin-sest-fait-tej/">take down</a> sur <a href="http://0bin.net">0bin.net</a>, essentiellement pour des trucs de copyright.</p>
<p>Et de temps en temps, on a des trucs vachement plus velus, comme par exemple des <a href="http://sametmax.com/des-pastes-mysterieux-sur-0bin/">clés d&#8217;entrée sur des portails pédophiles.</a></p>
<p>Mais depuis quelques temps, on nous signale des nouveautés : des gens qui ont leur profile complet (genre complet, complet) et qui nous demande de le virer.</p>
<p>Voici un exemple anonymisé d&#8217;un truc que j&#8217;ai viré cette semaine :</p>
<pre>
Picture: https://i.imgur.com/sa_gueule.jpg
------------------------------
Understalker:
- Name: prenom nom
- DOB: 01/01/19XX
- Phone #: xxx-xxx-xxxx - Landline, Google Voice
- Phone #: xxx-xxx-xxxx - Landline, Time Warner Cable
- Address: xxx Avenue, New York, NY 10027
- Passwords: passwd1, passwd2, passwd3
------------------------------
Emails:
- prenomgx3@aim.com, Reset: pseudo@yahoo.com
- hnom@yahoo.com
- prenomtruc@mac.com
------------------------------
Skypes:
- skype:prenomnom // Current
- skype:pseudo.2002
- skype:prenom.nom
- skype:pseudo.nom
- skype:prenomsg
- skype:pseudo
------------------------------
Accounts:
- https://prenomstuff.tumblr.com
- https://www.hackforums.net/member.php?action=profile&#038;uid=<id>
- https://www.facebook.com/prenom.nom.9
- https://www.epicnpc.com/members/pseudo
- https://www.whois.com/whois/nomdedomaine.tld
- https://www.whois.com/whois/nomdedomaine.tld
- https://www.whois.com/whois/nomdedomaine.tld
- https://www.whois.com/whois/nomdedomaine.tld
- https://www.whois.com/whois/nomdedomaine.tld
------------------------------
Time Warner Cable Account
- IP: xx.xxx.xxx.xx
- Name: prenom nom
- Address: xxx Avenue, New York, NY 10027
- Phone Number: xxx-xxx-xxxx
- Account Number:
- PIN:
- Modem MAC Address: XX:XX:XX:XX:XX:XX
- SSID: yxyxyxyxyxyx
------------------------------
Relatives:
- Mother: Prenom nom
- Phone #: xxx-xxx-xxxx - Landline, Time Warner Cable
- Address: xxx Avenue, New York, NY 10027
- Previous Phone #: xxx-xxx-xxxx - Landline, Verizon
- Previous Address: Une autre adresse complete
- Previous Address: Une autre adresse complete
- Skype: pseudo
- Skype: pseudo.nom
- Skype: pseudo.nom1
- Facebook: https://www.facebook.com/pseudo.nom.3
- Occupation: COSMETOLOGIST
- License #: Numero de putain licence !, Expir
- Used to own 'nom de son putain de salon de coifure !'

- Father: prenom nom
- Phone #: xxx-xxx-xxxx - Cell, Verizon
- Phone #: xxx-xxx-xxxx - Landline, Time Warner Cable
- Address: xxx Avenue, New York, NY 10027
- Email: pseudo@yahoo.com
- Facebook: https://www.facebook.com/prenom.nom
- Company: Ouai le nom de la société de son père
- Company #: et son siret

- Brother: Prenom nom

- Unknown: Prenom nom
- Phone #: xxx-xxx-xxxx - Landline, Time Warner Cable
- Address: Autre adresse à NY
- Facebook: https://www.facebook.com/prenom.nom

- Unknown: Prenom nom
- Facebook: https://www.facebook.com/prenom.nom</pre>
<p>J&#8217;ai tout sur ce gars, tout. Où il habite, sa famille, le taff, les contacts, de quoi lire ses mails et resetter tous ses passwords sur tous ses sites. Notez aussi qu&#8217;il y a un historique des comptes, et un croisement des identités en ligne. Truc de ouf.</p>
<p>J&#8217;ai tout viré, bien sur, mais je suppose qu&#8217;il y a en plein qu&#8217;on ne me signale pas, et qui sont là, sur le serveur.</p>


feed_id: http://sametmax.com/?p=16676
title: Utilisateurs en ligne avec le sorted set de Redis 7
uri: Utilisateurs-en-ligne-avec-le-sorted-set-de-Redis-7-Recently-update
url: http://sametmax.com/utilisateurs-en-ligne-avec-le-sorted-set-de-redis/
content:
<p>Beaucoup de gens sous exploitent Redis en se cantonnant à un usage clé/valeur, voire pour les plus aguérris à la liste ou le hash.</p>
<p>Mais derrière sa simplicité d&#8217;usage, l&#8217;outil possède une grande richesse de fonctionnalités comme le PUB/SUB, l&#8217;<a href="http://sametmax.com/le-compteur-mal-connu-lhyperloglog/">hyperloglog</a> et les sorted sets.</p>
<p>C&#8217;est de ces derniers dont on va parler.</p>
<p>En effet, dernièrement j&#8217;ai dû mettre en place un petit compteur des visiteurs en ligne, et c&#8217;est typiquement quelque chose qui fusille une base de données car ça fait une écriture ou une lecture à chaque changement de vue.</p>
<p>Redis est idéal pour ça, le truc tenant 100 000 écritures / seconde, et perdre 4 secondes d&#8217;historique du compteur en cas de crash est le cadet de mes soucis.</p>
<p>Pour compter les visiteurs, il nous faut non seulement un moyen d&#8217;incrémenter le compteur, mais aussi de le décrémenter quand le visiteur n&#8217;est plus en ligne. J&#8217;utiliserais WAMP, Crossbar pourrait me donner l&#8217;information à tout moment via l&#8217;<a href="http://sametmax.com/pendant-ce-temps-a-vera-cruz/">API meta</a>, mais sur un site HTTP only il n&#8217;y a aucun événement qui nous signale &#8220;l&#8217;utilisateur n&#8217;est plus connecté&#8221;.</p>
<p>Le seule moyen de savoir qu&#8217;un utilisateur n&#8217;est plus connecté est de garder une trace de lui et de la faire expirer quand on n&#8217;a pas de nouvelle depuis un temps supérieur à une durée raisonnable (calculée avec précision ) 10 minutes par l&#8217;<a href="http://sametmax.com/rapport-de-lindms-le-niveau-des-developpeurs/">INDMS</a>).</p>
<p>Problème, on peut bien faire expirer des clés avec Redis, mais on ne peut pas lister ces clés (la fonction <code>keys()</code> le fait mais avec des perfs désastreuses et l&#8217;auteur du logiciel la conseille uniquement pour regarder l&#8217;état de son serveur depuis la console). D&#8217;un autre côté on a des listes, mais l&#8217;expiration concerne toute la liste, pas juste un élément.</p>
<p>Une manière de contourner le problème est d&#8217;utiliser un sorted set, une structure de données Redis qui a les caractéristiques suivantes :</p>
<ul>
<li>Clé/valeur, mais la clé est forcément du texte, et la valeur forcément un chiffre.</li>
<li>Les clés sonts uniques.</li>
<li>Les valeurs peuvent être en doublons.</li>
<li>Les paires sont toujours ordonnées peu importe le nombre d&#8217;insertions.</li>
<li>L&#8217;ordre se fait sur les valeurs, qu&#8217;on appelle le score.</li>
</ul>
<p>En gros, c&#8217;est comme un dictionnaire, mais ordonné, qui ressemble à :</p>

<div class="wp_syntax"><table><tr><td class="code"><pre class="python" style="font-family:monospace;"><span style="color: black;">&#123;</span>
    <span style="color: #483d8b;">'cle'</span>: <span style="color: #ff4500;">789789</span>
    <span style="color: #483d8b;">'cle2'</span>: <span style="color: #ff4500;">78999</span>
    <span style="color: #483d8b;">'cle3'</span>: <span style="color: #ff4500;">6</span>
<span style="color: black;">&#125;</span></pre></td></tr></table></div>

<p>Comme toutes les structures de données un peu exotiques, on se demande à quoi ça sert d&#8217;avoir des caratéristiques aussi précises et ce qu&#8217;on peut bien en faire.</p>
<p>La réponse est comme d&#8217;hab, &#8220;plein de choses&#8221;, mais nous on va en faire un compteur :</p>

<div class="wp_syntax"><table><tr><td class="code"><pre class="python" style="font-family:monospace;"><span style="color: #ff7700;font-weight:bold;">import</span> <span style="color: #dc143c;">datetime</span>
&nbsp;
<span style="color: #ff7700;font-weight:bold;">import</span> redis
&nbsp;
<span style="color: #808080; font-style: italic;"># connection à redis</span>
con <span style="color: #66cc66;">=</span> redis.<span style="color: black;">StrictRedis</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
&nbsp;
<span style="color: #808080; font-style: italic;"># On appelle cette vue avec ajax, ignorant les</span>
<span style="color: #808080; font-style: italic;"># utilisateurs sans JS qui de toute façon sont des </span>
<span style="color: #808080; font-style: italic;"># anarco communistes qui ne rapportent</span>
<span style="color: #808080; font-style: italic;"># pas d'argent et fument des joins</span>
<span style="color: #808080; font-style: italic;"># Cette partie dépend de votre framework</span>
<span style="color: #66cc66;">@</span>ajax<span style="color: black;">&#40;</span><span style="color: #483d8b;">'/counter'</span><span style="color: black;">&#41;</span>
<span style="color: #ff7700;font-weight:bold;">def</span> add_user_count<span style="color: black;">&#40;</span>request<span style="color: black;">&#41;</span>:
&nbsp;
    <span style="color: #808080; font-style: italic;"># la clé d'accès à notre sorted set</span>
    counter_key <span style="color: #66cc66;">=</span> <span style="color: #483d8b;">&quot;users:online&quot;</span>
&nbsp;
    <span style="color: #808080; font-style: italic;"># un id unique pour notre visiteur. Ca dépend du</span>
    <span style="color: #808080; font-style: italic;"># framework web</span>
    user_id <span style="color: #66cc66;">=</span> request.<span style="color: black;">session</span>.<span style="color: #008000;">id</span>
&nbsp;
    <span style="color: #808080; font-style: italic;"># Calcul du timestamp de maintenant et d'il y a 10 minutes</span>
    <span style="color: #808080; font-style: italic;"># car ils vont nous servir de score max et min.</span>
    now <span style="color: #66cc66;">=</span> <span style="color: black;">&#40;</span><span style="color: #dc143c;">datetime</span>.<span style="color: black;">utcnow</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span> - <span style="color: #dc143c;">datetime</span><span style="color: black;">&#40;</span><span style="color: #ff4500;">1970</span><span style="color: #66cc66;">,</span> <span style="color: #ff4500;">1</span><span style="color: #66cc66;">,</span> <span style="color: #ff4500;">1</span><span style="color: black;">&#41;</span><span style="color: black;">&#41;</span>.<span style="color: black;">total_seconds</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
    ten_minutes_ago <span style="color: #66cc66;">=</span> now - <span style="color: black;">&#40;</span><span style="color: #ff4500;">60</span> * <span style="color: #ff4500;">10</span><span style="color: black;">&#41;</span>
&nbsp;
    <span style="color: #808080; font-style: italic;"># On ajoute l'utilisateur dans le sorted set. S'il existait déjà, son</span>
    <span style="color: #808080; font-style: italic;"># ancienne valeur est remplacée, sinon elle est créée. Comme c'est</span>
    <span style="color: #808080; font-style: italic;"># forcément le plus récent timestamp, il est tout en haut du sorted set</span>
    con.<span style="color: black;">zadd</span><span style="color: black;">&#40;</span>counter_key<span style="color: #66cc66;">,</span> now<span style="color: #66cc66;">,</span> user_id<span style="color: black;">&#41;</span>
&nbsp;
    <span style="color: #808080; font-style: italic;"># Le sorted set ressemble donc à ça :</span>
&nbsp;
    <span style="color: #808080; font-style: italic;"># {</span>
&nbsp;
    <span style="color: #808080; font-style: italic;">#     &quot;userid&quot;: 809809890, # timestamp de dernière requete</span>
    <span style="color: #808080; font-style: italic;">#     &quot;userid2&quot;: 809809885, # timestamp plus ancien</span>
    <span style="color: #808080; font-style: italic;">#     &quot;userid3&quot;: 809809880 # timestamp encore plus ancien</span>
    <span style="color: #808080; font-style: italic;">#     ...</span>
    <span style="color: #808080; font-style: italic;"># }</span>
&nbsp;
    <span style="color: #808080; font-style: italic;"># On retire toutes les entrées du sorted set avec un score</span>
    <span style="color: #808080; font-style: italic;"># qui est plus petit que le timestamp d'il y a 10 minutes</span>
    <span style="color: #808080; font-style: italic;"># histoire de pas saturer la mémoire.</span>
    con.<span style="color: black;">zremrangebyscore</span><span style="color: black;">&#40;</span>online_user_key<span style="color: #66cc66;">,</span> <span style="color: #ff4500;">0</span><span style="color: #66cc66;">,</span> ten_minutes_ago<span style="color: black;">&#41;</span>
&nbsp;
    <span style="color: #808080; font-style: italic;"># Et on récupére le nombre de visiteurs en ligne entre maintenant</span>
    <span style="color: #808080; font-style: italic;"># et 10 minutes.</span>
    visitors <span style="color: #66cc66;">=</span> con.<span style="color: black;">zcount</span><span style="color: black;">&#40;</span>online_user_key<span style="color: #66cc66;">,</span> ten_minutes_ago<span style="color: #66cc66;">,</span> now<span style="color: black;">&#41;</span>
&nbsp;
    <span style="color: #ff7700;font-weight:bold;">return</span> visitors</pre></td></tr></table></div>

<p>Notez que toutes ces étapes sont très rapides, que ce soit l&#8217;insertion, la suppression ou le compte du total grâce aux fantastiques perfs de Redis. J&#8217;aime Redis. Je suce la bite de Redis goulument.</p>
<p>La précision n&#8217;est pas parfaite, mais compter les utilisateurs est plus un art (divinatoire) qu&#8217;une science et avoir un chiffre précis à 2, 3% prêt est suffisant pour nous.</p>


feed_id: http://sametmax.com/?p=16696
title: Multiboards est désormais customisable 22
uri: Multiboards-est-désormais-customisable-1
url: http://sametmax.com/multiboards-est-desormais-customisable/
content:
<p>J&#8217;avais pensé à un titre du genre: &#8220;Vous aussi, créez votre propre <a href="http://multiboards.net/">Multiboards</a> et devenez riche&#8221; mais ça risquait de faire légèrement pompeux. On garde donc un titre simple.</p>
<p>Pour ceux qui ne connaissaient pas, Multiboards est une simple page web qui affiche les flux rss de certains sites sélectionnés par nos soins. Après quelques années de service on s&#8217;est aperçu que pas mal de monde l&#8217;utilisait, on a même eu des suggestions d&#8217;améliorations (merci Nain Genieu) et des demandes de libération du code source.<br />
Bon le code c&#8217;est pas pour tout de suite car il est super crade et que Sam acceptera jamais de laisser ça sans passer un bon coup de balais factoriseur XR-2200. Mais en attendant vous pouvez créer vos pages d&#8217;agrégation de flux persos et les partager avec le monde entier ou votre voisine de palier (celle qui a d&#8217;énormes nibards).</p>
<p>Pour créer son board on va sur <a href="http://multiboards.net/build">http://multiboards.net/build</a> et on ajoute ses flux RSS, on peut déplacer les flux dans la grille à sa convenance s&#8217;ils n&#8217;ont pas été rentrés dans le bon ordre. Lorsque l&#8217;on est satisfait avec sa page, on clique sur le bouton vert et on peut voir le mimi, le rara le miracle apparaître sous nos yeux ébahis !</p>
<p><img class="aligncenter size-full wp-image-16697" src="media/5dfb6da3.mb_custo.jpg" data-original-source="http://sametmax.com/wp-content/uploads/2015/07/mb_custo.jpg" alt="mb_custo" width="600" height="355" /></p>
<p>Pour le moment vu que ça a été fait à l&#8217;arrache dans les chiottes de l&#8217;aéroport de Manille y a pas bcp de fonctionnalités marrantes mais lorsque le code sera libéré on espère avoir de jolies contribs ^^.</p>
<p>Quelques idées pour le futur:</p>
<ul>
<li>Ajouter une alerte sur des mots-clef comme&#8230; &#8220;salope&#8221; (c&#8217;est important les mots-clef)</li>
<li>Lorsque une url a été lu l&#8217;indiquer sur la grille en lui attribuant une autre couleur</li>
<li>rajouter des radios ou des fonctionnalités au player</li>
<li>pouvoir customizer ses radios, les news de bas de page et pourquoi pas d&#8217;autres sources d&#8217;images à la place d&#8217;imgur</li>
<li>et plein d&#8217;autres choses qui ne devraient pas exister si nous étions dans un monde parfait.</li>
</ul>
<p>NB:<br />
Le player a été changé aussi, plus de flash et place au HTML5, un petit player maison hyper simple mais qui fait son job.</p>
<p>On attend vos retours, ça doit foisonner de bugs</p>


feed_id: http://sametmax.com/?p=16706
title: cd multiboards; git checkout versionquimarche 6
uri: cd-multiboards-git-checkout-versionquimarche-Recently-updated
url: http://sametmax.com/cd-multiboards-git-checkout-versionquimarche/
content:
<p>Il y a des mises en prod heureuses. Et d&#8217;autres non.</p>
<p>Là c&#8217;est non.</p>
<p>Je rollback jusqu&#8217;à ce qu&#8217;on répare tout ça.</p>
<p><span class='embed-youtube' style='text-align:center; display: block;'><iframe class='youtube-player' type='text/html' width='1170' height='689' src='http://www.youtube.com/embed/M1r4d2UWRPc?version=3&#038;rel=1&#038;fs=1&#038;showsearch=0&#038;showinfo=1&#038;iv_load_policy=1&#038;wmode=transparent' frameborder='0' allowfullscreen='true'></iframe></span></p>


feed_id: http://sametmax.com/?p=16712
title: Attention au télé-achat 30
uri: Attention-au-télé-achat-2-Recently-updated
url: http://sametmax.com/attention-au-tele-achat/
content:
<p>Dès qu&#8217;il y a une tendance, on voit les fanboys sortir leurs dentiers éclatant pour vous faire des démos ventant le produit qui retire toutes les tâches et si vous appelez maintenant il y a 20% de réduction.</p>
<p>Mais tout comme à la télé le magic bullet fait le smoothie en seulement 2 minutes parce que le mec commence la vidéo avec 10 fruits déjà choisis/payés/livrés/lavés/épluchés/découpés, la techno qu&#8217;on vous vend a ses travers.</p>
<p>C&#8217;est normal. Mais c&#8217;est rarement dit.</p>
<p>Ces dernières années, NoSQL, la programmation asynchrone, la programmation fonctionnelle et les projets JS, Node en tête, ont été sur toutes les chaînes : si vous avez pas programmé votre chat avec Mongo + MeteorJS avant 50 ans, vous avez raté votre vie ! Appelez le 5555-5555 et recevez un pins Haskell qui vous créé une copie de votre pull quand vous l&#8217;accrochez.</p>
<p>C&#8217;est pas qu&#8217;il y a une arnaque, c&#8217;est juste qu&#8217;il y a un truc qu&#8217;on appelle la réalité.</p>
<p>Par exemple, aujourd&#8217;hui, je tombe sur un projet appelé <a href="https://github.com/ekampf/pymaybe">pymaybe</a> qui implémente le pattern fonctionnel monadique maybe. Je déconne pas, c&#8217;est le terme technique.</p>
<p>Je taquine, mais je trouve le projet sincèrement intéressant. Néanmoins la page d&#8217;exemple reflète typiquement l&#8217;esprit Pierre Bellemare.</p>
<p>Ca commence par un exemple Python soit disant trop long :</p>

<div class="wp_syntax"><table><tr><td class="code"><pre class="python" style="font-family:monospace;"><span style="color: #ff7700;font-weight:bold;">def</span> get_user_name<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>:
    current_user <span style="color: #66cc66;">=</span> request.<span style="color: #dc143c;">user</span>
    <span style="color: #ff7700;font-weight:bold;">if</span> current_user:
        <span style="color: #ff7700;font-weight:bold;">return</span> current_user.<span style="color: black;">name</span>
&nbsp;
    <span style="color: #ff7700;font-weight:bold;">return</span> <span style="color: #483d8b;">''</span></pre></td></tr></table></div>

<p>Converti en :</p>

<div class="wp_syntax"><table><tr><td class="code"><pre class="python" style="font-family:monospace;"><span style="color: #ff7700;font-weight:bold;">def</span> get_user_name<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span> :
    <span style="color: #ff7700;font-weight:bold;">return</span> maybe<span style="color: black;">&#40;</span>request.<span style="color: #dc143c;">user</span><span style="color: black;">&#41;</span>.<span style="color: black;">name</span>.<span style="color: black;">or_else</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">''</span><span style="color: black;">&#41;</span></pre></td></tr></table></div>

<p>Mais c&#8217;est très maladroit, car on résout ici un problème qui n&#8217;existe pas. En effet, personne écrivant du Python professionnellement (et qui serait donc intéressé par ce projet) n&#8217;écrirait le premier exemple ainsi. Un code de prod serait :</p>

<div class="wp_syntax"><table><tr><td class="code"><pre class="python" style="font-family:monospace;"><span style="color: #ff7700;font-weight:bold;">def</span> get_user_name<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>:
    <span style="color: #ff7700;font-weight:bold;">return</span> <span style="color: #008000;">getattr</span><span style="color: black;">&#40;</span>request.<span style="color: #dc143c;">user</span><span style="color: #66cc66;">,</span> <span style="color: #483d8b;">'name'</span><span style="color: #66cc66;">,</span> <span style="color: #483d8b;">''</span><span style="color: black;">&#41;</span></pre></td></tr></table></div>

<p>Ce qui est plus court que les deux snippets ci-dessus, immédiatement reconnu par des pairs, et ne demande pas d&#8217;ajouter une dépendance externe à son code.</p>
<p>Le second exemple est plus trompeur encore, et commence par cet extrait :</p>

<div class="wp_syntax"><table><tr><td class="code"><pre class="python" style="font-family:monospace;">stores <span style="color: #66cc66;">=</span> json.<span style="color: black;">get</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'stores'</span><span style="color: black;">&#41;</span>
<span style="color: #ff7700;font-weight:bold;">if</span> stores:
    products <span style="color: #66cc66;">=</span> stores<span style="color: black;">&#91;</span><span style="color: #ff4500;">0</span><span style="color: black;">&#93;</span>.<span style="color: black;">get</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'products'</span><span style="color: black;">&#41;</span>
    <span style="color: #ff7700;font-weight:bold;">if</span> products:
        product_name <span style="color: #66cc66;">=</span> products<span style="color: black;">&#91;</span><span style="color: #ff4500;">0</span><span style="color: black;">&#93;</span>.<span style="color: black;">get</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'details'</span><span style="color: #66cc66;">,</span> <span style="color: black;">&#123;</span><span style="color: black;">&#125;</span><span style="color: black;">&#41;</span>.<span style="color: black;">get</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'name'</span><span style="color: black;">&#41;</span> <span style="color: #ff7700;font-weight:bold;">or</span> <span style="color: #483d8b;">'unknown'</span></pre></td></tr></table></div>

<p>Et avec &#8220;coder builder 3000&#8243; vous pouvez faire :</p>

<div class="wp_syntax"><table><tr><td class="code"><pre class="python" style="font-family:monospace;">product_name <span style="color: #66cc66;">=</span> maybe<span style="color: black;">&#40;</span>stores<span style="color: black;">&#41;</span><span style="color: black;">&#91;</span><span style="color: #ff4500;">0</span><span style="color: black;">&#93;</span><span style="color: black;">&#91;</span><span style="color: #483d8b;">'products'</span><span style="color: black;">&#93;</span><span style="color: black;">&#91;</span><span style="color: #ff4500;">0</span><span style="color: black;">&#93;</span><span style="color: black;">&#91;</span><span style="color: #483d8b;">'details'</span><span style="color: black;">&#93;</span><span style="color: black;">&#91;</span><span style="color: #483d8b;">'name'</span><span style="color: black;">&#93;</span>.<span style="color: black;">or_else</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'unknown'</span><span style="color: black;">&#41;</span></pre></td></tr></table></div>

<p>Impressionant ! C&#8217;est la barre de fer !</p>
<p>Sauf que&#8230; Avez-vous remarqué ? Le json.get() est passé à la trappe&#8230;</p>

<div class="wp_syntax"><table><tr><td class="code"><pre class="python" style="font-family:monospace;">stores <span style="color: #66cc66;">=</span> json.<span style="color: black;">get</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'stores'</span><span style="color: black;">&#41;</span>
product_name <span style="color: #66cc66;">=</span> maybe<span style="color: black;">&#40;</span>stores<span style="color: black;">&#41;</span><span style="color: black;">&#91;</span><span style="color: #ff4500;">0</span><span style="color: black;">&#93;</span><span style="color: black;">&#91;</span><span style="color: #483d8b;">'products'</span><span style="color: black;">&#93;</span><span style="color: black;">&#91;</span><span style="color: #ff4500;">0</span><span style="color: black;">&#93;</span><span style="color: black;">&#91;</span><span style="color: #483d8b;">'details'</span><span style="color: black;">&#93;</span><span style="color: black;">&#91;</span><span style="color: #483d8b;">'name'</span><span style="color: black;">&#93;</span>.<span style="color: black;">or_else</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'unknown'</span><span style="color: black;">&#41;</span></pre></td></tr></table></div>

<p>Et puis d&#8217;où vient <code>maybe()</code> ? Ce n&#8217;est pas un builtin&#8230; Donc il faut l&#8217;importer, comme pour les fruits du mixer qui ne se découpent pas tout seuls.</p>

<div class="wp_syntax"><table><tr><td class="code"><pre class="python" style="font-family:monospace;"><span style="color: #ff7700;font-weight:bold;">from</span> pymaybe <span style="color: #ff7700;font-weight:bold;">import</span> maybe
stores <span style="color: #66cc66;">=</span> json.<span style="color: black;">get</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'stores'</span><span style="color: black;">&#41;</span>
product_name <span style="color: #66cc66;">=</span> maybe<span style="color: black;">&#40;</span>stores<span style="color: black;">&#41;</span><span style="color: black;">&#91;</span><span style="color: #ff4500;">0</span><span style="color: black;">&#93;</span><span style="color: black;">&#91;</span><span style="color: #483d8b;">'products'</span><span style="color: black;">&#93;</span><span style="color: black;">&#91;</span><span style="color: #ff4500;">0</span><span style="color: black;">&#93;</span><span style="color: black;">&#91;</span><span style="color: #483d8b;">'details'</span><span style="color: black;">&#93;</span><span style="color: black;">&#91;</span><span style="color: #483d8b;">'name'</span><span style="color: black;">&#93;</span>.<span style="color: black;">or_else</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'unknown'</span><span style="color: black;">&#41;</span></pre></td></tr></table></div>

<p>Et si vous avez un editeur bien réglé, la 3eme ligne va lever un warning PEP8 : 85 charaters, c&#8217;est 5 de trop (sinon on peut écrire tout son script sur une ligne aussi).</p>

<div class="wp_syntax"><table><tr><td class="code"><pre class="python" style="font-family:monospace;"><span style="color: #ff7700;font-weight:bold;">from</span> pymaybe <span style="color: #ff7700;font-weight:bold;">import</span> maybe
stores <span style="color: #66cc66;">=</span> json.<span style="color: black;">get</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'stores'</span><span style="color: black;">&#41;</span>
product_name <span style="color: #66cc66;">=</span> maybe<span style="color: black;">&#40;</span>stores<span style="color: black;">&#41;</span><span style="color: black;">&#91;</span><span style="color: #ff4500;">0</span><span style="color: black;">&#93;</span><span style="color: black;">&#91;</span><span style="color: #483d8b;">'products'</span><span style="color: black;">&#93;</span><span style="color: black;">&#91;</span><span style="color: #ff4500;">0</span><span style="color: black;">&#93;</span><span style="color: black;">&#91;</span><span style="color: #483d8b;">'details'</span><span style="color: black;">&#93;</span><span style="color: black;">&#91;</span><span style="color: #483d8b;">'name'</span><span style="color: black;">&#93;</span>
product_name <span style="color: #66cc66;">=</span> product_name.<span style="color: black;">or_else</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'unknown'</span><span style="color: black;">&#41;</span></pre></td></tr></table></div>

<p>Ah, plus qu&#8217;une seule ligne d&#8217;avantage par rapport à la version originale. Mais une seconde, cette version vous l&#8217;écririez ainsi ? Bien entendu que non :</p>

<div class="wp_syntax"><table><tr><td class="code"><pre class="python" style="font-family:monospace;"><span style="color: #ff7700;font-weight:bold;">try</span>:
    product_name <span style="color: #66cc66;">=</span> json.<span style="color: black;">get</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'stores'</span><span style="color: black;">&#41;</span><span style="color: black;">&#91;</span><span style="color: #ff4500;">0</span><span style="color: black;">&#93;</span><span style="color: black;">&#91;</span><span style="color: #483d8b;">'products'</span><span style="color: black;">&#93;</span><span style="color: black;">&#91;</span><span style="color: #ff4500;">0</span><span style="color: black;">&#93;</span><span style="color: black;">&#91;</span><span style="color: #483d8b;">'details'</span><span style="color: black;">&#93;</span><span style="color: black;">&#91;</span><span style="color: #483d8b;">'name'</span><span style="color: black;">&#93;</span>
<span style="color: #ff7700;font-weight:bold;">except</span> <span style="color: black;">&#40;</span><span style="color: #008000;">TypeError</span><span style="color: #66cc66;">,</span> <span style="color: #008000;">KeyError</span><span style="color: #66cc66;">,</span> <span style="color: #008000;">IndexError</span><span style="color: black;">&#41;</span>:
    product_name <span style="color: #66cc66;">=</span> <span style="color: #483d8b;">'unknown'</span></pre></td></tr></table></div>

<p>Ce qui a en prime l&#8217;avantage de montrer explicitement les cas dans lequels <code>product_name = 'unknown'</code>, plutôt que de devoir savoir ce que fait exactement une lib tierce partie (qui doit en plus être déployée). Et si il y a une erreur en plus qu&#8217;il faut gérer ? <code>TypeError</code> ? <code>IoError</code> ? C&#8217;est juste une clause de plus. Pour l&#8217;autre version, on rajoute 4 lignes.</p>
<p>Attendez, ce n&#8217;est pas fini. Il est facile de croire, quand on vient d&#8217;un autre langage, que Python est un langage simpliste. Il se traine sa réputation de langage jouet depuis bien longtemps. Erreur classique !</p>
<p>Car Python est très simple à prendre en main, mais possède énormément de profondeur.</p>
<p><code>__getitem__</code>, anyone ?</p>

<div class="wp_syntax"><table><tr><td class="code"><pre class="python" style="font-family:monospace;"><span style="color: black;">&#40;</span><span style="color: #dc143c;">test</span><span style="color: black;">&#41;</span> <span style="color: #66cc66;">&gt;&gt;&gt;</span> <span style="color: #ff7700;font-weight:bold;">class</span> Test:
    <span style="color: #ff7700;font-weight:bold;">def</span> <span style="color: #0000cd;">__getitem__</span><span style="color: black;">&#40;</span><span style="color: #008000;">self</span><span style="color: #66cc66;">,</span> value<span style="color: black;">&#41;</span>:
        <span style="color: #ff7700;font-weight:bold;">return</span> <span style="color: #ff4500;">1</span>
      ...:     
&nbsp;
<span style="color: black;">&#40;</span><span style="color: #dc143c;">test</span><span style="color: black;">&#41;</span> <span style="color: #66cc66;">&gt;&gt;&gt;</span> maybe<span style="color: black;">&#40;</span>Test<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span><span style="color: black;">&#41;</span><span style="color: black;">&#91;</span><span style="color: #ff4500;">0</span><span style="color: black;">&#93;</span>
---------------------------------------------------------------------------
<span style="color: #008000;">AttributeError</span>                            Traceback <span style="color: black;">&#40;</span>most recent call last<span style="color: black;">&#41;</span>
<span style="color: #66cc66;">&lt;</span>ipython-input-<span style="color: #ff4500;">6</span>-e4a755e77e5b<span style="color: #66cc66;">&gt;</span> <span style="color: #ff7700;font-weight:bold;">in</span> <span style="color: #66cc66;">&lt;</span>module<span style="color: #66cc66;">&gt;</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
----<span style="color: #66cc66;">&gt;</span> <span style="color: #ff4500;">1</span> maybe<span style="color: black;">&#40;</span>Test<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span><span style="color: black;">&#41;</span><span style="color: black;">&#91;</span><span style="color: #ff4500;">0</span><span style="color: black;">&#93;</span>
&nbsp;
/home/sam/.<span style="color: black;">local</span>/share/virtualenvs/<span style="color: #dc143c;">test</span>/local/lib/python2.7/site-packages/pymaybe/<span style="color: #0000cd;">__init__</span>.<span style="color: black;">pyc</span> <span style="color: #ff7700;font-weight:bold;">in</span> <span style="color: #0000cd;">__getitem__</span><span style="color: black;">&#40;</span><span style="color: #008000;">self</span><span style="color: #66cc66;">,</span> key<span style="color: black;">&#41;</span>
    <span style="color: #ff4500;">212</span> 
    <span style="color: #ff4500;">213</span>     <span style="color: #ff7700;font-weight:bold;">def</span> <span style="color: #0000cd;">__getitem__</span><span style="color: black;">&#40;</span><span style="color: #008000;">self</span><span style="color: #66cc66;">,</span> key<span style="color: black;">&#41;</span>:
--<span style="color: #66cc66;">&gt;</span> <span style="color: #ff4500;">214</span>         <span style="color: #ff7700;font-weight:bold;">return</span> maybe<span style="color: black;">&#40;</span><span style="color: #008000;">self</span>.__value.<span style="color: black;">get</span><span style="color: black;">&#40;</span>key<span style="color: #66cc66;">,</span> <span style="color: #008000;">None</span><span style="color: black;">&#41;</span><span style="color: black;">&#41;</span>
    <span style="color: #ff4500;">215</span> 
    <span style="color: #ff4500;">216</span>     <span style="color: #ff7700;font-weight:bold;">def</span> <span style="color: #0000cd;">__setitem__</span><span style="color: black;">&#40;</span><span style="color: #008000;">self</span><span style="color: #66cc66;">,</span> key<span style="color: #66cc66;">,</span> value<span style="color: black;">&#41;</span>:
&nbsp;
<span style="color: #008000;">AttributeError</span>: Test instance has no attribute <span style="color: #483d8b;">'get'</span>
&nbsp;
<span style="color: black;">&#40;</span><span style="color: #dc143c;">test</span><span style="color: black;">&#41;</span> <span style="color: #66cc66;">&gt;&gt;&gt;</span> Test<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span><span style="color: black;">&#91;</span><span style="color: #ff4500;">0</span><span style="color: black;">&#93;</span>
           <span style="color: #ff4500;">1</span></pre></td></tr></table></div>

<p><code>defaultdict</code> ?</p>

<div class="wp_syntax"><table><tr><td class="code"><pre class="python" style="font-family:monospace;"><span style="color: black;">&#40;</span><span style="color: #dc143c;">test</span><span style="color: black;">&#41;</span> <span style="color: #66cc66;">&gt;&gt;&gt;</span> maybe<span style="color: black;">&#40;</span>defaultdict<span style="color: black;">&#40;</span><span style="color: #dc143c;">datetime</span>.<span style="color: black;">utcnow</span><span style="color: black;">&#41;</span><span style="color: black;">&#41;</span><span style="color: black;">&#91;</span><span style="color: #ff4500;">0</span><span style="color: black;">&#93;</span>
           <span style="color: #008000;">None</span>
&nbsp;
<span style="color: black;">&#40;</span><span style="color: #dc143c;">test</span><span style="color: black;">&#41;</span> <span style="color: #66cc66;">&gt;&gt;&gt;</span> defaultdict<span style="color: black;">&#40;</span><span style="color: #dc143c;">datetime</span>.<span style="color: black;">utcnow</span><span style="color: black;">&#41;</span><span style="color: black;">&#91;</span><span style="color: #ff4500;">0</span><span style="color: black;">&#93;</span>
           <span style="color: #dc143c;">datetime</span>.<span style="color: #dc143c;">datetime</span><span style="color: black;">&#40;</span><span style="color: #ff4500;">2015</span><span style="color: #66cc66;">,</span> <span style="color: #ff4500;">7</span><span style="color: #66cc66;">,</span> <span style="color: #ff4500;">30</span><span style="color: #66cc66;">,</span> <span style="color: #ff4500;">19</span><span style="color: #66cc66;">,</span> <span style="color: #ff4500;">39</span><span style="color: #66cc66;">,</span> <span style="color: #ff4500;">28</span><span style="color: #66cc66;">,</span> <span style="color: #ff4500;">298488</span><span style="color: black;">&#41;</span></pre></td></tr></table></div>

<p>Cela signifie-t-il que ce projet ne mérite pas d&#8217;être suivi ? Pas du tout, le principe est sympas, ça doit être creusé. Simplement il faut se méfier du mimi, du rara, du miracle.</p>
<p>Si vous relisez les articles sur WAMP et crossbar, vous noterez que je souligne clairement les problèmes de la techno : doc, debugging, syntax twisted&#8230; Personne ne demande d&#8217;être impartial, mais faut pas non plus vendre la vessie de l&#8217;ours avant la lanterne des boeufs.</p>


feed_id: http://sametmax.com/?p=16725
title: Le don du mois : Wikimedia 3
uri: Le-don-du-mois-Wikimedia-1
url: http://sametmax.com/le-don-du-mois-wikimedia/
content:
<p>Oui, y a des dons plus ballots que d&#8217;autres.</p>
<p>Certes, les campagnes de pub de Wikimedia, la fondation derrière Wikipedia, pour collecter les dons me gonflent profondément. J&#8217;ai l&#8217;impression d&#8217;assister au téléthon, mais online. </p>
<p>Mais détail ennuyeux mis à part, j&#8217;utilise, comme aujourd&#8217;hui presque tout le monde, Wikipedia en tant que base de départ pour aborder de nombreux sujets afin ensuite de glisser vers d&#8217;autres sources plus spécialisées. </p>
<p>Beaucoup de choses ont été dites sur le projet : remise en cause de sa fiabilité, de l&#8217;intention réelle de ses dirigeants, des spams des lobbyistes qui s&#8217;y installent, de la rigidité des règles et des modos ou encore de la courbe d&#8217;apprentissage de plus en plus grande pour y participer.</p>
<p>Malgré cela, j&#8217;ai contribué plusieurs articles, ai fréquenté des gens qui sont engagés au cœur de de l&#8217;outil et ai bénéficié de l&#8217;outil à un niveau personnel et professionnel.</p>
<p>C&#8217;est un magnifique projet.</p>
<p>Plein de défauts, mais vu l&#8217;envergure, comment ne pas en avoir ?</p>
<p>J&#8217;espère qu&#8217;il vivra le plus longtemps possible. Et je fais une <a href="https://wikimediafoundation.org/wiki/Ways_to_Give#bitcoin">donation en bitcoins</a> d&#8217;environ 50 dollars.</p>


feed_id: http://sametmax.com/?p=16735
title: 15 jours afk 6
uri: 15-jours-afk-1
url: http://sametmax.com/15-jours-afk/
content:
<p>Vous avez l&#8217;habitude maintenant, Sam se barre, Max fera son Max et en attendant, profitez bien de l&#8217;été !</p>


feed_id: http://sametmax.com/?p=16741
title: Multiboards Customizable – Le retour de la mort de la peur de mourir 18
uri: Multiboards-Customizable-Le-retour-de-la-mort-de-la-peur-de-mourir
url: http://sametmax.com/multiboards-customizable-le-retour-de-la-mort-de-la-peur-de-mourir/
content:
<p>Pour ceux qui ont suivi la lamentable mais néanmoins <a href="http://sametmax.com/multiboards-est-desormais-customisable/">poilante mise en prod de la nouvelle version buggée de MB </a> j&#8217;ai du reporter un rendez-vous avec mon escort pour réparer le bébé.</p>
<p>Normalement ça devrait marcher (ça valait pas un post mais c&#8217;est pour remplir un peu plus les BAL des abonnés&#8230;)</p>
<p>Rendez-vous <a href="http://www.multiboards.net/">ici</a> ou <a href="http://www.multiboards.net/">là</a> ou encore <a href="http://www.multiboards.net/">là</a> et donnez-moi vos impressions si comme moi vous n&#8217;avez pas d&#8217;ami et passez votre temps à la maison devant l&#8217;ordi au lieu de vous dorer la pilule entouré de femmes fontaines sur une île déserte.</p>
<p><a href="http://fluxrss.fr/">Une source de flux RSS pas mal ici.<br />
</a></p>
<p>PS: Normalement dans l&#8217;onglet en haut à droite il devrait y avoir la liste des boards populaires, on calcule ça en fonction du nombre de <a href="http://sametmax.com/utilisateurs-en-ligne-avec-le-sorted-set-de-redis/">visiteurs en ligne en temps réel</a>, Sam avait fait un article dessus.</p>
<p>Et une bonne fin qui pourrait faire brève de comptoir:<br />
&#8220;Tant que le board n&#8217;est pas populaire il reste anonyme&#8221; (et donc ne s&#8217;affiche pas dans l&#8217;onglet) ^^</p>


feed_id: http://sametmax.com/?p=16759
title: C’est bientôt la rentrée, on va réviser ! 6
uri: C-est-bientôt-la-rentrée-on-va-réviser-Recently-updated
url: http://sametmax.com/cest-bientot-la-rentree-on-va-reviser/
content:
<p>914 articles publiés sur ce blog, on a la classe ou on ne l&#8217;a pas, que voulez-vous. Le 1000eme n&#8217;est pas loin :)</p>
<p>Mais j&#8217;ai des projets assez lourds en vue, et donc peut être aurais-je moins de temps pour créer de nouveaux articles.</p>
<p>Que faire donc ? Je ne vais pas vous laisser en plan, comme ça, la bite derrière l&#8217;oreille (et/ou la chatte au vent, selon votre anatomie)&#8230;</p>
<p>En regardant l&#8217;histo, il m&#8217;a frappé que beaucoup de posts commencent à dater. Donc cette année, remise à niveau, je vais faire des updates par ordre chronologique des articles sur Python.</p>
<p>D&#8217;abord, passage à Python 3, of course. Et puis un lifting des libs utilisées, des idiomes et bonnes pratiques. Bref, le ravalement de façade, pore par pore.</p>
<p>Je n&#8217;oublie pas que j&#8217;ai toujours le dossier sur les tests à finir, ne vous inquiétez pas.</p>


feed_id: http://sametmax.com/?p=16779
title: Mise à jour de l’article sur les listes en intension 10
uri: Mise-à-jour-de-l-article-sur-les-listes-en-intension-2-Recently
url: http://sametmax.com/mise-a-jour-de-larticle-sur-les-listes-en-intension/
content:
<p>Aujourd&#8217;hui j&#8217;ai dépoussiéré la <a href="http://sametmax.com/python-love-les-listes-en-intention-partie/">partie 1</a> et <a href="http://sametmax.com/python-love-les-listes-en-intention-partie-2/">partie 2</a> des articles sur les listes en intension.</p>
<p>Tout est donc passé à Python 3, et j&#8217;ai rajouté quelques liens ou précisions ici et là.</p>
<p>Si vous ne savez pas encore ce que fait <code>[sum(line) for line in data if line[0] > 4]</code>, l&#8217;article n&#8217;attend que vous :)</p>
<p>Et on m&#8217;a fait remarquer en comment que dernièrement le blog était devenu plus sage. </p>
<p>Alors sans plus de transition, voici une cam amateur d&#8217;un salon du cul. Car j&#8217;imagine que certains pensent que c&#8217;est un mythe ou croient que les salons de l&#8217;érotisme en France sont super hot&#8230;</p>
<p><iframe src="http://flashservice.xvideos.com/embedframe/2280340" frameborder=0 width=510 height=400 scrolling=no></iframe></p>
<p> De rien.</p>


feed_id: http://sametmax.com/?p=16796
title: Article sur les raw strings mis à jour
uri: Article-sur-les-raw-strings-mis-à-jour
url: http://sametmax.com/article-sur-les-raw-strings-mis-a-jour/
content:
<p>Bon celui là c&#8217;était un facile : y avait juste à changer les prints. Histoire de pas faire ma loque, j&#8217;ai rajouté un petit paragraphe sur les combinaisons de prefixes.</p>
<p>Donc si vous croyez toujours que <code>r'truc'</code> c&#8217;est que pour les regexs, <a href="http://sametmax.com/comment-marchent-les-raw-strings-en-python/">lisez l&#8217;article.</a></p>
<p>Ah, oui, j&#8217;oubliais : </p>
<p><iframe width="590" height="480" src="http://www.sextvx.com/fr/embed/786035" frameborder="0" scrolling="no"></iframe></p>
<p>Si vous êtes curieux de savoir ce qui se passe quand on met sa main au lieu de sa bite dans un glory hole.</p>


feed_id: http://sametmax.com/?p=16801
title: Désactiver un service dans Ubuntu 14.04 11
uri: Désactiver-un-service-dans-Ubuntu-14-04-2-Recently-updated
url: http://sametmax.com/desactiver-un-service-dans-ubuntu-14-04/
content:
<p>Quand on installe un service, Ubuntu le lance automatiquement, et installe de quoi le faire démarer à chaque boot de la machine.</p>
<p>Si c&#8217;est votre machine de dev, vous ne voulez peut être pas qu&#8217;Apache, Nginx, Redis, MySQL, ElasticSearch, Solr, Supervisor, Postgres, Docker, et MongoDB soient up à chaque fois tous en même temps alors que vous voulez juste vous palucher sur youjizz.</p>
<p>La plupart des scripts de démarrage sont dans /etc/initd.d:</p>

<div class="wp_syntax"><table><tr><td class="code"><pre class="bash" style="font-family:monospace;"><span style="color: #c20cb9; font-weight: bold;">ls</span> <span style="color: #000000; font-weight: bold;">/</span>etc<span style="color: #000000; font-weight: bold;">/</span>init.d
acpid           brltty             grub-common  lpd         pppd-dns      resolvconf   skeleton           unattended-upgrades
anacron         console-setup      halt...</pre></td></tr></table></div>

<p>Mais ces scripts sont lancés parce qu&#8217;ils sont symlinkés dans un des répertoires <code>/etc/rcX</code>:</p>

<div class="wp_syntax"><table><tr><td class="code"><pre class="bash" style="font-family:monospace;"><span style="color: #c20cb9; font-weight: bold;">ls</span> <span style="color: #000000; font-weight: bold;">/</span>etc<span style="color: #000000; font-weight: bold;">/</span>rc<span style="color: #000000; font-weight: bold;">*</span>.d<span style="color: #000000; font-weight: bold;">/*</span>apache<span style="color: #000000; font-weight: bold;">*</span>
<span style="color: #000000; font-weight: bold;">/</span>etc<span style="color: #000000; font-weight: bold;">/</span>rc0.d<span style="color: #000000; font-weight: bold;">/</span>K80apache2  <span style="color: #000000; font-weight: bold;">/</span>etc<span style="color: #000000; font-weight: bold;">/</span>rc5.d<span style="color: #000000; font-weight: bold;">/</span>K80apache2  <span style="color: #000000; font-weight: bold;">/</span>etc<span style="color: #000000; font-weight: bold;">/</span>rc6.d<span style="color: #000000; font-weight: bold;">/</span>K80apache2</pre></td></tr></table></div>

<p>Ouai, j&#8217;ai encore des clients qui utilisent apache. Certains utilisent même Tomcat.</p>
<p>Pour désactiver le démarrage automatique, vous pouvez essayer bourinement la suppression des liens :</p>

<div class="wp_syntax"><table><tr><td class="code"><pre class="bash" style="font-family:monospace;"><span style="color: #c20cb9; font-weight: bold;">sudo</span> <span style="color: #c20cb9; font-weight: bold;">rm</span> $<span style="color: #7a0874; font-weight: bold;">&#40;</span><span style="color: #c20cb9; font-weight: bold;">ls</span> <span style="color: #000000; font-weight: bold;">/</span>etc<span style="color: #000000; font-weight: bold;">/</span>rc<span style="color: #000000; font-weight: bold;">*</span>.d<span style="color: #000000; font-weight: bold;">/*</span>apache<span style="color: #000000; font-weight: bold;">*</span><span style="color: #7a0874; font-weight: bold;">&#41;</span></pre></td></tr></table></div>

<p>Ça marche. </p>
<p>Solution plus pratique, utiliser la commande <code>update-rc.d</code>, qui n&#8217;est pas faite pour l&#8217;utilisation par les humains, mais fuck it :</p>

<div class="wp_syntax"><table><tr><td class="code"><pre class="bash" style="font-family:monospace;"><span style="color: #c20cb9; font-weight: bold;">sudo</span> update-rc.d <span style="color: #660033;">-f</span> apache2 remove</pre></td></tr></table></div>

<p>Ça supprime aussi tous les liens. <code>sudo update-rc.d -f apache2 disable</code> devrait le faire moins bourinement mais ne marche pas sur ma machine.</p>
<p>Pour inverser la tendance :</p>

<div class="wp_syntax"><table><tr><td class="code"><pre class="bash" style="font-family:monospace;"><span style="color: #c20cb9; font-weight: bold;">sudo</span> update-rc.d apache2 defaults</pre></td></tr></table></div>

<p>(ou enable si par miracle ça marche pour vous)</p>
<p>Enfin, si le cœur vous en dit:</p>

<div class="wp_syntax"><table><tr><td class="code"><pre class="bash" style="font-family:monospace;"><span style="color: #c20cb9; font-weight: bold;">sudo</span> update-rc.d <span style="color: #660033;">-f</span> apache2 remove
<span style="color: #c20cb9; font-weight: bold;">sudo</span> sysv-rc-conf</pre></td></tr></table></div>

<p>Vous donnera une joli interface curse pour le faire plus visuellement.</p>


feed_id: http://sametmax.com/?p=16817
title: Update du tuto sur les décorateurs 1
uri: Update-du-tuto-sur-les-décorateurs
url: http://sametmax.com/update-du-tuto-sur-les-decorateurs/
content:
<p>Lui aussi est en <a href="http://sametmax.com/comprendre-les-decorateurs-python-pas-a-pas-partie-1/">deux</a> <a href="http://sametmax.com/comprendre-les-decorateur-python-pas-a-pas-partie-2/">parties</a>. La première était facile : uniquement les prints à changer. Les features de prog fonctionnelle n&#8217;ont pas beaucoup évolué entre Python 2 et 3 il faut dire.</p>
<p>Pour la partie 2, j&#8217;ai changé quelques phrases, mais surtout un exemple qui avait pété, non pas à cause de Python 3, mais parce qu&#8217;il tapait sur une page dont le contenu avait changé.</p>
<p>Bref, si vous avez envie de savoir comment écrire vos propres décorateurs, vous savez quoi faire.</p>
<p>Et sur une note complètement différente, voici à quoi ressemble un vaccum bed, une  pratique sexuelle très particulière où la personne est installée dans un sceau de plastique qu&#8217;on l&#8217;on met sous vide pour le faire coller à la peau :</p>
<p><iframe width="510" height="400" src="http://xhamster.com/xembed.php?video=1973829" frameborder="0" scrolling="no" allowfullscreen></iframe></p>
<p>Ensuite chacun fait ce qu&#8217;il veut : anneau vibrant, dildo, huile chaude&#8230;</p>


feed_id: http://sametmax.com/?p=16835
title: Réécriture de l’article sur les closures 5
uri: Réécriture-de-l-article-sur-les-closures-2-Recently-updated
url: http://sametmax.com/reecriture-de-larticle-sur-les-closures/
content:
<p>Autant quelques articles précédents n’ont pas demandé de gros efforts de concentration, autant celui-ci a été une refonte totale.</p>
<p>En fait, un lecteur m’avait signalé en commentaire que je disais connerie sur connerie. </p>
<p>Vous pouvez croire que je suis têtu, mais mine de rien j’écoute ce qu’on me dit, et j’ai donc réécrit l’intégralité du billet.</p>
<p>Ce n’est pas une notion super importante en Python, mais si la curiosité vous pique, allez lire l’<a href="http://sametmax.com/closure-en-python-et-javascript/">article</a>.</p>
<p>Passons maintenant du coq à l’âne, ou devrais-je dire aux cocks à taille d’ânes avec une petite chubby et son partenaire portant un cock-ring :</p>
<p><iframe src=" http://www.youjizz.com/videos/embed/2173135 » frameborder=" 0" width="704" height="550" scrolling="no" allowtransparency="true"></iframe></p>
<p>Le cock ring, c’est le machin bleu en fin de vidéo, c’est un anneau, qui peut exister en modèle vibrant ou non, qui permet de varier les plaisirs. Je déteste ça, je trouve que ça fait mal à la teub, mais tous les gouts sont dans la nature.</p>


feed_id: http://sametmax.com/?p=16854
title: Édition du post sur le parcours par morceaux
uri: Édition-du-post-sur-le-parcours-par-morceaux
url: http://sametmax.com/edition-du-post-sur-le-parcours-par-morceaux/
content:
<p>C’est que je vais finir par manquer de synonymes pour les titres ^^</p>
<p>Bref, c’était <a href="http://sametmax.com/parcourir-un-iterable-par-morceaux-en-python/">un petit article</a> qui donnait une recette de parcours d’un itérable par morceaux.</p>
<p>Au programme :</p>
<ul>
<li>Correction de l’API de l’itérateur (__next__, next).</li>
<li>On remplace xrange() par range().</li>
<li>open() prend l’encoding en paramètre.</li>
<li>Mise à jour du code montré pour retourner des tuples.</li>
<li>Quelques corrections d’orthographe.</li>
<li>Ajout de liens vers quelques notions expliquées ailleurs sur le blog.</li>
</ul>
<p>Ce n’est pas dans le top 10 des trucs à lire à tout prix, mais c’est instructif si vous avez 10 minutes à tuer.</p>
<p>Puis une petit vidéo &#8211; c&#8217;est devenu traditionnel &#8211; qui illustre c&#8217;est qu&#8217;est un fish net (la tenue de la miss) :</p>
<p><iframe src=http://www.youporn.com/embed/132732/blonde-fishnets-suck-fuck-anal/ frameborder=0 height='340' width='560' scrolling=no name='yp_embed_video'></iframe></p>
<p>C&#8217;est l&#8217;exemple typique du truc sexy en théorie et super pourri en pratique : ça racle, ça gratte, ça irrite, et on peut pas caresser ou trouver une prise pour les mains. Super frustrant.</p>


feed_id: http://sametmax.com/?p=16864
title: Enrichissement du tuto sur l’opérateur splat 8
uri: Enrichissement-du-tuto-sur-l-opérateur-splat-1
url: http://sametmax.com/enrichissement-du-tuto-sur-loperateur-splat/
content:
<p>Ça, c’est un <a href="http://sametmax.com/operateur-splat-ou-etoile-en-python/">super tuto</a> que vous ne trouverez pas ailleurs. Et maintenant il est encore plus super, car je lui ai ajouté l’unpacking étendu de Python 3, et l’unpacking généralisé de Python 3.5. Et en prime les modifications cosmétiques et pratiques habituelles.</p>
<p>Si vous ne l&#8217;avez pas lu, sautez dessus, il est formidable !</p>
<p>En parlant de sauter, ne sautons pas un repas :</p>
<p><iframe src="http://flashservice.xvideos.com/embedframe/654951" frameborder=0 width=510 height=400 scrolling=no></iframe></p>
<p>Et oui ! Le foot fetish est une niche très porteuse.</p>


feed_id: http://sametmax.com/?p=16872
title: Le don du mois : Open Street Map 14
uri: Le-don-du-mois-Open-Street-Map-5-Recently-updated
url: http://sametmax.com/le-don-du-mois-open-street-map/
content:
<p>Soyons clair : j’utilise peu <a href="http://openstreetmap.org/">OSM</a>. Je ne code pas souvent des applications qui ont besoin de données géographiques, et quand je cherche quelque chose, j’ai encore le réflexe Google Map.</p>
<p>Mais ce projet, qui n’est ni plus ni moins qu’un Wikipédia de la cartographie, est phénoménal et doit être encouragé à tout prix. Surtout que la carto, ça bouffe en ressource, et c’est plus dur de trouver des bénévoles pour mapper des lieux (faut le matos et faut être sur place) que pour écrire un article sur le prépuce.</p>
<p>Bref, <a href="http://donate.openstreetmap.org/">ce don</a> de 50 euros est là pour soutenir une initiative purement parce que je la trouve belle. Pour le geste. Par écologie sociétale.</p>
<p>Néanmoins je ne doute pas qu’un jour, je serai bien content de pouvoir taper dans leur API plutôt que de dépendre d’une multinationale pour la carte d’un service clé.</p>


feed_id: http://sametmax.com/?p=16886
title: « Sept petites libs qui changent la vie d’un dev Python », tout beau, tout propre 6
uri: Sept-petites-libs-qui-changent-la-vie-d-un-dev-Python-tout-be
url: http://sametmax.com/sept-petites-libs-qui-changent-la-vie-dun-dev-python-tout-beau-tout-propre/
content:
<p>J’ai failli ne pas faire celui-là. Il est plus circonstanciel qu’autre chose.</p>
<p>Mais il était tellement à côté de la plaque que je ne voulais pas que des personnes tombent dessus et l’utilise.</p>
<p>Au programme :</p>
<ul>
<li>Remplacement des libs de l’époque par celles que j’utilise aujourd’hui.</li>
<li>Correction des bouts de code pour Python 3 (c’est un peu le but).</li>
<li>Et on étoffe quelques exemples.</li>
</ul>
<p>Servez-vous, <a href="http://sametmax.com/sept-petites-libs-qui-changent-la-vie-dun-dev-python/">il est tout chaud !</a></p>
<p>Honte à moi, j&#8217;avais oublié la video.</p>
<p>Pour me faire pardonner, une révélation : j&#8217;adore l&#8217;huile sur les meufs. </p>
<p>Du coup, une petit compilation &#8220;oiled and wet&#8221;, qui est une niche qui me plait particulièrement :)</p>
<p><iframe width="640" height="480" src="http://www.porntube.com/embed/1113815" frameborder="0" allowfullscreen></iframe><br /><a href="http://www.porntube.com/videos/oiled-wet-compilation-2_1113815" rel="nofollow" target="_blank" title="Oiled and Wet Compilation 2 - PORNTUBE.COM">Oiled and Wet Compilation 2</a> &#8211; PornTube &reg;.com</p>


feed_id: http://sametmax.com/?p=16899
title: « % ou format() en Python ? », inversion des polarités 8
uri: ou-format()-en-Python-inversion-des-polarités-1
url: http://sametmax.com/ou-format-en-python-inversion-des-polarites/
content:
<p>Normalement je fais les mises à jour d’articles par ordre chronologique, mais là une occasion s’est présentée avec l’article.</p>
<p>Je suis tombé dessus par hasard, je ne sais plus trop comment, et j’ai réalisé que j’avais changé d’avis sur l’article. Je ne recommandais plus du tout ce que j’y mettais.</p>
<p>Il fallait le mettre à jour, mais un si petit article, je serais probablement passé à côté dans le futur. En effet, je ne veux pas updater  600 articles, donc je fais ceux qui me paraissent prioritaires.</p>
<p>Du coup je l’ai fait tout de suite, histoire de ne pas oublier. En prime, il m’a donné envie d’écrire un article sur le formatage des chaines en Python en général, qui devrait sortir dans la semaine.</p>
<p>Cette réécriture comprend :</p>
<ul>
<li>Absolument aucun changement Python 3. Il n’y avait pas de code, mais de toute façon le code est le même en Python 2. J’ai quand même mis la mention « python 3 » en haut, histoire que les gens sachent que le conseil est d’actualité.</li>
<li>Des exemples rajoutés, car là c’était très théorique. Je devais être pressé le jour où je l’ai écrit :)</li>
<li>Et une mention des f-strings, histoire de faire un peu le teasing de la version 3.6.</li>
</ul>
<p>On se chauffe les yeux, et <a href="http://sametmax.com/ou-format-en-python/">on va le lire</a> !</p>
<p>Dans le cadre de notre parenthèse lubrique, j&#8217;ai eu envie de ruiner l&#8217;enfance de la nouvelle génération, la règle 34 ayant suffisamment attaqué la mienne :</p>
<p><iframe src="http://embed.redtube.com/?id=527722&#038;bgcolor=000000" frameborder="0" width="560" height="315" scrolling="no" allowfullscreen></iframe></p>
<p>Je suis quand même épaté de tout cet effort déployé par les monstres de tout poil (mouarf) juste pour insérer leur tentacules dans des orifices. Mais bon, quand ce sont des bites, au Japon, elles doivent être floutées, du coup ceci explique cela. Et puis ce qu&#8217;<a href="http://www.redtube.com/?search=my+little+poney">on trouve</a> sur MyLittlePoney n&#8217;est pas mieux.</p>


feed_id: http://sametmax.com/?p=16908
title: Nouvelle release de crossbar : support de Python 3 ! 9
uri: Nouvelle-release-de-crossbar-support-de-Python-3-9-Recently-upd
url: http://sametmax.com/nouvelle-release-de-crossbar-support-de-python-3/
content:
<p>L’équipe de Tavendo est à l’écoute de toutes les critiques de <a href="https://crossbar.io/">Crossbar</a> et <a href="http://sametmax.com/presentation-de-wamp-round-2/">WAMP</a> en général, et je me suis fait un plaisir de leur rapporter toutes les merdes dont vous m’avez fait part.</p>
<p>Cette <a href="https://groups.google.com/forum/#!topic/autobahnws/VvSM_mEQ0c0">nouvelle release</a> contient beaucoup de choses qui corrigent ou pallient un paquet de trucs relou dans le routeur Crossbar (et par conséquent la lib client Autobahn) :</p>
<ul>
<li>Support officiel de Python 3. Yes. Yes, yes yes !</li>
<li>Le debug a été complètement revu : meilleure console, meilleur login, meilleurs messages d’erreur et meilleur comportement en cas d’exceptions.</li>
<li>Un service dédié à l’upload de fichier intégré.</li>
<li>Un bridge HTTP complet qui permet d’utiliser Crossbar depuis n’importe quelle app qui peut faire des requêtes HTTP.</li>
</ul>
<p>Pour la suite, ils travaillent sur la doc, et l’amélioration de l’API. En attendant, on peut <code>pip install crossbar</code> et profiter de ces nouveautés sans avoir à passer par github.</p>
<p>De mon côté j&#8217;ai un article sur l&#8217;authentification avec Crossbar dans les cartons. ETA dans les 10 prochains jours.</p>


feed_id: http://sametmax.com/?p=16918
title: Jouons un peu avec Python 3.5 27
uri: Jouons-un-peu-avec-Python-3-5-1-Recently-updated
url: http://sametmax.com/jouons-un-peu-avec-python-3-5/
content:
<p>Zeste de savoir a fait un <a href="http://zestedesavoir.com/articles/264/sortie-de-python-35/">fantastique article</a> présentant Python 3.5, je ne vais donc pas répéter inutilement ce qu’ils ont dit. Le but de ce post est plutôt de faire mumuse avec le nouveau joujou.</p>
<p>La release est récente, mais fort heureusement on peut facilement l’installer. Sous Windows et Mac, il y a des <a href="https://www.python.org/downloads/release/python-350/">builds</a> tout chauds.</p>
<p>Pour linux, en attendant un repo tierce partie ou l’upgrade du système, on peut l’installer quelques commandes depuis les sources. Par exemple pour les distros basées sur Debian comme Ubuntu, ça ressemble à :</p>

<div class="wp_syntax"><table><tr><td class="code"><pre class="bash" style="font-family:monospace;">$ <span style="color: #666666; font-style: italic;"># dependances pour compiler python</span>
$ <span style="color: #c20cb9; font-weight: bold;">sudo</span> <span style="color: #c20cb9; font-weight: bold;">apt-get install</span> build-essential libreadline-dev tk8.4-dev libsqlite3-dev libgdbm-dev libreadline6-dev liblzma-dev libbz2-dev libncurses5-dev libssl-dev python3-dev tk-dev
$ <span style="color: #c20cb9; font-weight: bold;">sudo</span> <span style="color: #c20cb9; font-weight: bold;">apt-get build-dep</span> python3 <span style="color: #666666; font-style: italic;"># juste pour etre sur :)</span>
&nbsp;
$ <span style="color: #666666; font-style: italic;"># téléchargement des sources</span>
$ <span style="color: #7a0874; font-weight: bold;">cd</span> <span style="color: #000000; font-weight: bold;">/</span>tmp
$ <span style="color: #c20cb9; font-weight: bold;">wget</span> https:<span style="color: #000000; font-weight: bold;">//</span>www.python.org<span style="color: #000000; font-weight: bold;">/</span>ftp<span style="color: #000000; font-weight: bold;">/</span>python<span style="color: #000000; font-weight: bold;">/</span>3.5.0<span style="color: #000000; font-weight: bold;">/</span>Python-3.5.0.tar.xz
$ <span style="color: #c20cb9; font-weight: bold;">tar</span> <span style="color: #660033;">-xvf</span> Python-3.5.0.tar.xz
$ <span style="color: #7a0874; font-weight: bold;">cd</span> Python-3.5.0
&nbsp;
$ <span style="color: #666666; font-style: italic;"># et on build</span>
$ .<span style="color: #000000; font-weight: bold;">/</span>configure
$ <span style="color: #c20cb9; font-weight: bold;">make</span>
$ <span style="color: #c20cb9; font-weight: bold;">sudo</span> <span style="color: #c20cb9; font-weight: bold;">make</span> altinstall 
<span style="color: #666666; font-style: italic;"># pas 'make install' qui écrase le python du système !</span>
&nbsp;
$ python3.5 <span style="color: #666666; font-style: italic;"># ahhhhhh</span>
Python 3.5.0 <span style="color: #7a0874; font-weight: bold;">&#40;</span>default, Sep <span style="color: #000000;">16</span> <span style="color: #000000;">2015</span>, <span style="color: #000000;">10</span>:<span style="color: #000000;">44</span>:<span style="color: #000000;">14</span><span style="color: #7a0874; font-weight: bold;">&#41;</span> 
<span style="color: #7a0874; font-weight: bold;">&#91;</span>GCC 4.9.2<span style="color: #7a0874; font-weight: bold;">&#93;</span> on linux
Type <span style="color: #ff0000;">&quot;help&quot;</span>, <span style="color: #ff0000;">&quot;copyright&quot;</span>, <span style="color: #ff0000;">&quot;credits&quot;</span> or <span style="color: #ff0000;">&quot;license&quot;</span> <span style="color: #000000; font-weight: bold;">for</span> <span style="color: #c20cb9; font-weight: bold;">more</span> information.
<span style="color: #000000; font-weight: bold;">&gt;&gt;&gt;</span></pre></td></tr></table></div>

<p>Sur les centos-likes, c’est grosso merdo la même chose, sans le build-dep (mais plutôt un truc genre <code>sudo yum groupinstall 'Development Tools'</code>), et en remplaçant les <code>-dev</code> par <code>-devel</code>.</p>
<p><span class='embed-youtube' style='text-align:center; display: block;'><iframe class='youtube-player' type='text/html' width='1170' height='689' src='http://www.youtube.com/embed/TsgQdzzXEnY?version=3&#038;rel=1&#038;fs=1&#038;showsearch=0&#038;showinfo=1&#038;iv_load_policy=1&#038;wmode=transparent' frameborder='0' allowfullscreen='true'></iframe></span></p>
<h2>Nouvel opérateur</h2>
<p><code>@</code> est maintenant le nouvel opérateur de produit matriciel, mais il ne fait officiellement rien.</p>
<p>Comprenez par là que Python implémente l’opérateur, mais pas le produit en lui-même, la feature ayant été spécialement incluse pour faire plaisir aux utilisateurs de libs scientifiques type numpy.</p>
<p>On va donc tester ça sur le terrain. On se fait un petit env temporaire avec <a href="http://sametmax.com/mieux-que-python-virtualenvwrapper-pew/">pew</a> et on s’installe numpy :</p>

<div class="wp_syntax"><table><tr><td class="code"><pre class="bash" style="font-family:monospace;">pew mktmpenv <span style="color: #660033;">-p</span> python3.5
pip <span style="color: #c20cb9; font-weight: bold;">install</span> pip setuptools <span style="color: #660033;">--upgrade</span>
pip <span style="color: #c20cb9; font-weight: bold;">install</span> numpy 
<span style="color: #666666; font-style: italic;"># encore un peu de compilation</span></pre></td></tr></table></div>

<p>Testons mon bon. L’ancienne manière de faire :</p>

<div class="wp_syntax"><table><tr><td class="code"><pre class="python" style="font-family:monospace;"><span style="color: #66cc66;">&gt;&gt;&gt;</span> a <span style="color: #66cc66;">=</span> np.<span style="color: #dc143c;">array</span><span style="color: black;">&#40;</span><span style="color: black;">&#91;</span><span style="color: black;">&#91;</span><span style="color: #ff4500;">1</span><span style="color: #66cc66;">,</span> <span style="color: #ff4500;">0</span><span style="color: black;">&#93;</span><span style="color: #66cc66;">,</span> <span style="color: black;">&#91;</span><span style="color: #ff4500;">0</span><span style="color: #66cc66;">,</span> <span style="color: #ff4500;">1</span><span style="color: black;">&#93;</span><span style="color: black;">&#93;</span><span style="color: black;">&#41;</span>
<span style="color: #66cc66;">&gt;&gt;&gt;</span> b <span style="color: #66cc66;">=</span> np.<span style="color: #dc143c;">array</span><span style="color: black;">&#40;</span><span style="color: black;">&#91;</span><span style="color: black;">&#91;</span><span style="color: #ff4500;">4</span><span style="color: #66cc66;">,</span> <span style="color: #ff4500;">1</span><span style="color: black;">&#93;</span><span style="color: #66cc66;">,</span> <span style="color: black;">&#91;</span><span style="color: #ff4500;">2</span><span style="color: #66cc66;">,</span> <span style="color: #ff4500;">2</span><span style="color: black;">&#93;</span><span style="color: black;">&#93;</span><span style="color: black;">&#41;</span>
<span style="color: #66cc66;">&gt;&gt;&gt;</span> np.<span style="color: black;">dot</span><span style="color: black;">&#40;</span>a<span style="color: #66cc66;">,</span> b<span style="color: black;">&#41;</span>
<span style="color: #dc143c;">array</span><span style="color: black;">&#40;</span><span style="color: black;">&#91;</span><span style="color: black;">&#91;</span><span style="color: #ff4500;">4</span><span style="color: #66cc66;">,</span> <span style="color: #ff4500;">1</span><span style="color: black;">&#93;</span><span style="color: #66cc66;">,</span>
       <span style="color: black;">&#91;</span><span style="color: #ff4500;">2</span><span style="color: #66cc66;">,</span> <span style="color: #ff4500;">2</span><span style="color: black;">&#93;</span><span style="color: black;">&#93;</span><span style="color: black;">&#41;</span></pre></td></tr></table></div>

<p>Et la nouvelle :</p>

<div class="wp_syntax"><table><tr><td class="code"><pre class="python" style="font-family:monospace;"><span style="color: #66cc66;">&gt;&gt;&gt;</span> a <span style="color: #66cc66;">@</span> b
---------------------------------------------------------------------------
<span style="color: #008000;">TypeError</span>                                 Traceback <span style="color: black;">&#40;</span>most recent call last<span style="color: black;">&#41;</span>
<span style="color: #66cc66;">&lt;</span>ipython-input-<span style="color: #ff4500;">10</span>-3d41f06f59bb<span style="color: #66cc66;">&gt;</span> <span style="color: #ff7700;font-weight:bold;">in</span> <span style="color: #66cc66;">&lt;</span>module<span style="color: #66cc66;">&gt;</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
----<span style="color: #66cc66;">&gt;</span> <span style="color: #ff4500;">1</span> a <span style="color: #66cc66;">@</span> b
&nbsp;
<span style="color: #008000;">TypeError</span>: unsupported operand <span style="color: #008000;">type</span><span style="color: black;">&#40;</span>s<span style="color: black;">&#41;</span> <span style="color: #ff7700;font-weight:bold;">for</span> <span style="color: #66cc66;">@</span>: <span style="color: #483d8b;">'numpy.ndarray'</span> <span style="color: #ff7700;font-weight:bold;">and</span> <span style="color: #483d8b;">'numpy.ndarray'</span></pre></td></tr></table></div>

<p>Woops, apparemment numpy n’a pas encore implémenté le truc.</p>
<p>Bon. Bon, bon, bon. Comment on va tester alors&#8230; Ah, oui, y a une magic method :</p>

<div class="wp_syntax"><table><tr><td class="code"><pre class="python" style="font-family:monospace;"><span style="color: #ff7700;font-weight:bold;">class</span> Array<span style="color: black;">&#40;</span>np.<span style="color: black;">ndarray</span><span style="color: black;">&#41;</span>:
    <span style="color: #ff7700;font-weight:bold;">def</span> __matmul__<span style="color: black;">&#40;</span><span style="color: #008000;">self</span><span style="color: #66cc66;">,</span> other<span style="color: black;">&#41;</span>:
        <span style="color: #ff7700;font-weight:bold;">return</span> np.<span style="color: black;">dot</span><span style="color: black;">&#40;</span><span style="color: #008000;">self</span><span style="color: #66cc66;">,</span> other<span style="color: black;">&#41;</span>
&nbsp;
<span style="color: #66cc66;">&gt;&gt;&gt;</span> a <span style="color: #66cc66;">=</span> a.<span style="color: black;">view</span><span style="color: black;">&#40;</span>Array<span style="color: black;">&#41;</span>
<span style="color: #66cc66;">&gt;&gt;&gt;</span> b <span style="color: #66cc66;">=</span> b.<span style="color: black;">view</span><span style="color: black;">&#40;</span>Array<span style="color: black;">&#41;</span>
<span style="color: #66cc66;">&gt;&gt;&gt;</span> a <span style="color: #66cc66;">@</span> b
Array<span style="color: black;">&#40;</span><span style="color: black;">&#91;</span><span style="color: black;">&#91;</span><span style="color: #ff4500;">4</span><span style="color: #66cc66;">,</span> <span style="color: #ff4500;">1</span><span style="color: black;">&#93;</span><span style="color: #66cc66;">,</span>
       <span style="color: black;">&#91;</span><span style="color: #ff4500;">2</span><span style="color: #66cc66;">,</span> <span style="color: #ff4500;">2</span><span style="color: black;">&#93;</span><span style="color: black;">&#93;</span><span style="color: black;">&#41;</span></pre></td></tr></table></div>

<p>Bon, voilà ce que ça donnera quand les devs de numpy auront implémenté le bouzin (la dernière ligne hein, pas tout le bordel avant). </p>
<p>Apparemment ça fait bander les matheux, donc je suppose que c’est une super nouvelle.</p>
<h2>% is back on bytes</h2>
<p>En python 2, on pouvait faire <code>"truc %s" % "bidule"</code> et <code>u"truc %s" % u"bidule"</code> et <code>b"truc %s" % u"bidule"</code> et ça a été viré en python 3 qui ne garde <code>%</code> que pour <code>str</code> et pas pour <code>bytes</code>.</p>
<p>Ca n’aurait pas été un problème si ce n’est que Python est très utilisé pour le réseau, et que construire un paquet qui mélange de la sémantique binaire et textuelle devient soudainement une grosse soupe de <code>decode()</code> et <code>encode()</code>.</p>
<p>Jour 1, test 3, suspense&#8230;</p>

<div class="wp_syntax"><table><tr><td class="code"><pre class="python" style="font-family:monospace;"><span style="color: #66cc66;">&gt;&gt;&gt;</span> <span style="color: #dc143c;">bytearray</span><span style="color: black;">&#40;</span><span style="color: black;">&#91;</span><span style="color: #ff4500;">66</span><span style="color: #66cc66;">,</span> <span style="color: #ff4500;">108</span><span style="color: #66cc66;">,</span> <span style="color: #ff4500;">117</span><span style="color: #66cc66;">,</span> <span style="color: #ff4500;">101</span><span style="color: #66cc66;">,</span> <span style="color: #ff4500;">32</span><span style="color: #66cc66;">,</span> <span style="color: #ff4500;">112</span><span style="color: #66cc66;">,</span> <span style="color: #ff4500;">114</span><span style="color: #66cc66;">,</span> <span style="color: #ff4500;">105</span><span style="color: #66cc66;">,</span> <span style="color: #ff4500;">101</span><span style="color: #66cc66;">,</span> <span style="color: #ff4500;">115</span><span style="color: #66cc66;">,</span> <span style="color: #ff4500;">116</span><span style="color: #66cc66;">,</span> <span style="color: #ff4500;">32</span><span style="color: #66cc66;">,</span> <span style="color: #ff4500;">115</span><span style="color: #66cc66;">,</span> <span style="color: #ff4500;">97</span><span style="color: #66cc66;">,</span> <span style="color: #ff4500;">121</span><span style="color: #66cc66;">,</span> <span style="color: #ff4500;">115</span><span style="color: #66cc66;">,</span> <span style="color: #ff4500;">58</span><span style="color: #66cc66;">,</span> <span style="color: #ff4500;">32</span><span style="color: #66cc66;">,</span> <span style="color: #ff4500;">37</span><span style="color: #66cc66;">,</span> <span style="color: #ff4500;">115</span><span style="color: black;">&#93;</span><span style="color: black;">&#41;</span> % b<span style="color: #483d8b;">&quot;wololo&quot;</span>
<span style="color: #dc143c;">bytearray</span><span style="color: black;">&#40;</span>b<span style="color: #483d8b;">'Blue priest says: wololo'</span><span style="color: black;">&#41;</span></pre></td></tr></table></div>

<p>Voilà ça c’est fait !</p>
<h2>os.scandir()</h2>
<p><code>os.path.walk()</code> est dans mon top 10 des APIs que je déteste le plus en Python, juste à côté de la gestion timezone. Avoir <code>os.walk()</code> en Python 3 qui retourne un générateur me ravit. Avoir une version 10 X plus rapide avec <code>scandir</code>, n’est-ce pas choupinet ?</p>

<div class="wp_syntax"><table><tr><td class="code"><pre class="python" style="font-family:monospace;"><span style="color: #66cc66;">&gt;&gt;&gt;</span> <span style="color: #ff7700;font-weight:bold;">import</span> <span style="color: #dc143c;">os</span>
<span style="color: #66cc66;">&gt;&gt;&gt;</span> <span style="color: #008000;">list</span><span style="color: black;">&#40;</span><span style="color: #dc143c;">os</span>.<span style="color: black;">scandir</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'/tmp/'</span><span style="color: black;">&#41;</span><span style="color: black;">&#41;</span>
                       <span style="color: black;">&#91;</span><span style="color: #66cc66;">&lt;</span>DirEntry <span style="color: #483d8b;">'systemd-private-316509818ceb41488a4721c78dabb603-colord.service-eXUfPo'</span><span style="color: #66cc66;">&gt;,</span>
 <span style="color: #66cc66;">&lt;</span>DirEntry <span style="color: #483d8b;">'unity_support_test.0'</span><span style="color: #66cc66;">&gt;,</span>
 <span style="color: #66cc66;">&lt;</span>DirEntry <span style="color: #483d8b;">'config-err-7UpWeO'</span><span style="color: #66cc66;">&gt;,</span>
 <span style="color: #66cc66;">&lt;</span>DirEntry <span style="color: #483d8b;">'.ICE-unix'</span><span style="color: #66cc66;">&gt;,</span>
 <span style="color: #66cc66;">&lt;</span>DirEntry <span style="color: #483d8b;">'pip-rw_63q0_-unpack'</span><span style="color: #66cc66;">&gt;,</span>
 <span style="color: #66cc66;">&lt;</span>DirEntry <span style="color: #483d8b;">'systemd-private-316509818ceb41488a4721c78dabb603-systemd-timesyncd.service-eZumpq'</span><span style="color: #66cc66;">&gt;</span><span style="color: black;">&#93;</span></pre></td></tr></table></div>

<p>C’est très dommage que ça ne retourne pas des objets <code>Path</code> de <code>pathlib</code>, mais bon, les perfs, tout ça&#8230;</p>
<h2>Zipapp, le grand inaperçu</h2>
<p>Le saviez-vous ? Python peut exécuter un zip, ce qui permet de créer un script en plusieurs fichiers et de le partager comme un seul fichier. Non vous ne le saviez-vous-te-pas car personne n’en parle jamais. </p>
<p>La 3.5 vient avec un outil en ligne de commande pour faciliter la création de tels zip et une nouvelle extension (que l’installeur fera reconnaitre à Windows) pour cesdits fichiers : .pyz.</p>
<p>Je fais mon script :</p>

<div class="wp_syntax"><table><tr><td class="code"><pre class="bash" style="font-family:monospace;">foo
├── bar.py
├── __init__.py
└── __main__.py</pre></td></tr></table></div>

<p>__main__.py est obligatoire, c’est ce qui sera lancé quand on exécutera notre script. Dedans je mets <code>import bar</code> et dans bar <code>print('wololo again')</code>.</p>
<p>Ensuite je fusionne tout ça :</p>

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

<p>Et pouf, j’ai mon fichier foo.pyz :</p>

<div class="wp_syntax"><table><tr><td class="code"><pre class="bash" style="font-family:monospace;">$ python3.5  foo.pyz
wololo again</pre></td></tr></table></div>

<p>Attention aux imports dedans, ils sont assez chiants à gérer.</p>
<h2>L’unpacking généralisé</h2>
<p>J’adore cette feature. J’adore toutes les features de la 3.5. Cette release est fantastique. Depuis la 3.3 chaque release est fantastique.</p>
<p>Mais bon, zeste de savoir l’a traité en long et en large donc rien à dire de plus, si ce n’est que j’avais raté un GROS truc :</p>
<ul>
<li>On peut faire de l’unpacking sur n’importe quel itérable.</li>
<li>On peut faire de l’unpacking dans les tuples.</li>
<li>Les parenthèses des tuples sont facultatives.</li>
</ul>
<p>Donc ces syntaxes sont valides :</p>

<div class="wp_syntax"><table><tr><td class="code"><pre class="python" style="font-family:monospace;"><span style="color: #66cc66;">&gt;&gt;&gt;</span> *<span style="color: #008000;">range</span><span style="color: black;">&#40;</span><span style="color: #ff4500;">2</span><span style="color: black;">&#41;</span><span style="color: #66cc66;">,</span> *<span style="color: black;">&#91;</span><span style="color: #ff4500;">1</span><span style="color: #66cc66;">,</span> <span style="color: #ff4500;">3</span><span style="color: black;">&#93;</span><span style="color: #66cc66;">,</span> *<span style="color: #483d8b;">'ea'</span>
<span style="color: black;">&#40;</span><span style="color: #ff4500;">0</span><span style="color: #66cc66;">,</span> <span style="color: #ff4500;">1</span><span style="color: #66cc66;">,</span> <span style="color: #ff4500;">1</span><span style="color: #66cc66;">,</span> <span style="color: #ff4500;">3</span><span style="color: #66cc66;">,</span> <span style="color: #483d8b;">'e'</span><span style="color: #66cc66;">,</span> <span style="color: #483d8b;">'a'</span><span style="color: black;">&#41;</span>
<span style="color: #66cc66;">&gt;&gt;&gt;</span> *<span style="color: black;">&#91;</span>x * x <span style="color: #ff7700;font-weight:bold;">for</span> x <span style="color: #ff7700;font-weight:bold;">in</span> <span style="color: #008000;">range</span><span style="color: black;">&#40;</span><span style="color: #ff4500;">3</span><span style="color: black;">&#41;</span><span style="color: black;">&#93;</span><span style="color: #66cc66;">,</span> *<span style="color: black;">&#123;</span><span style="color: #483d8b;">&quot;a&quot;</span>: <span style="color: #ff4500;">1</span><span style="color: black;">&#125;</span>.<span style="color: black;">values</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
<span style="color: black;">&#40;</span><span style="color: #ff4500;">0</span><span style="color: #66cc66;">,</span> <span style="color: #ff4500;">1</span><span style="color: #66cc66;">,</span> <span style="color: #ff4500;">4</span><span style="color: #66cc66;">,</span> <span style="color: #ff4500;">1</span><span style="color: black;">&#41;</span></pre></td></tr></table></div>

<p>Ce qui peut être très chouette et aussi la porte ouverte à l’implémentation d’un sous-ensemble de Perl en Python. C’est selon l’abruti qui code.</p>
<h2>Type hints</h2>
<p>Ce qu’il faut bien comprendre avec les types hints, c’est que Python ne s’en sert pas. Il n’en fait rien. Que dalle. Nada. Peau de balle. Zob. Niet. Zero. La bulle. Néant. Null. None. Réforme gouvernementale.</p>
<p>Les types hints sont disponibles, mais Python ne va pas les traiter différemment d’autres annotations. Le but est de permettre à des outils externes (linter, IDE, etc) de se baser sur ces informations pour ajouter des fonctionnalités.</p>
<p>Pour l’instant, un seul le fait : <a href="http://mypy.readthedocs.org">mypy</a>.</p>
<p>Et là on sent bien que tout ça est tout neuf car si on fait <code>pip install mypy-lang</code>, on tombe sur une version buggée. Il faut donc l’installer directement depuis le repo, soit :</p>

<div class="wp_syntax"><table><tr><td class="code"><pre class="python" style="font-family:monospace;">pip install https://github.<span style="color: black;">com</span>/JukkaL/mypy/archive/master.<span style="color: #008000;">zip</span></pre></td></tr></table></div>

<p>Puis écriture d’une fonction annotée avec des types hints :</p>

<div class="wp_syntax"><table><tr><td class="code"><pre class="python" style="font-family:monospace;">&nbsp;
<span style="color: #ff7700;font-weight:bold;">from</span> typing <span style="color: #ff7700;font-weight:bold;">import</span> Iterable<span style="color: #66cc66;">,</span> Tuple
&nbsp;
PixelArray <span style="color: #66cc66;">=</span> Iterable<span style="color: black;">&#91;</span>Tuple<span style="color: black;">&#91;</span><span style="color: #008000;">int</span><span style="color: #66cc66;">,</span> <span style="color: #008000;">int</span><span style="color: #66cc66;">,</span> <span style="color: #008000;">int</span><span style="color: black;">&#93;</span><span style="color: black;">&#93;</span>
&nbsp;
<span style="color: #ff7700;font-weight:bold;">def</span> rgb2hex<span style="color: black;">&#40;</span>pixels: PixelArray<span style="color: black;">&#41;</span> -<span style="color: #66cc66;">&gt;</span> <span style="color: #008000;">list</span>:
    pattern <span style="color: #66cc66;">=</span> <span style="color: #483d8b;">&quot;#{0:02x}{1:02x}{2:02x}&quot;</span>
    <span style="color: #ff7700;font-weight:bold;">return</span> <span style="color: black;">&#91;</span>pattern.<span style="color: black;">format</span><span style="color: black;">&#40;</span>r<span style="color: #66cc66;">,</span> g<span style="color: #66cc66;">,</span> b<span style="color: black;">&#41;</span> <span style="color: #ff7700;font-weight:bold;">for</span> r<span style="color: #66cc66;">,</span> g<span style="color: #66cc66;">,</span> b <span style="color: #ff7700;font-weight:bold;">in</span> pixels<span style="color: black;">&#93;</span>
&nbsp;
&nbsp;
<span style="color: #808080; font-style: italic;"># ça marche :</span>
rgb2hex<span style="color: black;">&#40;</span><span style="color: black;">&#91;</span><span style="color: black;">&#40;</span><span style="color: #ff4500;">1</span><span style="color: #66cc66;">,</span> <span style="color: #ff4500;">2</span><span style="color: #66cc66;">,</span> <span style="color: #ff4500;">3</span><span style="color: black;">&#41;</span><span style="color: #66cc66;">,</span> <span style="color: black;">&#40;</span><span style="color: #ff4500;">1</span><span style="color: #66cc66;">,</span> <span style="color: #ff4500;">2</span><span style="color: #66cc66;">,</span> <span style="color: #ff4500;">3</span><span style="color: black;">&#41;</span><span style="color: black;">&#93;</span><span style="color: black;">&#41;</span>
<span style="color: #808080; font-style: italic;"># ['#010203', '#010203']</span></pre></td></tr></table></div>

<p>La preuve que Python n’en fait rien :</p>

<div class="wp_syntax"><table><tr><td class="code"><pre class="python" style="font-family:monospace;"><span style="color: #66cc66;">&gt;&gt;&gt;</span> <span style="color: #008000;">hex</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">&quot;fjdkls&quot;</span><span style="color: black;">&#41;</span>
---------------------------------------------------------------------------
<span style="color: #008000;">TypeError</span>                                 Traceback <span style="color: black;">&#40;</span>most recent call last<span style="color: black;">&#41;</span>
<span style="color: #66cc66;">&lt;</span>ipython-input-<span style="color: #ff4500;">2</span>-be62b8f062fe<span style="color: #66cc66;">&gt;</span> <span style="color: #ff7700;font-weight:bold;">in</span> <span style="color: #66cc66;">&lt;</span>module<span style="color: #66cc66;">&gt;</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
----<span style="color: #66cc66;">&gt;</span> <span style="color: #ff4500;">1</span> <span style="color: #008000;">hex</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">&quot;fjdkls&quot;</span><span style="color: black;">&#41;</span>
&nbsp;
<span style="color: #008000;">TypeError</span>: <span style="color: #483d8b;">'str'</span> <span style="color: #008000;">object</span> cannot be interpreted <span style="color: #ff7700;font-weight:bold;">as</span> an integer</pre></td></tr></table></div>

<p>Même la doc profite peu du typage :</p>
<pre>Help on function rgb2hex in module __main__:

rgb2hex(pixels:typing.Iterable) -> list
</pre>
<p>Mais si on met ça dans un fichier foo.py :</p>

<div class="wp_syntax"><table><tr><td class="code"><pre class="python" style="font-family:monospace;"><span style="color: #ff7700;font-weight:bold;">from</span> essai <span style="color: #ff7700;font-weight:bold;">import</span> rgb2hex
&nbsp;
<span style="color: #ff7700;font-weight:bold;">print</span><span style="color: black;">&#40;</span>rgb2hex<span style="color: black;">&#40;</span><span style="color: #483d8b;">&quot;fdjksl&quot;</span><span style="color: black;">&#41;</span><span style="color: black;">&#41;</span>
res <span style="color: #66cc66;">=</span> rgb2hex<span style="color: black;">&#40;</span><span style="color: black;">&#91;</span><span style="color: black;">&#40;</span><span style="color: #ff4500;">1</span><span style="color: #66cc66;">,</span> <span style="color: #ff4500;">2</span><span style="color: #66cc66;">,</span> <span style="color: #ff4500;">3</span><span style="color: black;">&#41;</span><span style="color: #66cc66;">,</span> <span style="color: black;">&#40;</span><span style="color: #ff4500;">3</span><span style="color: #66cc66;">,</span> <span style="color: #ff4500;">4</span><span style="color: #66cc66;">,</span> <span style="color: #ff4500;">5</span><span style="color: black;">&#41;</span><span style="color: black;">&#93;</span><span style="color: black;">&#41;</span>
<span style="color: #ff7700;font-weight:bold;">print</span><span style="color: black;">&#40;</span>res + <span style="color: #ff4500;">1</span><span style="color: black;">&#41;</span></pre></td></tr></table></div>

<p>Et qu&#8217;on le passe à la moulinette :</p>

<div class="wp_syntax"><table><tr><td class="code"><pre class="python" style="font-family:monospace;">$ mypy foo.<span style="color: black;">py</span> 
foo.<span style="color: black;">py</span>:<span style="color: #ff4500;">3</span>: error: Argument <span style="color: #ff4500;">1</span> to <span style="color: #483d8b;">&quot;rgb2hex&quot;</span> has incompatible <span style="color: #008000;">type</span> <span style="color: #483d8b;">&quot;str&quot;</span><span style="color: #66cc66;">;</span> expected Iterable<span style="color: black;">&#91;</span>...<span style="color: black;">&#93;</span>
foo.<span style="color: black;">py</span>:<span style="color: #ff4500;">5</span>: error: Unsupported operand <span style="color: #dc143c;">types</span> <span style="color: #ff7700;font-weight:bold;">for</span> + <span style="color: black;">&#40;</span>List<span style="color: black;">&#91;</span>Any<span style="color: black;">&#93;</span> <span style="color: #ff7700;font-weight:bold;">and</span> <span style="color: #483d8b;">&quot;int&quot;</span><span style="color: black;">&#41;</span></pre></td></tr></table></div>

<p>Ensuite j’ai essayé de créer un stub file, c’est-à-dire de mettre les hints dans un fichier à part plutôt que directement dans le code. Ma fonction redevient :</p>

<div class="wp_syntax"><table><tr><td class="code"><pre class="python" style="font-family:monospace;"><span style="color: #ff7700;font-weight:bold;">def</span> rgb2hex<span style="color: black;">&#40;</span>pixels<span style="color: black;">&#41;</span>:
    pattern <span style="color: #66cc66;">=</span> <span style="color: #483d8b;">&quot;#{0:02x}{1:02x}{2:02x}&quot;</span>
    <span style="color: #ff7700;font-weight:bold;">return</span> <span style="color: black;">&#91;</span>pattern.<span style="color: black;">format</span><span style="color: black;">&#40;</span>r<span style="color: #66cc66;">,</span> g<span style="color: #66cc66;">,</span> b<span style="color: black;">&#41;</span> <span style="color: #ff7700;font-weight:bold;">for</span> r<span style="color: #66cc66;">,</span> g<span style="color: #66cc66;">,</span> b <span style="color: #ff7700;font-weight:bold;">in</span> pixels<span style="color: black;">&#93;</span></pre></td></tr></table></div>

<p>Et mon fichier stub (même nom, mais avec extension .pyi) contient :</p>

<div class="wp_syntax"><table><tr><td class="code"><pre class="python" style="font-family:monospace;"><span style="color: #ff7700;font-weight:bold;">from</span> typing <span style="color: #ff7700;font-weight:bold;">import</span> Iterable<span style="color: #66cc66;">,</span> Tuple
&nbsp;
PixelArray <span style="color: #66cc66;">=</span> Iterable<span style="color: black;">&#91;</span>Tuple<span style="color: black;">&#91;</span><span style="color: #008000;">int</span><span style="color: #66cc66;">,</span> <span style="color: #008000;">int</span><span style="color: #66cc66;">,</span> <span style="color: #008000;">int</span><span style="color: black;">&#93;</span><span style="color: black;">&#93;</span>
&nbsp;
<span style="color: #ff7700;font-weight:bold;">def</span> rgb2hex<span style="color: black;">&#40;</span>pixels: PixelArray<span style="color: black;">&#41;</span> -<span style="color: #66cc66;">&gt;</span> <span style="color: #008000;">list</span>:...</pre></td></tr></table></div>

<p>Les stubs sont donc bien des fichiers Python valides, mais avec une extension différente, et juste les signatures des fonctions (le corps du bloc est une <code>Ellipsis</code>).</p>
<p>Et poof, ça marche pareil :</p>

<div class="wp_syntax"><table><tr><td class="code"><pre class="python" style="font-family:monospace;">$ mypy foo.<span style="color: black;">py</span> 
foo.<span style="color: black;">py</span>:<span style="color: #ff4500;">3</span>: error: Argument <span style="color: #ff4500;">1</span> to <span style="color: #483d8b;">&quot;rgb2hex&quot;</span> has incompatible <span style="color: #008000;">type</span> <span style="color: #483d8b;">&quot;str&quot;</span><span style="color: #66cc66;">;</span> expected Iterable<span style="color: black;">&#91;</span>...<span style="color: black;">&#93;</span>
foo.<span style="color: black;">py</span>:<span style="color: #ff4500;">5</span>: error: Unsupported operand <span style="color: #dc143c;">types</span> <span style="color: #ff7700;font-weight:bold;">for</span> + <span style="color: black;">&#40;</span>List<span style="color: black;">&#91;</span>Any<span style="color: black;">&#93;</span> <span style="color: #ff7700;font-weight:bold;">and</span> <span style="color: #483d8b;">&quot;int&quot;</span><span style="color: black;">&#41;</span></pre></td></tr></table></div>

<p>Il y a <a href="https://github.com/python/typeshed/tree/master/3">un repo</a> qui contient des fichiers stubs pour la stdlib. Vous pouvez y participer, c’est un moyen simple de contribuer à Python.</p>
<p>Bref, pour le moment ça demande encore un peu de maturité, mais je pense que d’ici quelques mois on aura des outils bien rodés pour faire tout ça automatiquement.</p>
<h2>Async/await</h2>
<p>La feature pub. Techniquement le truc qui a fait dire à tous ceux qui voulaient de l’asyncrone que Python en fait, c’était trop cool. Sauf que Python pouvait faire ça avec <code>yield from</code> avant, mais c’est sur que c’était super confusionant.</p>
<p>Maintenant on a un truc propre : pas de décorateur <code>@coroutine</code>, pas de syntaxe semblable aux générateurs, mais des méthodes magiques comme <code>__await__</code>  et de jolis mots-clés <code>async</code> et <code>await</code>.</p>
<p>Vu que Crossbar est maintenant compatible Python 3, et qu’il supporte asyncio pour les clients&#8230; Si on s’implémentait un petit wrapper WAMP pour s’amuser à voir ce que ressemblerait une API moderne pour du Websocket en Python ?</p>

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

<p>(Ouhhhh, plein de zolies couleurs apparaissent dans ma console ! Ils ont fait des efforts cosmétiques chez Tavendo)</p>
<p>Bien, voici maintenant l’exemple d’un client WAMP de base codé avec asyncio selon l’ancienne API :</p>

<div class="wp_syntax"><table><tr><td class="code"><pre class="python" style="font-family:monospace;"><span style="color: #ff7700;font-weight:bold;">import</span> asyncio
<span style="color: #ff7700;font-weight:bold;">from</span> autobahn.<span style="color: black;">asyncio</span>.<span style="color: black;">wamp</span> <span style="color: #ff7700;font-weight:bold;">import</span> ApplicationSession<span style="color: #66cc66;">,</span> ApplicationRunner
&nbsp;
<span style="color: #ff7700;font-weight:bold;">class</span> MyComponent<span style="color: black;">&#40;</span>ApplicationSession<span style="color: black;">&#41;</span>:
&nbsp;
    <span style="color: #66cc66;">@</span>asyncio.<span style="color: black;">coroutine</span>
    <span style="color: #ff7700;font-weight:bold;">def</span> onJoin<span style="color: black;">&#40;</span><span style="color: #008000;">self</span><span style="color: #66cc66;">,</span> details<span style="color: black;">&#41;</span>:
&nbsp;
        <span style="color: #808080; font-style: italic;"># on marque cette fonction comme appelable</span>
        <span style="color: #808080; font-style: italic;"># a distance en RPC</span>
        <span style="color: #ff7700;font-weight:bold;">def</span> add<span style="color: black;">&#40;</span>a<span style="color: #66cc66;">,</span> b<span style="color: black;">&#41;</span>:
            <span style="color: #ff7700;font-weight:bold;">return</span> a + b
        <span style="color: #008000;">self</span>.<span style="color: black;">register</span><span style="color: black;">&#40;</span>add<span style="color: #66cc66;">,</span> <span style="color: #483d8b;">&quot;add&quot;</span><span style="color: black;">&#41;</span>
&nbsp;
        <span style="color: #808080; font-style: italic;"># et on triche en l'appelant cash. J'ai</span>
        <span style="color: #808080; font-style: italic;"># la flemme de coder un deuxième client</span>
        <span style="color: #808080; font-style: italic;"># et ça passe quand même par le routeur</span>
        <span style="color: #808080; font-style: italic;"># donc merde</span>
        res <span style="color: #66cc66;">=</span> <span style="color: #ff7700;font-weight:bold;">yield</span> <span style="color: #ff7700;font-weight:bold;">from</span> <span style="color: #008000;">self</span>.<span style="color: black;">call</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">&quot;add&quot;</span><span style="color: #66cc66;">,</span> <span style="color: #ff4500;">2</span><span style="color: #66cc66;">,</span> <span style="color: #ff4500;">3</span><span style="color: black;">&#41;</span>
        <span style="color: #ff7700;font-weight:bold;">print</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">&quot;Got result: {}&quot;</span>.<span style="color: black;">format</span><span style="color: black;">&#40;</span>res<span style="color: black;">&#41;</span><span style="color: black;">&#41;</span>
&nbsp;
&nbsp;
<span style="color: #ff7700;font-weight:bold;">if</span> __name__ <span style="color: #66cc66;">==</span> <span style="color: #483d8b;">'__main__'</span>:
    runner <span style="color: #66cc66;">=</span> ApplicationRunner<span style="color: black;">&#40;</span><span style="color: #483d8b;">&quot;ws://127.0.0.1:8080/ws&quot;</span><span style="color: #66cc66;">,</span>
        u<span style="color: #483d8b;">&quot;crossbardemo&quot;</span><span style="color: #66cc66;">,</span>
        debug_wamp<span style="color: #66cc66;">=</span><span style="color: #008000;">False</span><span style="color: #66cc66;">,</span>  <span style="color: #808080; font-style: italic;"># optional; log many WAMP details</span>
        debug<span style="color: #66cc66;">=</span><span style="color: #008000;">False</span><span style="color: #66cc66;">,</span>  <span style="color: #808080; font-style: italic;"># optional; log even more details</span>
    <span style="color: black;">&#41;</span>
    runner.<span style="color: black;">run</span><span style="color: black;">&#40;</span>MyComponent<span style="color: black;">&#41;</span></pre></td></tr></table></div>

<p>Et ça marche nickel en 3.5. Mais qu’est-ce que c’est moche ! </p>
<p>On est <a href="https://github.com/tavendo/AutobahnPython/issues/472">en train de bosser</a> sur l’amélioration de l’API, mais je pense que ça va reste plus bas niveau que je le voudrais. </p>
<p>Donc, amusons-nous un peu à coder un truc plus sexy. Je vous préviens, le code du wrapper est velu, j’avais envie de me marrer un peu après les exemples ballots plus haut :</p>

<div class="wp_syntax"><table><tr><td class="code"><pre class="python" style="font-family:monospace;"><span style="color: #ff7700;font-weight:bold;">import</span> asyncio
<span style="color: #ff7700;font-weight:bold;">from</span> autobahn.<span style="color: black;">asyncio</span>.<span style="color: black;">wamp</span> <span style="color: #ff7700;font-weight:bold;">import</span> ApplicationSession<span style="color: #66cc66;">,</span> ApplicationRunner
&nbsp;
<span style="color: #ff7700;font-weight:bold;">class</span> App:
&nbsp;
    <span style="color: #ff7700;font-weight:bold;">def</span> <span style="color: #0000cd;">__init__</span><span style="color: black;">&#40;</span><span style="color: #008000;">self</span><span style="color: #66cc66;">,</span> *args<span style="color: #66cc66;">,</span> **kwargs<span style="color: black;">&#41;</span>:
        <span style="color: #008000;">super</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>.<span style="color: #0000cd;">__init__</span><span style="color: black;">&#40;</span>*args<span style="color: #66cc66;">,</span> **kwargs<span style="color: black;">&#41;</span>
        <span style="color: #008000;">self</span>.<span style="color: black;">procedures</span> <span style="color: #66cc66;">=</span> <span style="color: black;">&#91;</span><span style="color: black;">&#93;</span>
        <span style="color: #008000;">self</span>.<span style="color: black;">subscriptions</span> <span style="color: #66cc66;">=</span> <span style="color: black;">&#91;</span><span style="color: black;">&#93;</span>
        <span style="color: #008000;">self</span>.<span style="color: black;">event_handlers</span>  <span style="color: #66cc66;">=</span> <span style="color: black;">&#123;</span><span style="color: black;">&#125;</span>
&nbsp;
    <span style="color: #ff7700;font-weight:bold;">def</span> run<span style="color: black;">&#40;</span><span style="color: #008000;">self</span><span style="color: #66cc66;">,</span> url<span style="color: #66cc66;">=</span><span style="color: #483d8b;">&quot;ws://127.0.0.1:8080/ws&quot;</span><span style="color: #66cc66;">,</span>
                realm<span style="color: #66cc66;">=</span><span style="color: #483d8b;">&quot;realm1&quot;</span><span style="color: #66cc66;">,</span> debug_wamp<span style="color: #66cc66;">=</span><span style="color: #008000;">False</span><span style="color: #66cc66;">,</span> debug<span style="color: #66cc66;">=</span><span style="color: #008000;">False</span><span style="color: black;">&#41;</span>:
        runner <span style="color: #66cc66;">=</span> ApplicationRunner<span style="color: black;">&#40;</span>url<span style="color: #66cc66;">,</span> realm<span style="color: #66cc66;">,</span>
                                                     debug_wamp<span style="color: #66cc66;">=</span>debug_wamp<span style="color: #66cc66;">,</span>
                                                     debug<span style="color: #66cc66;">=</span>debug<span style="color: black;">&#41;</span>
        runner.<span style="color: black;">run</span><span style="color: black;">&#40;</span><span style="color: #008000;">self</span><span style="color: black;">&#41;</span>
&nbsp;
    <span style="color: #ff7700;font-weight:bold;">def</span> run_cmd<span style="color: black;">&#40;</span><span style="color: #008000;">self</span><span style="color: #66cc66;">,</span> *args<span style="color: #66cc66;">,</span> **kwargs<span style="color: black;">&#41;</span>:
        <span style="color: #808080; font-style: italic;"># et on pourrait même ici mettre du parsing d'argument</span>
        <span style="color: #808080; font-style: italic;"># et de os.environ, mais j'ai la flemme</span>
        <span style="color: #ff7700;font-weight:bold;">if</span> __name__ <span style="color: #66cc66;">==</span> <span style="color: #483d8b;">'__main__'</span>:
            <span style="color: #008000;">self</span>.<span style="color: black;">run</span><span style="color: black;">&#40;</span>*args<span style="color: #66cc66;">,</span> **kwargs<span style="color: black;">&#41;</span>
&nbsp;
    <span style="color: #808080; font-style: italic;"># quelques décorateurs pour faire du déclaratif</span>
    <span style="color: #808080; font-style: italic;"># et remettre les paramètres dans le bon ordre</span>
    <span style="color: #ff7700;font-weight:bold;">def</span> register<span style="color: black;">&#40;</span><span style="color: #008000;">self</span><span style="color: #66cc66;">,</span> name<span style="color: #66cc66;">,</span> *args<span style="color: #66cc66;">,</span> **kwargs<span style="color: black;">&#41;</span>:
            <span style="color: #ff7700;font-weight:bold;">def</span> wrapper<span style="color: black;">&#40;</span>proc<span style="color: black;">&#41;</span>:
                <span style="color: #008000;">self</span>.<span style="color: black;">procedures</span>.<span style="color: black;">append</span><span style="color: black;">&#40;</span><span style="color: black;">&#91;</span>name<span style="color: #66cc66;">,</span> proc<span style="color: #66cc66;">,</span> args<span style="color: #66cc66;">,</span> kwargs<span style="color: black;">&#93;</span><span style="color: black;">&#41;</span>
                <span style="color: #ff7700;font-weight:bold;">return</span> proc
            <span style="color: #ff7700;font-weight:bold;">return</span> wrapper
&nbsp;
    <span style="color: #ff7700;font-weight:bold;">def</span> subscribe<span style="color: black;">&#40;</span><span style="color: #008000;">self</span><span style="color: #66cc66;">,</span> topic<span style="color: #66cc66;">,</span> *args<span style="color: #66cc66;">,</span> **kwargs<span style="color: black;">&#41;</span>:
            <span style="color: #ff7700;font-weight:bold;">def</span> wrapper<span style="color: black;">&#40;</span>callback<span style="color: black;">&#41;</span>:
                <span style="color: #008000;">self</span>.<span style="color: black;">procedures</span>.<span style="color: black;">append</span><span style="color: black;">&#40;</span><span style="color: black;">&#91;</span>topic<span style="color: #66cc66;">,</span> callback<span style="color: #66cc66;">,</span> args<span style="color: #66cc66;">,</span> kwargs<span style="color: black;">&#93;</span><span style="color: black;">&#41;</span>
                <span style="color: #ff7700;font-weight:bold;">return</span> callback
            <span style="color: #ff7700;font-weight:bold;">return</span> wrapper
&nbsp;
    <span style="color: #808080; font-style: italic;"># un système d'event interne</span>
    <span style="color: #ff7700;font-weight:bold;">def</span> on<span style="color: black;">&#40;</span><span style="color: #008000;">self</span><span style="color: #66cc66;">,</span> event<span style="color: black;">&#41;</span>:
            <span style="color: #ff7700;font-weight:bold;">def</span> wrapper<span style="color: black;">&#40;</span>callback<span style="color: black;">&#41;</span>:
                <span style="color: #008000;">self</span>.<span style="color: black;">event_handlers</span>.<span style="color: black;">setdefault</span><span style="color: black;">&#40;</span>event<span style="color: #66cc66;">,</span> <span style="color: black;">&#91;</span><span style="color: black;">&#93;</span><span style="color: black;">&#41;</span>.<span style="color: black;">append</span><span style="color: black;">&#40;</span>callback<span style="color: black;">&#41;</span>
                <span style="color: #ff7700;font-weight:bold;">return</span> callback
            <span style="color: #ff7700;font-weight:bold;">return</span> wrapper
&nbsp;
    async <span style="color: #ff7700;font-weight:bold;">def</span> trigger<span style="color: black;">&#40;</span><span style="color: #008000;">self</span><span style="color: #66cc66;">,</span> event<span style="color: black;">&#41;</span>:
        <span style="color: #ff7700;font-weight:bold;">for</span> callback <span style="color: #ff7700;font-weight:bold;">in</span> <span style="color: #008000;">self</span>.<span style="color: black;">event_handlers</span>.<span style="color: black;">get</span><span style="color: black;">&#40;</span>event<span style="color: #66cc66;">,</span> <span style="color: black;">&#40;</span><span style="color: black;">&#41;</span><span style="color: black;">&#41;</span>:
            await callback<span style="color: black;">&#40;</span><span style="color: #008000;">self</span>.<span style="color: black;">session</span><span style="color: black;">&#41;</span>
&nbsp;
    <span style="color: #808080; font-style: italic;"># un peu de code de compatibilité avec l'API initiale</span>
    <span style="color: #ff7700;font-weight:bold;">def</span> <span style="color: #0000cd;">__call__</span><span style="color: black;">&#40;</span><span style="color: #008000;">self</span><span style="color: #66cc66;">,</span> *args<span style="color: black;">&#41;</span>:
        <span style="color: #ff7700;font-weight:bold;">class</span> CustomeSession<span style="color: black;">&#40;</span>ApplicationSession<span style="color: black;">&#41;</span>:
            async <span style="color: #ff7700;font-weight:bold;">def</span> onJoin<span style="color: black;">&#40;</span>session_self<span style="color: #66cc66;">,</span> details<span style="color: black;">&#41;</span>:
&nbsp;
                <span style="color: #808080; font-style: italic;"># on joint on fait tous les registers et tous les</span>
                <span style="color: #808080; font-style: italic;"># subscribes</span>
                <span style="color: #ff7700;font-weight:bold;">for</span> name<span style="color: #66cc66;">,</span> proc<span style="color: #66cc66;">,</span> args<span style="color: #66cc66;">,</span> kwargs <span style="color: #ff7700;font-weight:bold;">in</span> <span style="color: #008000;">self</span>.<span style="color: black;">procedures</span>:
                     session_self.<span style="color: black;">register</span><span style="color: black;">&#40;</span>proc<span style="color: #66cc66;">,</span> name<span style="color: #66cc66;">,</span> *args<span style="color: #66cc66;">,</span> **kwargs<span style="color: black;">&#41;</span>
&nbsp;
                <span style="color: #ff7700;font-weight:bold;">for</span> topic<span style="color: #66cc66;">,</span> callback<span style="color: #66cc66;">,</span> args<span style="color: #66cc66;">,</span> kwargs <span style="color: #ff7700;font-weight:bold;">in</span> <span style="color: #008000;">self</span>.<span style="color: black;">subscriptions</span>:
                     session_self.<span style="color: black;">subscribe</span><span style="color: black;">&#40;</span>proc<span style="color: #66cc66;">,</span> topic<span style="color: #66cc66;">,</span> *args<span style="color: #66cc66;">,</span> **kwargs<span style="color: black;">&#41;</span>
&nbsp;
                <span style="color: #808080; font-style: italic;"># on appelle les handlers de notre event</span>
                await <span style="color: #008000;">self</span>.<span style="color: black;">trigger</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'joined'</span><span style="color: black;">&#41;</span>
        <span style="color: #008000;">self</span>.<span style="color: black;">session</span> <span style="color: #66cc66;">=</span> CustomeSession<span style="color: black;">&#40;</span>*args<span style="color: black;">&#41;</span>
        <span style="color: #ff7700;font-weight:bold;">return</span> <span style="color: #008000;">self</span>.<span style="color: black;">session</span></pre></td></tr></table></div>

<p>Évidement la coloration syntaxique ne suit pas sur nos <code>async</code>/<code>await</code>.</p>
<p>Bon, vous allez me dire, mais ça quoi ça sert tout ça ? Et bien, c’est une version tronquée et codée à l’arrache de l’<a href="https://github.com/tavendo/AutobahnPython/blob/master/autobahn/twisted/wamp.py#L322">API Application</a> pour Twisted&#8230; mais version asyncio.</p>
<p>C’est-à-dire que c’est une lib qui permet de faire le même exemple que le tout premier qu’on a vu dans cette partie &#8211; qui souvenez-vous était fort moche -, mais comme ça :</p>

<div class="wp_syntax"><table><tr><td class="code"><pre class="python" style="font-family:monospace;">app <span style="color: #66cc66;">=</span> App<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
&nbsp;
<span style="color: #66cc66;">@</span>app.<span style="color: black;">register</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'add'</span><span style="color: black;">&#41;</span>
async <span style="color: #ff7700;font-weight:bold;">def</span> add<span style="color: black;">&#40;</span>a<span style="color: #66cc66;">,</span> b<span style="color: black;">&#41;</span>:
    <span style="color: #ff7700;font-weight:bold;">return</span> a + b
&nbsp;
<span style="color: #66cc66;">@</span>app.<span style="color: black;">on</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'joined'</span><span style="color: black;">&#41;</span>
async <span style="color: #ff7700;font-weight:bold;">def</span> _<span style="color: black;">&#40;</span>session<span style="color: black;">&#41;</span>:
    res <span style="color: #66cc66;">=</span> await session.<span style="color: black;">call</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">&quot;add&quot;</span><span style="color: #66cc66;">,</span> <span style="color: #ff4500;">2</span><span style="color: #66cc66;">,</span> <span style="color: #ff4500;">3</span><span style="color: black;">&#41;</span>
    <span style="color: #ff7700;font-weight:bold;">print</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">&quot;Got result: {}&quot;</span>.<span style="color: black;">format</span><span style="color: black;">&#40;</span>res<span style="color: black;">&#41;</span><span style="color: black;">&#41;</span>
&nbsp;
app.<span style="color: black;">run_cmd</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span></pre></td></tr></table></div>

<p>Des jolis décorateurs ! Des jolis async ! Des jolis await !</p>
<p>Et tout ça tourne parfaitement sur 3.5 messieurs-dames.</p>
<p>Bref, on peut faire du WAMP avec une syntaxe claire et belle, il faut juste se bouger le cul pour coder une abstraction un peu propre. </p>
<p>Je pense que autobahn restera toujours un peu bas niveau. Donc il va falloir que quelqu’un se colle à faire une lib pour wrapper tout ça.</p>
<p>Des volontaires ?</p>
<p>Arf, je savais bien que ça allait me retomber sur la gueule.</p>


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

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

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

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

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


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


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

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

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

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

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


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

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

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


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


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


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


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


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


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


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


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


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

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

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

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

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


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


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


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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

<p>Vous imaginez comme l&#8217;utilisateur final pouvait facilement décrire son problème&#8230; Ouai alors soit t&#8217;as pas un truc, soit le truc marche pas, soit tu te sers pas du truc. Ouai ça couvre à peut prêt l&#8217;ensemble des erreurs possibles sur tous systèmes, comme ça on peut pas avoir fondamentalement tort. </p>
<p><strong>EDIT:</strong></p>
<p>Histoire de mettre fin au débat sur le code final qui va immanquablement envahir les coms:</p>
<ul>
<li>La connexion n&#8217;a juste rien à foutre dans la fonction qui liste les dbs. Cela doit être deux fonctions séparées.</li>
<li>On attrape pas d&#8217;exception au niveau de la connexion. On attrape une exception au niveau du code qui déclenche la connexion afin de pouvoir donner un rapport d&#8217;erreur à l&#8217;utilisateur depuis le code qui gère l&#8217;UI.</li>
<li>On attrape pas une exception, mais 3-4, avec des messages d&#8217;erreur distincts pour chaque cas donnant une action claire à l&#8217;utilisateur qu&#8217;il puisse mettre en œuvre pour résoudre ce problème en particulier.</li>
<li>Oui, on peut retourner un tuple vide pour indiquer qu&#8217;on a rien trouvé (comme ça on retourne toujours un itérable) mais certainement pas pour signaler une erreur.</li>
<li>La fonction ne permet pas d&#8217;être unit testée facilement car non seulement elle fait trop de choses, mais en plus elle a plein d&#8217;entrées implicites (settings de connexion, liste des choses à fitrer, etc.). </li>
</ul>
<p>Bref, faut tout réécrire. Ça tombe bien, il me paie pour ça.</p>
<p>Notez que face à ce genre de code, le comportement à adopter n&#8217;est pas de mettre la misère à son client, mais simplement de lister les améliorations possibles, les justifier et estimer le coût et les bénéfices des changements. On est pas la pour fanfaronner, juste pour s&#8217;assurer qu&#8217;il puisse faire son taff.</p>
<p>Là je me permet de faire le clown parce que ça rend l&#8217;article plus léger, et parce qu&#8217;il est américain, et ne sera donc pas impacté par l&#8217;article. De l’intérêt également d&#8217;être un blogger anonyme et francophone&#8230;</p>


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


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

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

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


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


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


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

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

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


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


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

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

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


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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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


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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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


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

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

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

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

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

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

<p>Puis:</p>

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


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

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

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

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


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


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

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

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

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

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


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

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

<p>Par exemple :</p>

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

<div class="wp_syntax"><table><tr><td class="code"><pre class="python" style="font-family:monospace;"><span style="color: #ff7700;font-weight:bold;">def</span> stuff<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>: <span style="color: #808080; font-style: italic;"># pas de machin def</span>
    <span style="color: #ff7700;font-weight:bold;">yield</span> bidule <span style="color: #808080; font-style: italic;"># python comprends que c'est un générateur</span></pre></td></tr></table></div>

<p>Avec <code>await</code>, ça devrait être pareil:</p>

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

<p>Pas besoin de <code>async</code>. Si on veut faire une coroutine sans un seul <code>await</code>, il y a toujours <code>@asyncio.coroutine</code>.</p>
<p><code>async</code> reste très utile pour <code>async for</code> et <code>async with</code>, mais perso j’aurais préféré avoir un <code>await with</code> et un <code>await for</code> et pas de <code>async</code>.</p>
<p>On peut imaginer l&#8217;inverse aussi : tabler sur &#8220;explicit is bettern than implicit&#8221; et rajouter un <code>gen def</code> pour les générateurs et obtenir la parité.</p>
<p>Après, le prenez pas mal hein. J’adore <code>async</code>/<code>await</code>. Vive <code>asyncio</code> ! Mais la syntaxe pourrait être plus cohérente, plus proche du comportement des générateurs, puisqu&#8217;un coroutine n&#8217;est qu&#8217;un générateur spécialisé.</p>


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

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

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

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

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

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

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

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

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

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

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


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


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


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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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


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

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

<p>Et faites:</p>

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

<p>Par exemple:</p>

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

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

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

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

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

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

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

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


