<?xml version="1.0" encoding="utf-8"?><?xml-stylesheet title="XSL formatting" type="text/xsl" href="http://www.bonz.org/tech/feed/rss2/xslt" ?><rss version="2.0"
  xmlns:dc="http://purl.org/dc/elements/1.1/"
  xmlns:wfw="http://wellformedweb.org/CommentAPI/"
  xmlns:content="http://purl.org/rss/1.0/modules/content/">
<channel>
  <title>ZenTech - python</title>
  <link>http://www.bonz.org/tech/</link>
  <description>Om Mani Padme Hum</description>
  <language>fr</language>
  <pubDate>Tue, 23 Sep 2008 11:58:05 +0200</pubDate>
  <copyright></copyright>
  <docs>http://blogs.law.harvard.edu/tech/rss</docs>
  <generator>Dotclear</generator>
  
    
  <item>
    <title>Qui est le plus fort entre l'éléphant et l'hippopotame</title>
    <link>http://www.bonz.org/tech/post/2008/04/01/Qui-est-le-plus-fort-entre-lelephant-et-lhippopotame</link>
    <guid isPermaLink="false">urn:md5:680d04d6d6bc409ad11e407ba1a7f22f</guid>
    <pubDate>Wed, 02 Apr 2008 15:31:00 +0200</pubDate>
    <dc:creator>hr</dc:creator>
        <category>code</category>
        <category>performance</category><category>python</category><category>regexp</category><category>timeit</category>    
    <description>&lt;p&gt;Je me pose toujours des questions de performance lorsque je développe. Il y a souvent plusieurs façons de rêgler un problème et certaines plus consommatrices de ressources que d'autres. Dans le cas qui m'intéresse, je me questionne sur la performance des expressions régulières en regard d'autres méthodes de traitement de chaîne. Je dois effectuer un traitement sur un très grand nombre de lignes de log au fur et à mesure de leur émission en &lt;code&gt;syslog&lt;/code&gt;.&lt;/p&gt;    &lt;p&gt;La plupart du temps avec python, avoir une idée innovante est modérée par l'existence d'un module qui couvre déjà le sujet. Dans ce cas, le module &lt;code&gt;&lt;a href=&quot;http://docs.python.org/lib/module-timeit.html&quot; hreflang=&quot;en&quot;&gt;timeit&lt;/a&gt;&lt;/code&gt; va me permettre de tester la rapidité d'exécution de différents bouts de code.&lt;/p&gt;


&lt;p&gt;Le format d'une ligne de log syslog est connu et décrit par la &lt;a href=&quot;http://www.ietf.org/rfc/rfc3164.txt&quot; hreflang=&quot;en&quot;&gt;RFC3164&lt;/a&gt;, ce format me permet différentes approches pour en extraire différents champs. Dans mon cas, je vais extraire le champ appelé &lt;code&gt;HEADER&lt;/code&gt; de différentes façons. Un ligne de log syslog doit être de la forme&amp;nbsp;:&lt;/p&gt;
&lt;pre&gt;
&amp;lt;     &amp;gt;TIMESTAMP HOSTNAME TAG CONTENT
| PRI |      HEADER      |    MSG
&lt;/pre&gt;

&lt;p&gt;Dans le code python ci-dessous, les trois fonctions testées sont&amp;nbsp;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;split()&lt;/code&gt; qui récupère le champ header en effectuant un split sur le caractère espace et en réunissant les champs résultants.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;match()&lt;/code&gt; effectue une sélection par expression régulière.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;match_full()&lt;/code&gt; effectue une capture par expression régulière et occurence nommée.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Il est important de bien faire attention au contexte et à la portée des variables et fonctions (&quot;scope&quot; et &quot;namespace&quot;) lorsqu'on utilise &lt;code&gt;timeit&lt;/code&gt;.&lt;/p&gt;

&lt;pre&gt;
import timeit

def split():
    str = '&amp;lt;22&amp;gt;Mar 27 16:13:35 192.168.1.1 mx-daemon[2754]: m2RFDTbw002754: from=&amp;lt;j.doe@nowhere.com&amp;gt;, size=3356, class=0, nrcpts=1, msgid=&amp;lt;000701c8902d$04cb7356$879fa6b4@epwlhnj&amp;gt;, proto=ESMTP, daemon=MX, relay=[192.168.1.2]'
    ' '.join(str.split()[0:4]).split('&amp;gt;')[1]

def match():
    import re
    pattern = r'^&amp;lt;(\d+)&amp;gt;(\w{3} (?: |\d)\d \d\d:\d\d:\d\d [\w:.]+) (\w+.+)'
    _pattern = re.compile(pattern)
    str = '&amp;lt;22&amp;gt;Mar 27 16:13:35 192.168.1.1 mx-daemon[2754]: m2RFDTbw002754: from=&amp;lt;j.doe@nowhere.com&amp;gt;, size=3356, class=0, nrcpts=1, msgid=&amp;lt;000701c8902d$04cb7356$879fa6b4@epwlhnj&amp;gt;, proto=ESMTP, daemon=MX, relay=[192.168.1.2]'
    _pattern.search(str).group(2)

def match_full():
    import re
    pattern_full = r'^&amp;lt;(?P&amp;lt;pri&amp;gt;\d+)&amp;gt;(?P&amp;lt;header&amp;gt;\w{3} (?: |\d)\d \d\d:\d\d:\d\d [\w:.]+) (?P&amp;lt;msg&amp;gt;\w+.+)'
    _pattern_full = re.compile(pattern_full)
    str = '&amp;lt;22&amp;gt;Mar 27 16:13:35 192.168.1.1 mx-daemon[2754]: m2RFDTbw002754: from=&amp;lt;j.doe@nowhere.com&amp;gt;, size=3356, class=0, nrcpts=1, msgid=&amp;lt;000701c8902d$04cb7356$879fa6b4@epwlhnj&amp;gt;, proto=ESMTP, daemon=MX, relay=[192.168.1.2]'
    _pattern_full.search(str).groupdict()['header']

print &amp;quot;split:&amp;quot;
print timeit.Timer(&amp;quot;split()&amp;quot;, &amp;quot;from __main__ import split&amp;quot;).timeit()
print &amp;quot;match:&amp;quot;
print timeit.Timer(&amp;quot;match()&amp;quot;, &amp;quot;from __main__ import match&amp;quot;).timeit()
print &amp;quot;match_full:&amp;quot;
print timeit.Timer(&amp;quot;match_full()&amp;quot;, &amp;quot;from __main__ import match_full&amp;quot;).timeit()
&lt;/pre&gt;

&lt;p&gt;Ce qui donne après exécution&amp;nbsp;:&lt;/p&gt;
&lt;pre&gt;
$ python stress.py
split:
6.14724898338
match:
14.80702281
match_full:
19.0696430206
&lt;/pre&gt;


&lt;p&gt;En y réfléchissant bien, les fonctions testées ne sont pas vraiment optimisées et ne sont pas donc comparables en vitesse d'exécution. Pour vraiment jouer sur un pied d'égalité, je procède aux modifications suivantes&amp;nbsp;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Import du module re en début de script, ça fonctionne finalement, j'avais peur que les problèmes de contexte de &lt;code&gt;timeit&lt;/code&gt; ne posent problème.&lt;/li&gt;
&lt;li&gt;La compilation des expressions régulières est faite une fois pour toute et pas à chaque lancement des fonctions &lt;code&gt;cmatch()&lt;/code&gt; et &lt;code&gt;cmatch_full()&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Introduction de deux nouvelles fonctions effectuant une capture par expression régulière sans compilation de l'expression.&lt;/li&gt;
&lt;li&gt;Utilisation de la méthode &lt;code&gt;repeat()&lt;/code&gt; plutôt que &lt;code&gt;timeit()&lt;/code&gt; pour essayer d'obtenir un résultat moins influencé par le fonctionnement du système.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;
import re, timeit

str='&amp;lt;22&amp;gt;Mar 27 16:13:35 192.168.1.1 mx-daemon[2754]: m2RFDTbw002754: from=&amp;lt;j.doe@nowhere.com&amp;gt;, size=3356, class=0, nrcpts=1, msgid=&amp;lt;000701c8902d$04cb7356$879fa6b4@epwlhnj&amp;gt;, proto=ESMTP, daemon=MX, relay=[192.168.1.2]'

split_init = &amp;quot;&amp;quot;&amp;quot;from __main__ import split&amp;quot;&amp;quot;&amp;quot;
match_init = &amp;quot;&amp;quot;&amp;quot;from __main__ import match&amp;quot;&amp;quot;&amp;quot;
match_full_init = &amp;quot;&amp;quot;&amp;quot;from __main__ import match_full&amp;quot;&amp;quot;&amp;quot;
cmatch_init = &amp;quot;&amp;quot;&amp;quot;from __main__ import cmatch&amp;quot;&amp;quot;&amp;quot;
cmatch_full_init = &amp;quot;&amp;quot;&amp;quot;from __main__ import cmatch_full&amp;quot;&amp;quot;&amp;quot;

pattern = r'^&amp;lt;(\d+)&amp;gt;(\w{3} (?: |\d)\d \d\d:\d\d:\d\d [\w:.]+) (\w+.+)'
pattern_full = r'^&amp;lt;(?P&amp;lt;pri&amp;gt;\d+)&amp;gt;(?P&amp;lt;header&amp;gt;\w{3} (?: |\d)\d \d\d:\d\d:\d\d [\w:.]+) (?P&amp;lt;msg&amp;gt;\w+.+)'
_pattern = re.compile(pattern)
_pattern_full = re.compile(pattern_full)

def split():
    ' '.join(str.split(' ')[0:4]).split('&amp;gt;')[1]

def match():
    re.search(pattern, str).group(2)

def match_full():
    re.search(pattern_full, str).groupdict()['header']

def cmatch():
    _pattern.search(str).group(2)

def cmatch_full():
    _pattern_full.search(str).groupdict()['header']

print &amp;quot;Testing %d occurences of each match, %d times&amp;quot; % (timeit.default_number, timeit.default_repeat)
split_result = timeit.Timer(&amp;quot;split()&amp;quot;, split_init).repeat()
print &amp;quot;split: %fs %s&amp;quot; % (min(split_result), split_result)
match_result = timeit.Timer(&amp;quot;match()&amp;quot;, match_init).repeat()
print &amp;quot;match: %fs %s&amp;quot; % (min(match_result), match_result)
match_full_result = timeit.Timer(&amp;quot;match_full()&amp;quot;, match_full_init).repeat()
print &amp;quot;match_full: %fs %s&amp;quot; % (min(match_full_result), match_full_result)
cmatch_result = timeit.Timer(&amp;quot;cmatch()&amp;quot;, cmatch_init).repeat()
print &amp;quot;cmatch: %fs %s&amp;quot; % (min(cmatch_result), cmatch_result)
cmatch_full_result = timeit.Timer(&amp;quot;cmatch_full()&amp;quot;, cmatch_full_init).repeat()
print &amp;quot;cmatch_full: %fs %s&amp;quot; % (min(cmatch_full_result), cmatch_full_result)
&lt;/pre&gt;


&lt;p&gt;Qui me donne les résultats suivants&amp;nbsp;:&lt;/p&gt;
&lt;pre&gt;
$ python stress.py
Testing 1000000 occurences of each match, 3 times
split: 5.818318s [5.8183181285858154, 6.5981872081756592, 6.327794075012207]
match: 8.220056s [8.2262570858001709, 8.2361149787902832, 8.2200560569763184]
match_full: 12.399510s [12.40582799911499, 12.399509906768799, 12.501888036727905]
cmatch: 3.818037s [3.8180370330810547, 3.8276369571685791, 3.8310809135437012]
cmatch_full: 6.724747s [6.7247469425201416, 6.7392361164093018, 6.7564170360565186]
&lt;/pre&gt;

&lt;p&gt;Il est surprenant de constater la différence énorme entre les expressions régulières compilées et non compilées, l'avantage de compiler ses expressions dans le cas d'une utilisation de masse est évident. La rapidité de &lt;code&gt;split()&lt;/code&gt; est plutôt décevante par rapport à l'utilisation des expressions régullières.&lt;/p&gt;


&lt;p&gt;Ces tests sont intéressant et donnent des indications de performance mais ne remplacent pas des tests complet de &lt;a href=&quot;http://docs.python.org/lib/profile.html&quot; hreflang=&quot;en&quot;&gt;profilage&lt;/a&gt;.&lt;/p&gt;</description>
    
    
    
      </item>
    
  <item>
    <title>HAL&gt; Affirmative, Dave, I read you.</title>
    <link>http://www.bonz.org/tech/post/2008/01/10/HAL-Affirmative-Dave-I-read-you</link>
    <guid isPermaLink="false">urn:md5:1b24cb50e2d7f4d973321a63fd0da31d</guid>
    <pubDate>Fri, 11 Jan 2008 17:39:00 +0100</pubDate>
    <dc:creator>hr</dc:creator>
        <category>code</category>
        <category>freebsd</category><category>irc</category><category>linux</category><category>python</category>    
    <description>&lt;p&gt;&lt;img src=&quot;http://www.bonz.org/tech/public/tech/Hal-9000.jpg&quot; alt=&quot;HAL&quot; style=&quot;float:right; margin: 0 0 1em 1em;&quot; /&gt; Ah la vacuité d'une session &lt;a href=&quot;http://www.faqs.org/rfcs/rfc1459.html&quot; hreflang=&quot;en&quot;&gt;IRC&lt;/a&gt; sur un canal dirigé par un automate. Quoi de plus extraordinaire que pouvoir connaitre la météo de &lt;a href=&quot;http://www.mairie-felletin.fr/&quot; hreflang=&quot;fr&quot;&gt;Felletin dans la Creuse&lt;/a&gt; ou d'avoir une &lt;a href=&quot;http://eggnet.warpzone.org/moduledesc.php?module=9&quot; hreflang=&quot;en&quot;&gt;blague carambar&lt;/a&gt; à la demande. En ce moment je fais beaucoup de python, donc je me suis penché sur le problème pour un &lt;a href=&quot;http://www.binaries.fr/blog/&quot; hreflang=&quot;fr&quot;&gt;ami&lt;/a&gt;. Je vais essayer de vous exposer les clés de l'utilisation d'&lt;code&gt;&lt;a href=&quot;http://python-irclib.sourceforge.net/&quot; hreflang=&quot;en&quot;&gt;irclib&lt;/a&gt;&lt;/code&gt; pour construire un automate irc simple.&lt;/p&gt;    &lt;p&gt;L'automate est présenté ci-après, ses fonctions sont simplement la connexion à un serveur irc local sur le port 6667 et l'entrée dans le canal #test. L'automate s'attend à lire des commandes préfixées de '&lt;code&gt;!&lt;/code&gt;' dans le canal. La commande &lt;code&gt;!cycle&lt;/code&gt; demande à l'automate de partir et revenir sur le canal, la commande &lt;code&gt;!die&lt;/code&gt; provoque la déconnexion du serveur puis l'arrêt de l'automate. Le script de l'automate ci-après se trouve en téléchargement &lt;a href=&quot;http://www.bonz.org/files/mybot.py&quot;&gt;ici&lt;/a&gt; et peut être lancé par la commande &lt;code&gt;python mybot.py&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;
import sys
try:
    from ircbot import SingleServerIRCBot
    import irclib
except ImportError, e:
    print e
    sys.exit(1)

class pybot(SingleServerIRCBot):
    def __init__(self, server, nick, realname, chan):
        SingleServerIRCBot.__init__(self, server, nick, realname)
        self.chan = chan
        self.nick = nick

    ## Event callbacks
    def on_welcome(self, c, e):
        if self.chan:
            c.join(self.chan)

    def on_join(self, c, e):
        who = e.source().split('!', 1)
        if who[0] == self.nick:
            c.privmsg(e.target(), 'Hello world!')

    def on_pubmsg(self, c, e):
        msg = e.arguments()[0].strip()
        if msg.startswith('!'):
            cmd = msg[1:].split(' ', 1)[0]
            self.trigger(c, e, cmd)

    ## Command triggers
    def trigger(self, c, e, cmd):
        try:
            getattr(self, 'cmd_%s' % cmd)(c, e)
        except AttributeError:
            pass

    def cmd_cycle(self, c, e):
        chan = e.target()
        c.part(chan, 'cycle')
        c.join(chan)

    def cmd_die(self, c, e):
        SingleServerIRCBot.die(self, 'Argh')

## Here comes the real script
if __name__ == '__main__':
    server = [('localhost', 6667)]
    nick = 'monbot'
    realname = 'monbot'
    channel = '#test'

    bot = pybot(server, nick, realname, channel)
    try:
        bot.start()
    except KeyboardInterrupt:
        bot.connection.disconnect(&amp;quot;Ctrl-C m'a tuer&amp;quot;)
        bot.connection.close()
&lt;/pre&gt;


&lt;p&gt;La librairie &lt;code&gt;irclib&lt;/code&gt; est basée sur la gestion d'évènements, des méthodes vont être lancées sur apparition d'un évènement au sens IRC. Les méthodes de l'objet &lt;code&gt;pybot&lt;/code&gt; commençant par '&lt;code&gt;on_&lt;/code&gt;' sont les méthodes de gestion d'évènements, par exemple lorsqu'un message est envoyé sur un canal (&lt;code&gt;PUBMSG&lt;/code&gt;) la méthode on_pubsmg() est appelée. La création de l'automate se base sur la surcharge des méthodes de gestion d'évènements. Ces méthodes ont toutes la même définition d'appel&amp;nbsp;:&lt;/p&gt;
&lt;pre&gt;
def on_foo(self, c, e):
    [...]
&lt;/pre&gt;

&lt;p&gt;La variable &lt;code&gt;c&lt;/code&gt; est un objet &lt;code&gt;ServerConnection&lt;/code&gt;, cet objet permet de manipuler tout ce qui concerne le serveur irc d'où provient l'évènement. On peut par exemple rejoindre un canal (&lt;code&gt;c.join()&lt;/code&gt;) ou envoyer un message à un canal ou une personne (commande &lt;code&gt;PRIVMSG&lt;/code&gt; via &lt;code&gt;c.privmsg()&lt;/code&gt;). La variable &lt;code&gt;e&lt;/code&gt; est un objet &lt;code&gt;Event&lt;/code&gt;, elle permet d'obtenir toutes les informations sur l'évènement qui a provoqué l'appel de la méthode. L'origine de l'évènement (&lt;code&gt;e.source()&lt;/code&gt;), la destination de cet évènement (&lt;code&gt;e.target()&lt;/code&gt;) ou encore les arguments de cet évènement (&lt;code&gt;e.arguments()&lt;/code&gt;). Toutes les informations sur ces 2 méthodes se trouvent dans le module irclib (&lt;code&gt;pydoc irclib&lt;/code&gt;).&lt;/p&gt;


&lt;p&gt;A partir de la connaissance de ces éléments de base, la construction d'un automate devient assez simple. Si l'automate doit effectuer des tâches régulières, les méthodes &lt;code&gt;execute_at()&lt;/code&gt; et &lt;code&gt;execute_delayed()&lt;/code&gt; (héritage de l'objet IRC parent de SingleServerIRCBot) peuvent être utilisées. Plus d'information sur &lt;code&gt;irclib&lt;/code&gt; devrait se trouver sur mon &lt;a href=&quot;http://wiki.bonz.org/python/irclib&quot; hreflang=&quot;fr&quot;&gt;wiki&lt;/a&gt;.&lt;/p&gt;


&lt;p&gt;Il est également possible d'utiliser &lt;a href=&quot;http://twistedmatrix.com/documents/current/api/twisted.words.protocols.irc.html&quot; hreflang=&quot;en&quot;&gt;twisted.words&lt;/a&gt; pour effectuer la même chose mais ça me semble une librairie un peu imposante pour un simple automate.&lt;/p&gt;


&lt;p&gt;Il faut noter qu'on peut faire également le même genre de chose en &lt;a href=&quot;http://search.cpan.org/~bingos/POE-Component-IRC-5.48/lib/POE/Component/IRC.pm&quot; hreflang=&quot;en&quot;&gt;Perl&lt;/a&gt;.&lt;/p&gt;</description>
    
    
    
      </item>
    
  <item>
    <title>WOPR&gt; Global Thermonuclear War</title>
    <link>http://www.bonz.org/tech/post/2008/01/03/WOPR-Global-Thermonuclear-War</link>
    <guid isPermaLink="false">urn:md5:e11fe7ac496ab81a907768d677602a9c</guid>
    <pubDate>Thu, 03 Jan 2008 18:33:00 +0100</pubDate>
    <dc:creator>hr</dc:creator>
        <category>code</category>
        <category>freebsd</category><category>linux</category><category>python</category>    
    <description>&lt;p&gt;Dans mon parcours d'initiation à python, je me suis retrouvé face à un problème existentiel (ce qui donne tout le caractère initiatique au parcours, le problème, parce qu'il est existentiel ... laisse tomber). Lors de l'utilisation du module &lt;a href=&quot;http://docs.python.org/lib/module-cmd.html&quot; hreflang=&quot;en&quot;&gt;cmd&lt;/a&gt;, comment gérer correctement les interruptions classiques que sont &lt;code&gt;ctrl+D&lt;/code&gt; (&lt;code&gt;EOF&lt;/code&gt;) et &lt;code&gt;ctrl-C&lt;/code&gt; (&lt;code&gt;SIGINT&lt;/code&gt;).&lt;/p&gt;    &lt;p&gt;Ci-après, un squelette pour la gestion des 2 signaux évoqués. Juste après ci-après, les quelques explications qui s'imposent.&lt;/p&gt;
&lt;pre&gt;
import sys
import cmd

class shell(cmd.Cmd):
    def __init__(self):
        cmd.Cmd.__init__(self)

    # Let's handle ctrl+D signals and quit gently
    def do_EOF(self, str):
        # CLEAN HERE
        return 1

if __name__ == &amp;quot;__main__&amp;quot;:
    console = shell()
    try:
        console.cmdloop()
    except KeyboardInterrupt:
        # CLEAN HERE
        sys.exit(0)
&lt;/pre&gt;


&lt;h2&gt;&lt;code&gt;ctrl-D&lt;/code&gt; ou la fin du monde&lt;/h2&gt;

&lt;p&gt;Le manuel de &lt;a href=&quot;http://docs.python.org/lib/Cmd-objects.html#l2h-4880&quot; hreflang=&quot;en&quot;&gt;cmd&lt;/a&gt; nous dit&amp;nbsp;:&lt;/p&gt;

&lt;blockquote&gt;&lt;p&gt;An end-of-file on input is passed back as the string 'EOF'.&lt;/p&gt;&lt;/blockquote&gt;

&lt;p&gt;Ce qui signifie que la combinaison de touches &lt;code&gt;ctrl+D&lt;/code&gt; revient à gérer la commande &lt;code&gt;EOF&lt;/code&gt;, la méthode appelée est donc &lt;code&gt;do_EOF()&lt;/code&gt;.&lt;br /&gt;
Une autre information à propos de &lt;code&gt;cmdloop()&lt;/code&gt; donnée dans le manuel est&amp;nbsp;:&lt;/p&gt;

&lt;blockquote&gt;&lt;p&gt;This method will return when the postcmd() method returns a true value. The stop argument to postcmd() is the return value from the command's corresponding do_*() method.&lt;/p&gt;&lt;/blockquote&gt;

&lt;p&gt;Donc si la méthode &lt;code&gt;do_EOF()&lt;/code&gt; retourne simplement 1, la boucle va tout bêtement s'arrêter.&lt;/p&gt;


&lt;h2&gt;&lt;code&gt;ctrl-C&lt;/code&gt; ou la fin de l'univers&lt;/h2&gt;

&lt;p&gt;En Python, l'utilisation de &lt;code&gt;ctrl-C&lt;/code&gt; lève l'exception &lt;code&gt;&lt;a href=&quot;http://docs.python.org/lib/module-exceptions.html#l2h-111&quot; hreflang=&quot;en&quot;&gt;KeyboardInterrupt&lt;/a&gt;&lt;/code&gt;, en capturant cette interruption, on permet au script de sortir de façon maitrisée.&lt;/p&gt;


&lt;h2&gt;Evolution&lt;/h2&gt;

&lt;p&gt;Il est conseillé d'ajouter des actions de nettoyage au niveau des commentaires &lt;code&gt;# CLEAN HERE&lt;/code&gt; pour permettre une sortie de la console vraiment sans encombre. Il reste encore à trouver un moyen élégant d'utiliser &lt;code&gt;ctrl-c&lt;/code&gt; pour annuler la ligne en cours et récupérer l'invite vierge sans quitter &lt;code&gt;cmd.cmdloop()&lt;/code&gt;&lt;br /&gt;
Attention toutefois, ce code n'a pas été testé dans un environnement microsoft. Les signaux ne sont surement pas traités de la même façon, ça n'étonnera personne.&lt;/p&gt;</description>
    
    
    
      </item>
    
</channel>
</rss>