<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom"><title>joe di castro</title><link href="http://joedicastro.com" rel="alternate"></link><link href="http://joedicastro.com/atom.xml" rel="self"></link><id>http://joedicastro.com</id><updated>2011-12-16T00:00:00+01:00</updated><entry><title>Cambio de diseño</title><link href="http://joedicastro.com/cambio-de-diseno.html" rel="alternate"></link><updated>2011-12-16T00:00:00+01:00</updated><author><name>joe di castro</name></author><id>http://joedicastro.com/cambio-de-diseno.html</id><summary type="html">&lt;p&gt;Después de los ligeros cambios de diseño que introduje en el sitio la semana 
pasada he decidido ir un poco más allá y dar un giro completo al mismo, 
conservando la estructura (casi no he tocado el HTML), pero con un diseño 
más limpio y minimalista.&lt;/p&gt;
&lt;p&gt;La idea principal es la de favorecer aún más la lectura del mismo y darle el protagonismo absoluto al contenido (en el diseño anterior ya tenia un gran peso), 
eliminando todos aquellos elementos superfluos que pueden entorpecer o distraer 
de la lectura del mismo. Por eso he realizado cambios en la tipografía, el fondo, 
la división gráfica de las secciones, los cuadros con fragmentos de código y he 
eliminado los enlaces e iconos de la parte inferior. Además he transformado la 
página de &lt;em&gt;about me&lt;/em&gt; en la de entrada al sitio. &lt;/p&gt;
&lt;h2 id="motivos_del_cambio"&gt;Motivos del cambio&lt;/h2&gt;
&lt;p&gt;Realmente los motivos que me han llevado a realizar el cambio son mi propia 
tendencia, y cada vez la de más gente, a leer los contenidos web de otra forma.
Normalmente leo los articulos a través de un lector de feeds, como 
&lt;strong&gt;Google Reader&lt;/strong&gt; o &lt;a href="http://www.rssowl.org/"&gt;RSSOwl&lt;/a&gt;, donde el contenido está 
compuesto únicamente por el texto y las imágenes originales del mismo, 
sobre un diseño sencillo que nos elimina cualquier trazo del diseño original. 
Esto facilita la lectura y unifica el diseño de sitios muy dispares. Lo que nos 
libra además de tener que lidiar con diseños recargados de imágenes, iconos, 
enlaces y publicidad&lt;sup id="fnref:1"&gt;&lt;a href="#fn:1" rel="footnote"&gt;1&lt;/a&gt;&lt;/sup&gt;, centrándonos únicamente en lo que realmente importa: 
&lt;strong&gt;el contenido&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Esta tendencia se ha acentuado últimamente con el éxito de servicios como los de 
&lt;a href="http://readitlaterlist.com/"&gt;Read It Later&lt;/a&gt; e &lt;a href="http://www.instapaper.com/"&gt;Instapaper&lt;/a&gt; que nos permiten postergar para un 
momento más idóneo la lectura de aquello que nos interesa leer con calma. Además 
nos permite leer el contenido en formato &lt;em&gt;"solo texto"&lt;/em&gt; y offline, sin tener que 
acceder al sitio original. Tal ha sido el éxito de estos servicios y tan clara la 
tendencia a emplear el modo texto de los mismos, que han surgido algunos nuevos 
servicios destinados precisamente a facilitar la lectura de cualquier contenido 
web. Los más destacados son &lt;a href="http://www.readability.com/"&gt;Readability&lt;/a&gt; y &lt;a href="http://www.evernote.com/about/download/clearly.php"&gt;Evernote Clearly&lt;/a&gt;. 
Personalmente, cuando visito una pagina que tiene un diseño algo cargado y me 
interesa leer su contenido, no me lo pienso dos veces; tecleo &lt;strong&gt; &lt;code&gt;:rea ↹ ↵&lt;/code&gt; &lt;/strong&gt; en 
&lt;a href="http://dactyl.sourceforge.net/pentadactyl/"&gt;Pentadactyl&lt;/a&gt; y en par de segundos tengo el texto en &lt;strong&gt;Readability&lt;/strong&gt; para 
leerlo tranquilamente sin distracciones.&lt;/p&gt;
&lt;p&gt;Aunque realmente no me acaba de convencer el diseño último de ninguno de estos 
sitios, por eso es mi idea el proporcionar la mejor experiencia de lectura 
posible de mi sitio, sin tener que recurrir a servicios externos. Porque desde mi 
punto de vista "lo simple es bello" y "menos es más", eliminar lo superfluo, 
contribuye a mejorar la calidad del diseño.&lt;/p&gt;
&lt;h2 id="inspiraci+n"&gt;Inspiración&lt;/h2&gt;
&lt;p&gt;Para el nuevo diseño, me he inspirado principalmente en los diseños de blogs en 
esta línea que más me gustan, como son los de &lt;a href="http://stevelosh.com/about/"&gt;Steve Losh&lt;/a&gt;, 
&lt;a href="http://lucumr.pocoo.org/about/"&gt;Armin Ronacher&lt;/a&gt;, el tema &lt;a href="http://jimbarraud.com/manifest/"&gt;Manifest&lt;/a&gt; para Wordpress o algunos 
de los temas minimalistas más empleados en &lt;strong&gt;Tumblr&lt;/strong&gt;. &lt;/p&gt;
&lt;p&gt;Quizá lo más importante haya sido el cambio a una tipografía &lt;a href="http://es.wikipedia.org/wiki/Serif"&gt;Serif&lt;/a&gt;, por ser más cómoda la lectura empleando la misma. 
Y también el haberme decidido a emplear una &lt;a href="http://es.wikipedia.org/wiki/Web_Open_Font_Format"&gt;Web Font&lt;/a&gt; después de haberlas descartado inicialmente para el sitio, 
para las cabeceras. Al haber eliminado los iconos inferiores y depender de una 
hoja de estilo alojada externamente en los servidores de Google, apenas se ve 
afectada la velocidad de carga y el peso de la página.&lt;/p&gt;
&lt;h2 id="css_3_para_los_bloques_de_c+digo"&gt;CSS 3 para los bloques de código&lt;/h2&gt;
&lt;p&gt;Quizá lo más espectacular del cambio de diseño sea el nuevo aspecto que tienen 
ahora los bloques en los que se presentan los fragmentos de código, como el que 
sigue. Para poder verlos debidamente es necesario emplear una versión reciente 
del navegador web que permita HTML 5 y CSS 3. &lt;/p&gt;
&lt;p&gt;Estas son las reglas CSS 3 que empleo para implementarlo:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="nc"&gt;.codehilite&lt;/span&gt;&lt;span class="nd"&gt;:before&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;.codehilite&lt;/span&gt;&lt;span class="nd"&gt;:after&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;content&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot; &amp;quot;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;position&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="k"&gt;absolute&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;z-index&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="m"&gt;-1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;transform&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;skew&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;-3&lt;/span&gt;&lt;span class="n"&gt;deg&lt;/span&gt;&lt;span class="o"&gt;,-&lt;/span&gt;&lt;span class="m"&gt;2&lt;/span&gt;&lt;span class="n"&gt;deg&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;ms&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;transform&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;skew&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;-3&lt;/span&gt;&lt;span class="n"&gt;deg&lt;/span&gt;&lt;span class="o"&gt;,-&lt;/span&gt;&lt;span class="m"&gt;2&lt;/span&gt;&lt;span class="n"&gt;deg&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;webkit&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;transform&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;skew&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;-3&lt;/span&gt;&lt;span class="n"&gt;deg&lt;/span&gt;&lt;span class="o"&gt;,-&lt;/span&gt;&lt;span class="m"&gt;2&lt;/span&gt;&lt;span class="n"&gt;deg&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="err"&gt;o&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;transform&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;skew&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;-3&lt;/span&gt;&lt;span class="n"&gt;deg&lt;/span&gt;&lt;span class="o"&gt;,-&lt;/span&gt;&lt;span class="m"&gt;2&lt;/span&gt;&lt;span class="n"&gt;deg&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;moz&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;transform&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;skew&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;-3&lt;/span&gt;&lt;span class="n"&gt;deg&lt;/span&gt;&lt;span class="o"&gt;,-&lt;/span&gt;&lt;span class="m"&gt;2&lt;/span&gt;&lt;span class="n"&gt;deg&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;bottom&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="m"&gt;14px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;left&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;box&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;shadow&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0px&lt;/span&gt; &lt;span class="m"&gt;15px&lt;/span&gt; &lt;span class="m"&gt;5px&lt;/span&gt; &lt;span class="m"&gt;#aaa&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;height&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="m"&gt;50px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;max-width&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="m"&gt;50&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;width&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="m"&gt;50&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nc"&gt;.codehilite&lt;/span&gt;&lt;span class="nd"&gt;:after&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;transform&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;skew&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;3&lt;/span&gt;&lt;span class="n"&gt;deg&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;&lt;span class="m"&gt;2&lt;/span&gt;&lt;span class="n"&gt;deg&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;ms&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;transform&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;skew&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;3&lt;/span&gt;&lt;span class="n"&gt;deg&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;&lt;span class="m"&gt;2&lt;/span&gt;&lt;span class="n"&gt;deg&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;webkit&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;transform&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;skew&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;3&lt;/span&gt;&lt;span class="n"&gt;deg&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;&lt;span class="m"&gt;2&lt;/span&gt;&lt;span class="n"&gt;deg&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="err"&gt;o&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;transform&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;skew&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;3&lt;/span&gt;&lt;span class="n"&gt;deg&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;&lt;span class="m"&gt;2&lt;/span&gt;&lt;span class="n"&gt;deg&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;moz&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;transform&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;skew&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;3&lt;/span&gt;&lt;span class="n"&gt;deg&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;&lt;span class="m"&gt;2&lt;/span&gt;&lt;span class="n"&gt;deg&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;left&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="k"&gt;auto&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;right&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nc"&gt;.codehilite&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;position&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="k"&gt;relative&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;padding&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;background&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#fafafa&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;margin&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="m"&gt;25px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;box&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;shadow&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0px&lt;/span&gt; &lt;span class="m"&gt;0px&lt;/span&gt; &lt;span class="m"&gt;1px&lt;/span&gt; &lt;span class="m"&gt;1px&lt;/span&gt; &lt;span class="m"&gt;#ccc&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Aunque desafortunadamente emplear la propiedad &lt;code&gt;transform&lt;/code&gt; y sus equivalentes 
para cada familia de navegadores, provoca que no se valide por ahora correctamente 
como CSS 3 valido, ya que la propiedad aún está en proceso de estandarización.&lt;/p&gt;
&lt;h3 id="dise+o_antiguo"&gt;Diseño antiguo&lt;/h3&gt;
&lt;p&gt;Por supuesto &lt;em&gt;nunca llueve a gusto de todos&lt;/em&gt; y habrá quien prefiera el aspecto 
anterior de la página, pero sinceramente creo que esto nuevo diseño contribuye en 
gran manera a ayudar a "digerir" los "pequeños" artículos con los que suelo 
prodigarme. Para que quede como testigo del cambio, está imagen corresponde 
con el diseño que tenia anteriormente el blog:&lt;/p&gt;
&lt;p style="text-align:center;"&gt;&lt;img src="pictures/diseño_2011.png" 
alt="diseño anterior del blog" title="diseño anterior del blog" width=700 
height=813 /&gt;&lt;/p&gt;

&lt;div class="footnote"&gt;
&lt;hr /&gt;
&lt;ol&gt;
&lt;li id="fn:1"&gt;
&lt;p&gt;Bueno, nos libra de "casi" toda la publicidad, algunos deberían aprender que incluir publicidad en los feeds es de bastante mal gusto.
&amp;#160;&lt;a href="#fnref:1" rev="footnote" title="Jump back to footnote 1 in the text"&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;</summary><category term="blog"></category><category term="diseño"></category><category term="css"></category></entry><entry><title>Convertir ficheros djvu a pdf en Linux</title><link href="http://joedicastro.com/convertir-ficheros-djvu-a-pdf-en-linux.html" rel="alternate"></link><updated>2011-12-03T00:00:00+01:00</updated><author><name>joe di castro</name></author><id>http://joedicastro.com/convertir-ficheros-djvu-a-pdf-en-linux.html</id><summary type="html">&lt;p&gt;Tengo por costumbre almacenar mis documentos escaneados en el formato 
&lt;a href="http://es.wikipedia.org/wiki/DjVu"&gt;djvu&lt;/a&gt;, que fue expresamente creado para esa tarea y que otorga la mejor 
calidad posible en el menor espacio. Es el formato perfecto para documentos 
complejos sobre los que no se va a realizar un &lt;a href="http://es.wikipedia.org/wiki/Reconocimiento_%C3%B3ptico_de_caracteres"&gt;OCR&lt;/a&gt; (aunque también lo soporta). Además es un formato abierto, por lo que nos garantiza que podrá seguir 
empleándose en un futuro. Pero a veces necesito compartir estos ficheros con 
otros y para evitarme problemas suelo convertirlos a un formato más conocido y 
difundido como &lt;a href="http://es.wikipedia.org/wiki/Pdf"&gt;PDF&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Para realizar esta conversión empleo desde hace años (la primera versión es del 
2009) un sencillo script en python. Ahora que he necesitado una conversión 
masiva de documentos de un formato al otro, he modificado el script para hacer 
esto más sencillo y he decidido compartirlo con cualquiera que pueda necesitarlo. &lt;/p&gt;
&lt;h2 id="los_requisitos_previos"&gt;Los requisitos previos&lt;/h2&gt;
&lt;p&gt;Está diseñado para funcionar en Linux y necesita de la instalación de dos 
pequeños programas que son los que realmente realizan la conversión. Estos dos 
programas son &lt;code&gt;ddjvu&lt;/code&gt; y &lt;code&gt;tiff2pdf&lt;/code&gt;. Además de tener instalado &lt;strong&gt;Python&lt;/strong&gt; en una 
versión &lt;em&gt;2.7&lt;/em&gt; o superior. Estos dos programas vienen en los repositorios de 
prácticamente todas las distribuciones importantes dentro de los paquetes 
&lt;a href="http://djvu.sourceforge.net/"&gt;djvulibre&lt;/a&gt; y &lt;a href="http://libtiff.maptools.org"&gt;libtiff&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;En el caso de no tenerlos instalados, la instalación de los mismos es muy 
sencilla, para distribuciones basadas en Debian/Ubuntu:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="gp"&gt;$&lt;/span&gt; apt-get install djvulibre-bin libtiff-tools
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;&lt;code&gt;ddjvu&lt;/code&gt; nos extrae las páginas que conforman el documento &lt;em&gt;.djvu&lt;/em&gt; a un archivo intermedio en formato &lt;em&gt;.tiff&lt;/em&gt; y &lt;code&gt;tiff2pdf&lt;/code&gt; nos lo convierte en &lt;em&gt;.pdf&lt;/em&gt;.&lt;/p&gt;
&lt;h2 id="modo_de_empleo"&gt;Modo de empleo&lt;/h2&gt;
&lt;p&gt;Emplearlo es muy sencillo, como se puede ver en la ayuda del mismo:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="gp"&gt;$&lt;/span&gt; djvu2pdf -h
&lt;span class="go"&gt;usage: djvu2pdf [-h] [-d | -z] [-v] file [file ...]&lt;/span&gt;

&lt;span class="go"&gt;Converts a djvu file into a pdf file&lt;/span&gt;

&lt;span class="go"&gt;positional arguments:&lt;/span&gt;
&lt;span class="go"&gt;  file           The djvu file&lt;/span&gt;

&lt;span class="go"&gt;optional arguments:&lt;/span&gt;
&lt;span class="go"&gt;  -h, --help     show this help message and exit&lt;/span&gt;
&lt;span class="go"&gt;  -d             no compression. Best quality but big files.&lt;/span&gt;
&lt;span class="go"&gt;  -z             zip compression. More quality, more size.&lt;/span&gt;
&lt;span class="go"&gt;  -v, --version  show program&amp;#39;s version number and exit&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Básicamente llamandalo desde python y poniendo a continuación el nombre del 
fichero/s es lo único que necesitamos para ejecutarlo, por ejemplo:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="gp"&gt;$&lt;/span&gt; ls
&lt;span class="go"&gt;documento.djvu  documento_2.djvu&lt;/span&gt;
&lt;span class="gp"&gt;$&lt;/span&gt; python djvu2pdf.py documento.djvu documento_2.djvu
&lt;span class="gp"&gt;$&lt;/span&gt; ls
&lt;span class="go"&gt;documento.djvu  documento.pdf  documento_2.djvu  documento_2.pdf&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Opcionalmente tenemos las opciones &lt;code&gt;-d&lt;/code&gt; y &lt;code&gt;-z&lt;/code&gt;, que nos sirven para especificar 
si queremos no emplear compresión en el &lt;em&gt;.pdf&lt;/em&gt; (por defecto emplea compresión 
&lt;a href="http://es.wikipedia.org/wiki/Jpeg"&gt;jpeg&lt;/a&gt;) o emplear compresión &lt;a href="http://es.wikipedia.org/wiki/Formato_de_compresi%C3%B3n_ZIP"&gt;zip&lt;/a&gt;, respectivamente. Si no empleamos compresión, la calidad final será la mejor posible, pero los archivos serán muy grandes. En cambio, empleando &lt;em&gt;zip&lt;/em&gt;, tenemos unos ficheros ligeramente mayores a cambio de una calidad muy buena. Aunque la compresión &lt;em&gt;zip&lt;/em&gt; puede dar problemas 
con algunos visores y lectores de ebooks.&lt;/p&gt;
&lt;h2 id="el_script_djvu2pdfpy"&gt;El script, djvu2pdf.py&lt;/h2&gt;
&lt;p&gt;El contenido del scipt es el que sigue. Este está disponible, actualizado siempre 
a la última versión, en mi &lt;a href="https://bitbucket.org/joedicastro/python-recipes/src/tip/src/djvu2pdf.py"&gt;repositorio&lt;/a&gt;&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="c"&gt;#!/usr/bin/env python&lt;/span&gt;
&lt;span class="c"&gt;# -*- coding: utf8 -*-&lt;/span&gt;

&lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="sd"&gt;    djvu2pdf.py: Converts a .djvu file into a .pdf file&lt;/span&gt;
&lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;

&lt;span class="c"&gt;#==============================================================================&lt;/span&gt;
&lt;span class="c"&gt;# This Script does exactly as the description above says.&lt;/span&gt;
&lt;span class="c"&gt;#==============================================================================&lt;/span&gt;

&lt;span class="c"&gt;#==============================================================================&lt;/span&gt;
&lt;span class="c"&gt;#    Copyright 2011 joe di castro &amp;lt;joe@joedicastro.com&amp;gt;&lt;/span&gt;
&lt;span class="c"&gt;#&lt;/span&gt;
&lt;span class="c"&gt;#    This program is free software: you can redistribute it and/or modify&lt;/span&gt;
&lt;span class="c"&gt;#    it under the terms of the GNU General Public License as published by&lt;/span&gt;
&lt;span class="c"&gt;#    the Free Software Foundation, either version 3 of the License, or&lt;/span&gt;
&lt;span class="c"&gt;#    (at your option) any later version.&lt;/span&gt;
&lt;span class="c"&gt;#&lt;/span&gt;
&lt;span class="c"&gt;#    This program is distributed in the hope that it will be useful,&lt;/span&gt;
&lt;span class="c"&gt;#    but WITHOUT ANY WARRANTY; without even the implied warranty of&lt;/span&gt;
&lt;span class="c"&gt;#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the&lt;/span&gt;
&lt;span class="c"&gt;#    GNU General Public License for more details.&lt;/span&gt;
&lt;span class="c"&gt;#&lt;/span&gt;
&lt;span class="c"&gt;#    You should have received a copy of the GNU General Public License&lt;/span&gt;
&lt;span class="c"&gt;#    along with this program.  If not, see &amp;lt;http://www.gnu.org/licenses/&amp;gt;.&lt;/span&gt;
&lt;span class="c"&gt;#==============================================================================&lt;/span&gt;

&lt;span class="n"&gt;__author__&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;joe di castro &amp;lt;joe@joedicastro.com&amp;gt;&amp;quot;&lt;/span&gt;
&lt;span class="n"&gt;__license__&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;GNU General Public License version 3&amp;quot;&lt;/span&gt;
&lt;span class="n"&gt;__date__&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;03/12/2011&amp;quot;&lt;/span&gt;
&lt;span class="n"&gt;__version__&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;0.3&amp;quot;&lt;/span&gt;

&lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;sys&lt;/span&gt;
    &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;os&lt;/span&gt;
    &lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;argparse&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;ArgumentParser&lt;/span&gt;
    &lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;subprocess&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Popen&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;PIPE&lt;/span&gt;
&lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="ne"&gt;ImportError&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="c"&gt;# Checks the installation of the necessary python modules&lt;/span&gt;
    &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;linesep&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;join&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;An error found importing one module:&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;exc_info&lt;/span&gt;&lt;span class="p"&gt;()[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]),&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;You need to install it&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;Stopping...&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;]))&lt;/span&gt;
    &lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;exit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;check_execs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;progs&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;Check if the programs are installed, if not exit and report.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;prog&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;progs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;Popen&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="n"&gt;prog&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;--help&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;stdout&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;PIPE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;stderr&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;PIPE&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="ne"&gt;OSError&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;msg&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;The {0} program is necessary to run the script&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;prog&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;exit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;arguments&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;Defines the command line arguments for the script.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
    &lt;span class="n"&gt;main_desc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;&amp;quot;&amp;quot;Converts a djvu file into a pdf file&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;

    &lt;span class="n"&gt;parser&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ArgumentParser&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;description&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;main_desc&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;parser&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;add_argument&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;file&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;nargs&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;+&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;help&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;The djvu file&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;group&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;parser&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;add_mutually_exclusive_group&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;group&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;add_argument&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;-d&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;dest&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;qlty&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;action&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;store_const&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;const&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;-d&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                        &lt;span class="n"&gt;help&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;no compression. Best quality but big files.&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;group&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;add_argument&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;-z&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;dest&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;qlty&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;action&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;store_const&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;const&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;-z&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                        &lt;span class="n"&gt;help&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;zip compression. More quality, more size.&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;parser&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;add_argument&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;-v&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;--version&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;action&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;version&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                        &lt;span class="n"&gt;version&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;&lt;/span&gt;&lt;span class="si"&gt;%(prog)s&lt;/span&gt;&lt;span class="s"&gt; {0}&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;__version__&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
                        &lt;span class="n"&gt;help&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;show program&amp;#39;s version number and exit&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;parser&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;process&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;command&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;fname&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;Process the external commands and report the errors.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
    &lt;span class="n"&gt;errors&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Popen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;command&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;stderr&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;PIPE&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;stderr&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;readlines&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;errors&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;{0}: {1}&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fname&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;upper&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;rstrip&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;linesep&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;Main section.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
    &lt;span class="n"&gt;args&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;arguments&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;parse_args&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;djvu_files&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;file&lt;/span&gt;

    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;djvu&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;djvu_files&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;exists&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;djvu&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
            &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;ERROR: cannot open &amp;#39;{0}&amp;#39; (No such file)&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;djvu&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;djvu_filename&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;djvu&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;.djvu&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
            &lt;span class="n"&gt;tiff&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;{0}.tif&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;djvu_filename&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;pdf&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;{0}.pdf&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;djvu_filename&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;process&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;ddjvu&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;-format=tiff&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;djvu&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tiff&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;tiff&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;exists&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tiff&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
                &lt;span class="n"&gt;quality&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;qlty&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;qlty&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;-j&amp;quot;&lt;/span&gt;
                &lt;span class="n"&gt;process&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;tiff2pdf&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;quality&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;-o&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;pdf&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tiff&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;pdf&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;remove&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tiff&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;__name__&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;__main__&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;check_execs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;ddjvu&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;tiff2pdf&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;</summary><category term="script"></category><category term="python"></category><category term="djvu"></category><category term="pdf"></category><category term="linux"></category></entry><entry><title>Productividad en el escritorio Linux: Xmonad</title><link href="http://joedicastro.com/productividad-en-el-escritorio-linux-xmonad.html" rel="alternate"></link><updated>2011-11-26T00:00:00+01:00</updated><author><name>joe di castro</name></author><id>http://joedicastro.com/productividad-en-el-escritorio-linux-xmonad.html</id><summary type="html">&lt;p&gt;&lt;a href="http://xmonad.org"&gt;xmonad&lt;/a&gt; es un &lt;a href="http://joedicastro.com/productividad-en-el-escritorio-linux-tiling.html"&gt;Gestor de ventanas de mosaico&lt;/a&gt;, uno de los más 
empleados y de los más potentes. Después de haber probado varios (dwm, bluetile, 
wmii y el plugin Compiz Grid) llevo ya unos cuantos meses trabajando con él y 
posiblemente siga conmigo mucho, mucho tiempo. Una vez que trabajas con un 
&lt;strong&gt;tiling window manager&lt;/strong&gt; es muy difícil que echarse atrás, volver a un gestor de
ventanas flotantes tradicional es casi impensable. Probaré uno más, 
&lt;a href="http://awesome.naquadah.org"&gt;awesome&lt;/a&gt;, que promete mucho y solo si este logra convencerme del cambio, 
abandonaré Xmonad. &lt;/p&gt;
&lt;p&gt;¿Pero que tiene de especial un &lt;strong&gt;twm&lt;/strong&gt; como &lt;strong&gt;Xmonad&lt;/strong&gt; para haber decidido 
abandonar la gestión de ventanas por defecto de Compiz, Gnome Shell, Unity, Kwin, 
etc y apostar por él? Cuando uno está cansado de perder el tiempo con maniobras y 
decisiones triviales -ajustar las ventanas en la pantalla, que si el navegador por 
aquí, que si este terminal por allá, etc y cambiando continuamente la mano del 
teclado al ratón y viceversa- pues entonces decides que hay que darle una 
oportunidad a un twm, que sea el gestor de ventanas quien haga el trabajo sucio 
por ti.&lt;/p&gt;
&lt;p&gt;¿Y hasta que punto resuelve bien este problema un twm como Xmonad? En este vídeo 
de xmonad trabajando con mi configuración, uno se puede hacer una idea de lo que 
es capaz.&lt;/p&gt;
&lt;div style="text-align:center"&gt;
&lt;iframe src="http://player.vimeo.com/video/33164152?title=0&amp;amp;byline=0&amp;amp;portrait=0&amp;amp;color=59a5d1" width="667" height="417"&gt;&lt;/iframe&gt;&lt;p&gt;Esbozo de las posibilidades de un Tiling Window Manager como Xmonad trabajando conjuntamente con Gnome 3 y Unity 2D sobre un Ubuntu 11.10. Recomiendo ver en HD.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Xmonad&lt;/strong&gt; no necesita de un entorno de escritorio para trabajar, se puede iniciar 
una sesión directamente en él, al igual que la mayoría de gestores de ventanas 
de mosaico y otros gestores de ventanas como &lt;a href="http://es.wikipedia.org/wiki/Openbox"&gt;OpenBox&lt;/a&gt;. En el vídeo se puede 
apreciar que lo estoy empleando conjuntamente con Unity, en concreto &lt;strong&gt;Unity 2D&lt;/strong&gt;. 
Esto es así porque emplear Xmonad (u otro twm) de forma autónoma te obliga a 
montar una serie de servicios que vienen montados por defecto normalmente en un 
escritorio, como salvapantallas, fondo de pantalla, bandeja de sistema, gestor 
de red, notificaciones, control de volumen, soporte impresoras, etc. Como estoy 
contento con el funcionamiento en general de Gnome, lo mejor es no reinventar la 
rueda y aprovechar lo mejor de los dos mundos empleando ambos a la vez. 
Anteriormente con Gnome 2, lo tenia funcionando sin Unity, pero con Gnome 3 me 
encuentro con la desagradable situación de que Gnome classic (el fallback mode) 
tiene un panel que es un autentico desastre y que además no es redimensionable 
en altura (tiene 30px y yo lo quiero con 21px). Actualmente hay una 
&lt;a href="http://askubuntu.com/questions/69576/how-to-customize-the-gnome-classic-panel/76884#76884"&gt;forma de arreglar esto&lt;/a&gt;, pero de momento lo tengo montado todo con Unity 
2D y no hecho nada en falta. Eso si, de Unity solo empleo el panel, el Global Menu 
y la base de Gnome 3, lo demás lo descarto. Resumiendo, que básicamente sustituyo 
&lt;code&gt;metacity&lt;/code&gt; por &lt;code&gt;xmonad&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id="xmonad_con_unity_2d"&gt;Xmonad con Unity 2D&lt;/h2&gt;
&lt;p&gt;Aquí contaré como tengo configurado actualmente &lt;strong&gt;Xmonad&lt;/strong&gt; con &lt;strong&gt;Ubuntu 11.10&lt;/strong&gt;
(Oneiric Ocelot) y con &lt;strong&gt;Gnome 3&lt;/strong&gt; y el panel de &lt;strong&gt;Unity 2D&lt;/strong&gt; (&lt;code&gt;unity-2p-panel&lt;/code&gt;). 
Si alguien continua con Gnome 2D o alguna versión anterior de Ubuntu puede revisar 
&lt;a href="http://markhansen.co.nz/xmonad-ubuntu-lucid/"&gt;este enlace&lt;/a&gt;, la base es muy parecida, y luego solo habría que aplicar mi 
configuración (u otra).&lt;/p&gt;
&lt;p&gt;Lo primero que se necesita es instalar xmonad, que en Ubuntu viene siendo así:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;sudo apt-get install xmonad
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;A continuación crearíamos un fichero &lt;code&gt;~/.xmonad/xmonad.hs&lt;/code&gt; de configuración 
básica &lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;mkdir ~/.xmonad           &lt;span class="c"&gt;# El carácter ~ se obtiene pulsando Alt Gr + 4&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;vi ~/.xmonad/xmonad.hs    &lt;span class="c"&gt;# Puedes emplear gedit en vez de vi si lo prefieres&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;e incluiríamos lo siguiente dentro de él:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="kr"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;XMonad&lt;/span&gt;
&lt;span class="kr"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;XMonad.Config.Gnome&lt;/span&gt;

&lt;span class="nf"&gt;myManageHook&lt;/span&gt; &lt;span class="ow"&gt;=&lt;/span&gt; &lt;span class="n"&gt;composeAll&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="n"&gt;manageHook&lt;/span&gt; &lt;span class="n"&gt;gnomeConfig&lt;/span&gt;
    &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;className&lt;/span&gt; &lt;span class="o"&gt;=?&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;Unity-2d-panel&amp;quot;&lt;/span&gt; &lt;span class="o"&gt;--&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;doIgnore&lt;/span&gt;
    &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;className&lt;/span&gt; &lt;span class="o"&gt;=?&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;Unity-2d-launcher&amp;quot;&lt;/span&gt; &lt;span class="o"&gt;--&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;doFloat&lt;/span&gt;
    &lt;span class="p"&gt;])&lt;/span&gt;

&lt;span class="nf"&gt;main&lt;/span&gt; &lt;span class="ow"&gt;=&lt;/span&gt; &lt;span class="n"&gt;xmonad&lt;/span&gt; &lt;span class="n"&gt;gnomeConfig&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;manageHook&lt;/span&gt; &lt;span class="ow"&gt;=&lt;/span&gt; &lt;span class="n"&gt;myManageHook&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Ahora tendríamos que recompilar Xmonad, es algo necesario cada vez que realizamos 
algún cambio en la configuración, pero es un proceso que no suele llevar más de 
5 segundos (y Xmonad se puede reiniciar sin tener que cerrar la sesión):&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;xmonad --recompile
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Lo siguiente es crear los ficheros necesarios para crear una nueva sesión de 
Gnome. Creamos primero el fichero que define nuestra nueva sesión:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;sudo vi /usr/share/gnome-session/sessions/xmonad.session
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;y dentro añadimos esto:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="k"&gt;[GNOME Session]&lt;/span&gt;
&lt;span class="na"&gt;Name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;Xmonad Unity 2D&lt;/span&gt;
&lt;span class="na"&gt;RequiredComponents&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;gnome-settings-daemon;&lt;/span&gt;
&lt;span class="na"&gt;RequiredProviders&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;windowmanager;panel;&lt;/span&gt;
&lt;span class="na"&gt;DefaultProvider-windowmanager&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;xmonad&lt;/span&gt;
&lt;span class="na"&gt;DefaultProvider-panel&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;unity-2d-panel&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;a continuación creamos el fichero que inicia esta sesión:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;sudo vi /usr/share/xsessions/xmonad-unity-session.desktop
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;con este contenido:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="k"&gt;[Desktop Entry]&lt;/span&gt;
&lt;span class="na"&gt;Name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;XMonad Unity 2D&lt;/span&gt;
&lt;span class="na"&gt;Comment&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;Tiling window manager&lt;/span&gt;
&lt;span class="na"&gt;TryExec&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;/usr/bin/gnome-session&lt;/span&gt;
&lt;span class="na"&gt;Exec&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;gnome-session --session=xmonad&lt;/span&gt;
&lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;XSession&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Con esto estaría ya montada una sesión con la configuración por defecto de 
Xmonad funcionado sobre Gnome y empleando el panel de Unity 2D. Ahora solo 
tendrías que cerrar la sesión e iniciar la sesión con &lt;em&gt;Xmonad Unity 2D&lt;/em&gt;. &lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Advertencia:&lt;/strong&gt; Por defecto verás que se ha creado automáticamente una sesión 
 llamada Xmonad al instalar este. Bien, si vas iniciar esa sesión, será mejor 
 que sepas que para salir tienes que pulsar &lt;strong&gt;Win + Mayús + Q&lt;/strong&gt;, porque será una 
 sesión donde solo tendrás Xmonad, y es la única forma de cerrar la sesión.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Si queréis hacer alguna prueba con esta configuración básica por defecto, es 
mejor tener a mano una referencia con las combinaciones de teclas disponibles, 
aquí hay un &lt;a href="http://haskell.org/wikiupload/b/b8/Xmbindings.png"&gt;mapa del teclado&lt;/a&gt; de referencia que será muy útil.&lt;/p&gt;
&lt;h2 id="mi_configuraci+n"&gt;Mi configuración&lt;/h2&gt;
&lt;p&gt;Para poder emplear la configuración que se puede ver en el vídeo simplemente 
habría que editar el fichero &lt;code&gt;~/.xmonad/xmonad.hs&lt;/code&gt; y sustituir su contenido por 
el de mi fichero de configuración. El contenido del fichero lo incluyo al final 
del articulo para no entorpecer la lectura del mismo.&lt;/p&gt;
&lt;h3 id="atajos_de_teclado"&gt;Atajos de teclado&lt;/h3&gt;
&lt;p&gt;Al comienzo del fichero enumero las combinaciones de teclas empleadas en mi 
configuración, que difieren de las combinaciones por defecto y se asemejan de 
algún modo a las que estaba habituado en Gnome y son más intuitivas para los que 
estamos acostumbrados a él. Aunque las que se emplean por defecto también me son 
muy familiares al estar basadas en parte en Vim. Voy a detallar algunas de estas 
combinaciones.&lt;/p&gt;
&lt;h4 id="lanzar_aplicaciones"&gt;Lanzar aplicaciones&lt;/h4&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="c1"&gt;-- Win  +  Enter                   Terminal&lt;/span&gt;
&lt;span class="c1"&gt;-- Win  +  F1                      Nautilus&lt;/span&gt;
&lt;span class="c1"&gt;-- Win  +  F2                      Firefox                  (single instance)&lt;/span&gt;
&lt;span class="c1"&gt;-- Win  +  F3                      Thunderbird              (single instance)&lt;/span&gt;
&lt;span class="c1"&gt;-- Win  +  F4                      RSSOwl                   (single instance)&lt;/span&gt;
&lt;span class="c1"&gt;-- Win  +  F5                      Hotot                    (single instance)&lt;/span&gt;
&lt;span class="c1"&gt;-- Win  +  F6                      ncmpcpp  - MPD player&lt;/span&gt;
&lt;span class="c1"&gt;-- Win  +  F7                      taskwarrior               &lt;/span&gt;
&lt;span class="c1"&gt;-- Win  +  F8                      PAC                      (single instance)&lt;/span&gt;
&lt;span class="c1"&gt;-- Win  +  F9                      Aptana                   (single instance)&lt;/span&gt;
&lt;span class="c1"&gt;-- Win  +  F10                     Xmind                    (single instance)&lt;/span&gt;
&lt;span class="c1"&gt;-- Win  +  F11                     Dbeaver                  (single instance)&lt;/span&gt;
&lt;span class="c1"&gt;-- Win  +  F12                     VirtualBox               (single instance)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Lo primero que se puede ver es una serie de atajos para lanzar aplicaciones 
empleando la tecla &lt;strong&gt;Win&lt;/strong&gt; más las teclas de función. La tecla &lt;strong&gt;Win&lt;/strong&gt; (también 
llamada &lt;strong&gt;Super&lt;/strong&gt;) es la tecla maestra por defecto de Xmonad (&lt;code&gt;mod4Mask&lt;/code&gt;) y es la 
que se emplea en combinación con otras para realizar todo tipo de acciones. Estas 
combinaciones me permiten lanzar las aplicaciones que uso con más frecuencia con 
un par de teclas. &lt;/p&gt;
&lt;p&gt;Algunas aparecen con la frase &lt;em&gt;single instance&lt;/em&gt; entre paréntesis, esto quiere 
decir que de estas aplicaciones solamente se abrirá una ventana. De hecho, lo que 
ocurre si volvemos a pulsar la combinación de teclas que abre la aplicación (una 
vez que esta ya está abierta) es que en lugar de abrir una nueva, nos movemos a 
la ventana ya abierta, esté en el escritorio que esté. Lo que es muy útil para 
localizar la ventana de una de estas aplicaciones de forma inmediata.&lt;/p&gt;
&lt;p&gt;Adicionalmente la combinación &lt;strong&gt;Alt + Enter&lt;/strong&gt; nos permite abrir una ventana del 
terminal. En este caso lo tengo predeterminado para que abra una venta de 
&lt;code&gt;terminator&lt;/code&gt;&lt;/p&gt;
&lt;h4 id="consola_emergente"&gt;Consola emergente&lt;/h4&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="c1"&gt;-- Win  +  Space                   Run or Raise Shell Prompt&lt;/span&gt;
&lt;span class="c1"&gt;-- Win  +  Shift   +  Space        Run Shell Prompt&lt;/span&gt;
&lt;span class="c1"&gt;-- Win  +  Control +  Space        Window Prompt&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;A continuación podemos ver las teclas dedicadas a la consola emergente inferior 
(al estilo de Guake, Yakuake o Tilda) y que según la combinación pulsada 
realizara una acción u otra. La consola cuenta con auto-completado de texto, con 
lo cual escribiendo las primeras letras se nos muestran todas las coincidencias 
disponibles, a través de las que podemos desplazarnos con el tabulador.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Win + Espacio&lt;/strong&gt; nos despliega una consola que nos permite lanzar una 
aplicación de igual modo que los lanzadores gráficos. Si la aplicación ya se 
encuentra abierta, nos dirige a la ventana de la misma.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Win + Mayús + Espacio&lt;/strong&gt; ejecuta el comando que escribamos. Similar a la 
ventana de ejecutar comando de Gnome (&lt;em&gt;Alt + F2&lt;/em&gt; en Gnome)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Win + Control + Espacio&lt;/strong&gt; nos muestra las ventanas que están abiertas en ese 
momento. Seleccionando una nos envía directamente a ella. Gracias a esto, 
localizar una ventana abierta entre los múltiples escritorios es cuestión de 
segundos. Si conocemos el nombre de la ventana podemos filtrarla escribiendo las 
primeras letras con el teclado. Imagina una sesión en la que tenemos abiertas más 
de 10 aplicaciones en varios escritorios distintos, esto nos permite localizar 
una ventana en segundos, es muy útil.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id="reiniciar_xmonad"&gt;Reiniciar Xmonad&lt;/h4&gt;
&lt;p&gt;Con la combinación &lt;strong&gt;Win + q&lt;/strong&gt; lo que hacemos es reiniciar Xmonad sin necesidad 
de reiniciar la sesión. Esto es muy útil cuando introducimos cambios en la 
configuración, después de guardar el fichero y ejecutar &lt;code&gt;xmonad --recompile&lt;/code&gt; en 
un terminal, pulsamos esta combinación y los cambios se reflejaran de forma 
inmediata.&lt;/p&gt;
&lt;h2 id="transparencias"&gt;Transparencias&lt;/h2&gt;
&lt;p&gt;Una de las carencias de Xmonad es que no incorpora un &lt;a href="http://es.wikipedia.org/wiki/Gestor_de_composici%C3%B3n_de_ventanas"&gt;gestor de composición&lt;/a&gt; 
por lo que si queremos manejar transparencias en nuestros terminales, 
notificaciones, ventanas, etc, debemos emplear uno externo. Para esto empleo 
&lt;code&gt;xcompmgr&lt;/code&gt; que funciona perfectamente y es muy ligero. Como aún conservo las 
otras sesiones de Ubuntu (Unity, Unity 2 D, Gnome Shell, etc) lo que hago para 
que solo se ejecute en la sesión de &lt;em&gt;Xmonad Unity 2D&lt;/em&gt; es emplear este script bash:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="c"&gt;#!/bin/bash&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nv"&gt;$DESKTOP_SESSION&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;xmonad-unity-session&amp;quot;&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;; &lt;span class="k"&gt;then&lt;/span&gt;
&lt;span class="k"&gt;    &lt;/span&gt;xcompmgr -I1 -O1 -Ff;    
&lt;span class="k"&gt;fi&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Luego añado el script a las aplicaciones que se ejecutan al inicio de la sesión 
y listo.&lt;/p&gt;
&lt;h2 id="bandeja_del_sistema"&gt;Bandeja del sistema&lt;/h2&gt;
&lt;p&gt;Una de las carencias de Unity en la versión 11.10 es que no han incorporado una 
bandeja del sistema donde alojar los iconos de aplicaciones que aún no tienen 
soporte para los &lt;em&gt;indicadores&lt;/em&gt;. Se supone que debería añadirlos de forma 
automática al panel, incluso hay una configuración a través de &lt;code&gt;dconf&lt;/code&gt; para 
habilitar esto, pero no funciona para todas las aplicaciones. Para añadir un 
&lt;em&gt;systray&lt;/em&gt; empleo un script parecido al anterior para lanzar la aplicación 
&lt;code&gt;trayer&lt;/code&gt; en esta sesión.&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="c"&gt;#!/bin/bash&lt;/span&gt;

&lt;span class="nv"&gt;MYARGS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;--edge top --widthtype request --transparent true --alpha 0 --distancefrom right --distance 1100 --height 24 --tint 0x4c4a44 --align right&amp;quot;&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nv"&gt;$DESKTOP_SESSION&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;xmonad-unity-session&amp;quot;&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;; &lt;span class="k"&gt;then&lt;/span&gt;
&lt;span class="k"&gt;    &lt;/span&gt;sleep 24 &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; trayer &lt;span class="nv"&gt;$MYARGS&lt;/span&gt;;
&lt;span class="k"&gt;fi&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;br /&gt;

&lt;h2 id="como_personalizar_xmonad_haskell"&gt;Como personalizar Xmonad: Haskell&lt;/h2&gt;
&lt;p&gt;El mayor inconveniente de Xmonad (y a la vez una de sus ventajas) es que la 
configuración es necesario hacerla a través del mismo lenguaje de programación 
en el que está creado: &lt;a href="http://haskell.org/haskellwiki/Haskell"&gt;Haskell&lt;/a&gt;. Esto es una importante barrera de 
entrada para los que no conocen el lenguaje, yo incluido, pero a base de ir 
probando algunas de las configuraciones aportadas y el prueba y error, puedes 
salir del paso muy satisfactoriamente. Con un poco de práctica y estudiando un 
poco el lenguaje (que de entrada no es de los más fáciles de leer) y gracias a 
la documentación disponible, puedes conseguir exactamente lo que quieres. Y esa es 
precisamente también su mayor ventaja, que puedes llegar a un gran nivel de 
personalización si conoces Haskell. Además Haskell le proporciona una gran 
estabilidad y extensibilidad. &lt;/p&gt;
&lt;p&gt;Afortunadamente hay varios recursos disponibles que nos hacen el trabajo más 
fácil, porque Xmonad está aceptablemente bien documentado (en Inglés).&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="http://haskell.org/haskellwiki/Xmonad/Screenshots"&gt;Pantallazos&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://haskell.org/haskellwiki/Xmonad/Config_archive"&gt;Ficheros de configuración&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://haskell.org/haskellwiki/Xmonad"&gt;Wiki&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://xmonad.org/xmonad-docs/xmonad/index.html"&gt;API Docs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://xmonad.org/xmonad-docs/xmonad-contrib/index.html"&gt;API Extensiones Docs&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="xmonadhs"&gt;Xmonad.hs&lt;/h2&gt;
&lt;p&gt;Este es mi mi fichero de configuración de Xmonad:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="c1"&gt;-- joe di castro&amp;#39;s xmonad.hs&lt;/span&gt;
&lt;span class="c1"&gt;-- Based on rupa&amp;#39;s xmonad.hs, https://github.com/rupa/xmonad&lt;/span&gt;

&lt;span class="c1"&gt;-- ===============================================================  KEY BINDINGS&lt;/span&gt;

&lt;span class="c1"&gt;------------------------------------------------- Launch (or Raise) Applications&lt;/span&gt;
&lt;span class="c1"&gt;-- Win  +  Enter                   Terminal&lt;/span&gt;
&lt;span class="c1"&gt;-- Win  +  F1                      Nautilus&lt;/span&gt;
&lt;span class="c1"&gt;-- Win  +  F2                      Firefox                  (single instance)&lt;/span&gt;
&lt;span class="c1"&gt;-- Win  +  F3                      Thunderbird              (single instance)&lt;/span&gt;
&lt;span class="c1"&gt;-- Win  +  F4                      RSSOwl                   (single instance)&lt;/span&gt;
&lt;span class="c1"&gt;-- Win  +  F5                      Hotot                    (single instance)&lt;/span&gt;
&lt;span class="c1"&gt;-- Win  +  F6                      ncmpcpp  - MPD player&lt;/span&gt;
&lt;span class="c1"&gt;-- Win  +  F7                      taskwarrior               &lt;/span&gt;
&lt;span class="c1"&gt;-- Win  +  F8                      PAC                      (single instance)&lt;/span&gt;
&lt;span class="c1"&gt;-- Win  +  F9                      Aptana                   (single instance)&lt;/span&gt;
&lt;span class="c1"&gt;-- Win  +  F10                     Xmind                    (single instance)&lt;/span&gt;
&lt;span class="c1"&gt;-- Win  +  F11                     Dbeaver                  (single instance)&lt;/span&gt;
&lt;span class="c1"&gt;-- Win  +  F12                     VirtualBox               (single instance)                                                             &lt;/span&gt;
&lt;span class="c1"&gt;----------------------------------------------------------- Shell/Window prompts&lt;/span&gt;
&lt;span class="c1"&gt;-- Win  +  Space                   Run or Raise Shell Prompt&lt;/span&gt;
&lt;span class="c1"&gt;-- Win  +  Shift   +  Space        Run Shell Prompt&lt;/span&gt;
&lt;span class="c1"&gt;-- Win  +  Control +  Space        Window Prompt&lt;/span&gt;
&lt;span class="c1"&gt;--------------------------------------------------------------------- Navigation&lt;/span&gt;
&lt;span class="c1"&gt;-- Win  +  [1..9]                  Switch to workspace N&lt;/span&gt;
&lt;span class="c1"&gt;-- Win  +  Shift   +  [1..9]       Move client to workspace N&lt;/span&gt;
&lt;span class="c1"&gt;-- Ctrl +  Alt     +  Left/Right   Previous/Next workspace&lt;/span&gt;
&lt;span class="c1"&gt;-- Alt  +  Tab                     Focus next window&lt;/span&gt;
&lt;span class="c1"&gt;-- Alt  +  Shift   +  Tab          Focus previous window&lt;/span&gt;
&lt;span class="c1"&gt;-------------------------------------------------------------- Window management&lt;/span&gt;
&lt;span class="c1"&gt;-- Win  +  Shift   +  Left         Move window to previous workspace&lt;/span&gt;
&lt;span class="c1"&gt;-- Win  +  Shift   +  Right        Move window to next workspace&lt;/span&gt;
&lt;span class="c1"&gt;-- Win  +  Control +  Left         Move window to previous empty workspace&lt;/span&gt;
&lt;span class="c1"&gt;-- Win  +  Control +  Right        Move window to next empty workspace&lt;/span&gt;
&lt;span class="c1"&gt;-- Ctrl +  Up/Down                 Move focused window up/down&lt;/span&gt;
&lt;span class="c1"&gt;-- Win  +  m                       Toggle focused window Full Screen&lt;/span&gt;
&lt;span class="c1"&gt;-- Win  +  n                       Refresh&lt;/span&gt;
&lt;span class="c1"&gt;-- Win  +  -                       Move focused windows to master area&lt;/span&gt;
&lt;span class="c1"&gt;-- Win  +  w  (or Alt + F4)        Close focused window&lt;/span&gt;
&lt;span class="c1"&gt;-- Win  +  t                       Back to tiling (unfloat floating window)&lt;/span&gt;
&lt;span class="c1"&gt;-- Win  +  Shift   +  t            Back All to tiling (unfloat ALL windows)&lt;/span&gt;
&lt;span class="c1"&gt;-------------------------------------------------------------  Layout management&lt;/span&gt;
&lt;span class="c1"&gt;-- Win  +  Tab                     Rotate layouts&lt;/span&gt;
&lt;span class="c1"&gt;-- Win  +  Left/Right              Shrink/Expand the master area&lt;/span&gt;
&lt;span class="c1"&gt;-- Win  +  Up/Down                 Mirror Shrink/expand&lt;/span&gt;
&lt;span class="c1"&gt;-- Win  +  ,/.                     Increment/Deincrement 1 window in master area&lt;/span&gt;
&lt;span class="c1"&gt;-- Win  +  f                       Hide/Unhide the gnome-panel/status bar&lt;/span&gt;
&lt;span class="c1"&gt;-- Win  +  Shift   +  n            Reset current workspace to main layout&lt;/span&gt;
&lt;span class="c1"&gt;------------------------------------------------------------------ Mosaic Layout&lt;/span&gt;
&lt;span class="c1"&gt;-- Win  +  a/z                     Taller/Wider&lt;/span&gt;
&lt;span class="c1"&gt;-- Win  +  Control +  n            Reset&lt;/span&gt;
&lt;span class="c1"&gt;------------------------------------------------------------------------- Others&lt;/span&gt;
&lt;span class="c1"&gt;-- Print Screen                    Capture screen&lt;/span&gt;
&lt;span class="c1"&gt;-- Win  +  q                       Restart XMonad&lt;/span&gt;
&lt;span class="c1"&gt;-- Win  +  Shift   +  q            Close gnome session dialog&lt;/span&gt;

&lt;span class="c1"&gt;-- ============================================================== MOUSE BINDINGS&lt;/span&gt;

&lt;span class="c1"&gt;-- Win  +  Button 1                Float Window and Move by dragging&lt;/span&gt;
&lt;span class="c1"&gt;-- Win  +  Button 2                Raise Window to the top&lt;/span&gt;
&lt;span class="c1"&gt;-- Win  +  Button 3                Float Window and Resize by dragging&lt;/span&gt;

&lt;span class="kr"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;XMonad&lt;/span&gt;
&lt;span class="kr"&gt;import&lt;/span&gt; &lt;span class="k"&gt;qualified&lt;/span&gt; &lt;span class="nn"&gt;XMonad.StackSet&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;W&lt;/span&gt;
&lt;span class="kr"&gt;import&lt;/span&gt; &lt;span class="k"&gt;qualified&lt;/span&gt; &lt;span class="nn"&gt;Data.Map&lt;/span&gt;        &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;M&lt;/span&gt;
&lt;span class="kr"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;Data.Monoid&lt;/span&gt;

&lt;span class="kr"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;XMonad.Actions.CycleWS&lt;/span&gt;
&lt;span class="kr"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;XMonad.Actions.FlexibleResize&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;Flex&lt;/span&gt;
&lt;span class="kr"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;XMonad.Actions.SinkAll&lt;/span&gt;
&lt;span class="kr"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;XMonad.Actions.UpdatePointer&lt;/span&gt;
&lt;span class="kr"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;XMonad.Actions.WindowGo&lt;/span&gt;

&lt;span class="kr"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;XMonad.Hooks.DynamicLog&lt;/span&gt;
&lt;span class="kr"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;XMonad.Hooks.EwmhDesktops&lt;/span&gt;
&lt;span class="kr"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;XMonad.Hooks.ManageDocks&lt;/span&gt;
&lt;span class="kr"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;XMonad.Hooks.ManageHelpers&lt;/span&gt;

&lt;span class="kr"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;XMonad.Layout.LayoutHints&lt;/span&gt;
&lt;span class="kr"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;XMonad.Layout.NoBorders&lt;/span&gt;
&lt;span class="kr"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;XMonad.Layout.ResizableTile&lt;/span&gt;
&lt;span class="kr"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;XMonad.Layout.Tabbed&lt;/span&gt;
&lt;span class="kr"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;XMonad.Layout.ToggleLayouts&lt;/span&gt;
&lt;span class="kr"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;XMonad.Layout.WindowArranger&lt;/span&gt;
&lt;span class="kr"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;XMonad.Layout.Mosaic&lt;/span&gt;

&lt;span class="kr"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;XMonad.Prompt&lt;/span&gt;
&lt;span class="kr"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;XMonad.Prompt.Input&lt;/span&gt;
&lt;span class="kr"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;XMonad.Prompt.RunOrRaise&lt;/span&gt;
&lt;span class="kr"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;XMonad.Prompt.Shell&lt;/span&gt;
&lt;span class="kr"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;XMonad.Prompt.Window&lt;/span&gt;

&lt;span class="kr"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;XMonad.Util.Run&lt;/span&gt;
&lt;span class="kr"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;XMonad.Util.Scratchpad&lt;/span&gt;
&lt;span class="kr"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;XMonad.Util.WorkspaceCompare&lt;/span&gt;
&lt;span class="kr"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;XMonad.Util.XSelection&lt;/span&gt;

&lt;span class="kr"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;XMonad.Config.Gnome&lt;/span&gt;

&lt;span class="c1"&gt;-- Mod4 is the Super / Windows key&lt;/span&gt;
&lt;span class="nf"&gt;winMask&lt;/span&gt; &lt;span class="ow"&gt;=&lt;/span&gt; &lt;span class="n"&gt;mod4Mask&lt;/span&gt;
&lt;span class="nf"&gt;altMask&lt;/span&gt; &lt;span class="ow"&gt;=&lt;/span&gt; &lt;span class="n"&gt;mod1Mask&lt;/span&gt;

&lt;span class="c1"&gt;-- key bindings&lt;/span&gt;
&lt;span class="nf"&gt;myKeys&lt;/span&gt; &lt;span class="n"&gt;conf&lt;/span&gt;&lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;XConfig&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="kt"&gt;XMonad&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;modMask&lt;/span&gt; &lt;span class="ow"&gt;=&lt;/span&gt; &lt;span class="n"&gt;modMask&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="ow"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;M&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fromList&lt;/span&gt; &lt;span class="o"&gt;$&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;winMask&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;                    &lt;span class="n"&gt;xK_Return&lt;/span&gt;   &lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;spawn&lt;/span&gt; &lt;span class="o"&gt;$&lt;/span&gt; &lt;span class="kt"&gt;XMonad&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;terminal&lt;/span&gt; &lt;span class="n"&gt;conf&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;winMask&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;                    &lt;span class="n"&gt;xK_F1&lt;/span&gt;       &lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;spawn&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;nautilus ~&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;winMask&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;                    &lt;span class="n"&gt;xK_F2&lt;/span&gt;       &lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;runOrRaise&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;firefox&amp;quot;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;className&lt;/span&gt; &lt;span class="o"&gt;=?&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;Firefox&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;winMask&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;                    &lt;span class="n"&gt;xK_F3&lt;/span&gt;       &lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;runOrRaise&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;thunderbird&amp;quot;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;className&lt;/span&gt; &lt;span class="o"&gt;=?&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;Thunderbird&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;winMask&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;                    &lt;span class="n"&gt;xK_F4&lt;/span&gt;       &lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;runOrRaise&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;./rssowl/RSSOwl&amp;quot;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;className&lt;/span&gt; &lt;span class="o"&gt;=?&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;RSSOwl&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;winMask&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;                    &lt;span class="n"&gt;xK_F5&lt;/span&gt;       &lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;runOrRaise&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;hotot&amp;quot;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;className&lt;/span&gt; &lt;span class="o"&gt;=?&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;Hotot&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;winMask&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;                    &lt;span class="n"&gt;xK_F6&lt;/span&gt;       &lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;spawn&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;terminator -e ncmpcpp&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;winMask&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;                    &lt;span class="n"&gt;xK_F7&lt;/span&gt;       &lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;spawn&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;terminator -e &amp;#39;task shell&amp;#39; -p task&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;    
    &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;winMask&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;                    &lt;span class="n"&gt;xK_F8&lt;/span&gt;       &lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;runOrRaise&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;pac&amp;quot;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;className&lt;/span&gt; &lt;span class="o"&gt;=?&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;Pac&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;        
    &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;winMask&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;                    &lt;span class="n"&gt;xK_F9&lt;/span&gt;       &lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;runOrRaise&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;./Aptana Studio 3/AptanaStudio3&amp;quot;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;className&lt;/span&gt; &lt;span class="o"&gt;=?&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;Aptana Studio 3&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;            
    &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;winMask&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;                    &lt;span class="n"&gt;xK_F10&lt;/span&gt;      &lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;runOrRaise&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;/usr/local/xmind/xmind&amp;quot;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;className&lt;/span&gt; &lt;span class="o"&gt;=?&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;XMind&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;            
    &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;winMask&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;                    &lt;span class="n"&gt;xK_F11&lt;/span&gt;      &lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;runOrRaise&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;./dbeaver/dbeaver&amp;quot;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;className&lt;/span&gt; &lt;span class="o"&gt;=?&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;DBeaver&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;        
    &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;winMask&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;                    &lt;span class="n"&gt;xK_F12&lt;/span&gt;      &lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;runOrRaise&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;VirtualBox&amp;quot;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;className&lt;/span&gt; &lt;span class="o"&gt;=?&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;VirtualBox&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;    
    &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;winMask&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;                    &lt;span class="n"&gt;xK_space&lt;/span&gt;    &lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;runOrRaisePrompt&lt;/span&gt; &lt;span class="n"&gt;mySP&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;winMask&lt;/span&gt; &lt;span class="o"&gt;.|.&lt;/span&gt; &lt;span class="n"&gt;shiftMask&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;      &lt;span class="n"&gt;xK_space&lt;/span&gt;    &lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;shellPrompt&lt;/span&gt; &lt;span class="n"&gt;mySP&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;winMask&lt;/span&gt; &lt;span class="o"&gt;.|.&lt;/span&gt; &lt;span class="n"&gt;controlMask&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;    &lt;span class="n"&gt;xK_space&lt;/span&gt;    &lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;windowPromptGoto&lt;/span&gt; &lt;span class="n"&gt;mySP&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;                          &lt;span class="n"&gt;xK_Print&lt;/span&gt;    &lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;unsafeSpawn&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;gnome-screenshot&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;altMask&lt;/span&gt; &lt;span class="o"&gt;.|.&lt;/span&gt; &lt;span class="n"&gt;controlMask&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;    &lt;span class="n"&gt;xK_Right&lt;/span&gt;    &lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;moveTo&lt;/span&gt; &lt;span class="kt"&gt;Next&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;WSIs&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;return&lt;/span&gt; &lt;span class="o"&gt;$&lt;/span&gt; &lt;span class="n"&gt;not&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;NSP&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt; &lt;span class="kt"&gt;W&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tag&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;  
    &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;altMask&lt;/span&gt; &lt;span class="o"&gt;.|.&lt;/span&gt; &lt;span class="n"&gt;controlMask&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;    &lt;span class="n"&gt;xK_Left&lt;/span&gt;     &lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;moveTo&lt;/span&gt; &lt;span class="kt"&gt;Prev&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;WSIs&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;return&lt;/span&gt; &lt;span class="o"&gt;$&lt;/span&gt; &lt;span class="n"&gt;not&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;NSP&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt; &lt;span class="kt"&gt;W&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tag&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
    &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;winMask&lt;/span&gt; &lt;span class="o"&gt;.|.&lt;/span&gt; &lt;span class="n"&gt;shiftMask&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;      &lt;span class="n"&gt;xK_Right&lt;/span&gt;    &lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;shiftTo&lt;/span&gt; &lt;span class="kt"&gt;Next&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;WSIs&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;return&lt;/span&gt; &lt;span class="o"&gt;$&lt;/span&gt; &lt;span class="n"&gt;not&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;NSP&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt; &lt;span class="kt"&gt;W&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tag&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
    &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;winMask&lt;/span&gt; &lt;span class="o"&gt;.|.&lt;/span&gt; &lt;span class="n"&gt;shiftMask&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;      &lt;span class="n"&gt;xK_Left&lt;/span&gt;     &lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;shiftTo&lt;/span&gt; &lt;span class="kt"&gt;Prev&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;WSIs&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;return&lt;/span&gt; &lt;span class="o"&gt;$&lt;/span&gt; &lt;span class="n"&gt;not&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;NSP&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt; &lt;span class="kt"&gt;W&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tag&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
    &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;winMask&lt;/span&gt; &lt;span class="o"&gt;.|.&lt;/span&gt; &lt;span class="n"&gt;controlMask&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;    &lt;span class="n"&gt;xK_Right&lt;/span&gt;    &lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;shiftTo&lt;/span&gt; &lt;span class="kt"&gt;Next&lt;/span&gt; &lt;span class="kt"&gt;EmptyWS&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;winMask&lt;/span&gt; &lt;span class="o"&gt;.|.&lt;/span&gt; &lt;span class="n"&gt;controlMask&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;    &lt;span class="n"&gt;xK_Left&lt;/span&gt;     &lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;shiftTo&lt;/span&gt; &lt;span class="kt"&gt;Prev&lt;/span&gt; &lt;span class="kt"&gt;EmptyWS&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;winMask&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;                    &lt;span class="n"&gt;xK_Tab&lt;/span&gt;      &lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;sendMessage&lt;/span&gt; &lt;span class="kt"&gt;NextLayout&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dynamicLogString&lt;/span&gt; &lt;span class="n"&gt;myPP&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;=&lt;/span&gt; &lt;span class="nf"&gt;\&lt;/span&gt;&lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="ow"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;safeSpawn&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;gnome-osd-client&amp;quot;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="p"&gt;]))&lt;/span&gt;
    &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;altMask&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;                    &lt;span class="n"&gt;xK_Tab&lt;/span&gt;      &lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;windows&lt;/span&gt; &lt;span class="kt"&gt;W&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;focusDown&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;altMask&lt;/span&gt; &lt;span class="o"&gt;.|.&lt;/span&gt; &lt;span class="n"&gt;shiftMask&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;      &lt;span class="n"&gt;xK_Tab&lt;/span&gt;      &lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;windows&lt;/span&gt; &lt;span class="kt"&gt;W&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;focusUp&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;controlMask&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;                &lt;span class="n"&gt;xK_Down&lt;/span&gt;     &lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;windows&lt;/span&gt; &lt;span class="kt"&gt;W&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;swapDown&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;controlMask&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;                &lt;span class="n"&gt;xK_Up&lt;/span&gt;       &lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;windows&lt;/span&gt; &lt;span class="kt"&gt;W&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;swapUp&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;winMask&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;                    &lt;span class="n"&gt;xK_Left&lt;/span&gt;     &lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;sendMessage&lt;/span&gt; &lt;span class="kt"&gt;Shrink&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;winMask&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;                    &lt;span class="n"&gt;xK_Right&lt;/span&gt;    &lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;sendMessage&lt;/span&gt; &lt;span class="kt"&gt;Expand&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;winMask&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;                    &lt;span class="n"&gt;xK_Down&lt;/span&gt;     &lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;sendMessage&lt;/span&gt; &lt;span class="kt"&gt;MirrorShrink&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;winMask&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;                    &lt;span class="n"&gt;xK_Up&lt;/span&gt;       &lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;sendMessage&lt;/span&gt; &lt;span class="kt"&gt;MirrorExpand&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;winMask&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;                    &lt;span class="n"&gt;xK_minus&lt;/span&gt;    &lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;windows&lt;/span&gt; &lt;span class="kt"&gt;W&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;shiftMaster&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;winMask&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;                    &lt;span class="n"&gt;xK_comma&lt;/span&gt;    &lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;sendMessage&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;IncMasterN&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;winMask&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;                    &lt;span class="n"&gt;xK_period&lt;/span&gt;   &lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;sendMessage&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;IncMasterN&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
    &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;winMask&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;                    &lt;span class="n"&gt;xK_n&lt;/span&gt;        &lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;refresh&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;winMask&lt;/span&gt; &lt;span class="o"&gt;.|.&lt;/span&gt; &lt;span class="n"&gt;shiftMask&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;      &lt;span class="n"&gt;xK_n&lt;/span&gt;        &lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;setLayout&lt;/span&gt; &lt;span class="o"&gt;$&lt;/span&gt; &lt;span class="kt"&gt;XMonad&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;layoutHook&lt;/span&gt; &lt;span class="n"&gt;conf&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;winMask&lt;/span&gt; &lt;span class="p"&gt;,&lt;/span&gt;                   &lt;span class="n"&gt;xK_a&lt;/span&gt;        &lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;sendMessage&lt;/span&gt; &lt;span class="kt"&gt;Taller&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;winMask&lt;/span&gt; &lt;span class="p"&gt;,&lt;/span&gt;                   &lt;span class="n"&gt;xK_z&lt;/span&gt;        &lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;sendMessage&lt;/span&gt; &lt;span class="kt"&gt;Wider&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;winMask&lt;/span&gt; &lt;span class="o"&gt;.|.&lt;/span&gt; &lt;span class="n"&gt;controlMask&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;    &lt;span class="n"&gt;xK_n&lt;/span&gt;        &lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;sendMessage&lt;/span&gt; &lt;span class="kt"&gt;Reset&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;winMask&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;                    &lt;span class="n"&gt;xK_m&lt;/span&gt;        &lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;sendMessage&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Toggle&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;Full&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dynamicLogString&lt;/span&gt; &lt;span class="n"&gt;myPP&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;=&lt;/span&gt; &lt;span class="nf"&gt;\&lt;/span&gt;&lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="ow"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;safeSpawn&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;gnome-osd-client&amp;quot;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="p"&gt;]))&lt;/span&gt;
    &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;winMask&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;                    &lt;span class="n"&gt;xK_t&lt;/span&gt;        &lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;withFocused&lt;/span&gt; &lt;span class="o"&gt;$&lt;/span&gt; &lt;span class="n"&gt;windows&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt; &lt;span class="kt"&gt;W&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sink&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;winMask&lt;/span&gt; &lt;span class="o"&gt;.|.&lt;/span&gt; &lt;span class="n"&gt;shiftMask&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;      &lt;span class="n"&gt;xK_t&lt;/span&gt;        &lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;sinkAll&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;    
    &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;winMask&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;                    &lt;span class="n"&gt;xK_f&lt;/span&gt;        &lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;sendMessage&lt;/span&gt; &lt;span class="kt"&gt;ToggleStruts&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;winMask&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;                    &lt;span class="n"&gt;xK_w&lt;/span&gt;        &lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;kill&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;altMask&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;                    &lt;span class="n"&gt;xK_F4&lt;/span&gt;       &lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;kill&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;winMask&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;                    &lt;span class="n"&gt;xK_q&lt;/span&gt;        &lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;broadcastMessage&lt;/span&gt; &lt;span class="kt"&gt;ReleaseResources&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;restart&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;xmonad&amp;quot;&lt;/span&gt; &lt;span class="kt"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;winMask&lt;/span&gt; &lt;span class="o"&gt;.|.&lt;/span&gt; &lt;span class="n"&gt;shiftMask&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;      &lt;span class="n"&gt;xK_q&lt;/span&gt;        &lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;spawn&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;gnome-session-quit&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;   
    &lt;span class="p"&gt;]&lt;/span&gt;

    &lt;span class="o"&gt;++&lt;/span&gt;
    &lt;span class="c1"&gt;-- mod-[1..9], Switch to workspace N&lt;/span&gt;
    &lt;span class="c1"&gt;-- mod-shift-[1..9], Move client to workspace N&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;m&lt;/span&gt; &lt;span class="o"&gt;.|.&lt;/span&gt; &lt;span class="n"&gt;winMask&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;windows&lt;/span&gt; &lt;span class="o"&gt;$&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;&amp;lt;-&lt;/span&gt; &lt;span class="n"&gt;zip&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;XMonad&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;workspaces&lt;/span&gt; &lt;span class="n"&gt;conf&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;xK_1&lt;/span&gt; &lt;span class="o"&gt;..&lt;/span&gt; &lt;span class="n"&gt;xK_9&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;&amp;lt;-&lt;/span&gt; &lt;span class="p"&gt;[(&lt;/span&gt;&lt;span class="kt"&gt;W&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;greedyView&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;W&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;shift&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;shiftMask&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt; &lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="c1"&gt;-- mouse bindings &lt;/span&gt;
&lt;span class="nf"&gt;myMouseBindings&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;XConfig&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="kt"&gt;XMonad&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;modMask&lt;/span&gt; &lt;span class="ow"&gt;=&lt;/span&gt; &lt;span class="n"&gt;modMask&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="ow"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;M&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fromList&lt;/span&gt; &lt;span class="o"&gt;$&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;winMask&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;button1&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;\&lt;/span&gt;&lt;span class="n"&gt;w&lt;/span&gt; &lt;span class="ow"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;focus&lt;/span&gt; &lt;span class="n"&gt;w&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;mouseMoveWindow&lt;/span&gt; &lt;span class="n"&gt;w&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;winMask&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;button2&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;\&lt;/span&gt;&lt;span class="n"&gt;w&lt;/span&gt; &lt;span class="ow"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;focus&lt;/span&gt; &lt;span class="n"&gt;w&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;windows&lt;/span&gt; &lt;span class="kt"&gt;W&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;shiftMaster&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;winMask&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;button3&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;\&lt;/span&gt;&lt;span class="n"&gt;w&lt;/span&gt; &lt;span class="ow"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;focus&lt;/span&gt; &lt;span class="n"&gt;w&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Flex&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mouseResizeWindow&lt;/span&gt; &lt;span class="n"&gt;w&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="c1"&gt;-- decoration theme&lt;/span&gt;
&lt;span class="nf"&gt;myDeco&lt;/span&gt; &lt;span class="ow"&gt;=&lt;/span&gt; &lt;span class="n"&gt;defaultTheme&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;activeColor&lt;/span&gt;           &lt;span class="ow"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;orange&amp;quot;&lt;/span&gt;
    &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;inactiveColor&lt;/span&gt;         &lt;span class="ow"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;#222222&amp;quot;&lt;/span&gt;
    &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;urgentColor&lt;/span&gt;           &lt;span class="ow"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;yellow&amp;quot;&lt;/span&gt;
    &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;activeBorderColor&lt;/span&gt;     &lt;span class="ow"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;orange&amp;quot;&lt;/span&gt;
    &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;inactiveBorderColor&lt;/span&gt;   &lt;span class="ow"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;#222222&amp;quot;&lt;/span&gt;
    &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;urgentBorderColor&lt;/span&gt;     &lt;span class="ow"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;yellow&amp;quot;&lt;/span&gt;
    &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;activeTextColor&lt;/span&gt;       &lt;span class="ow"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;orange&amp;quot;&lt;/span&gt;
    &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;inactiveTextColor&lt;/span&gt;     &lt;span class="ow"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;#222222&amp;quot;&lt;/span&gt;
    &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;urgentTextColor&lt;/span&gt;       &lt;span class="ow"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;yellow&amp;quot;&lt;/span&gt;
    &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;decoHeight&lt;/span&gt;            &lt;span class="ow"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;-- tab theme&lt;/span&gt;
&lt;span class="nf"&gt;myTab&lt;/span&gt; &lt;span class="ow"&gt;=&lt;/span&gt; &lt;span class="n"&gt;defaultTheme&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;activeColor&lt;/span&gt;           &lt;span class="ow"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;black&amp;quot;&lt;/span&gt;
    &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;inactiveColor&lt;/span&gt;         &lt;span class="ow"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;black&amp;quot;&lt;/span&gt;
    &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;urgentColor&lt;/span&gt;           &lt;span class="ow"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;yellow&amp;quot;&lt;/span&gt;
    &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;activeBorderColor&lt;/span&gt;     &lt;span class="ow"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;orange&amp;quot;&lt;/span&gt;
    &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;inactiveBorderColor&lt;/span&gt;   &lt;span class="ow"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;#222222&amp;quot;&lt;/span&gt;
    &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;urgentBorderColor&lt;/span&gt;     &lt;span class="ow"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;black&amp;quot;&lt;/span&gt;
    &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;activeTextColor&lt;/span&gt;       &lt;span class="ow"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;orange&amp;quot;&lt;/span&gt;
    &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;inactiveTextColor&lt;/span&gt;     &lt;span class="ow"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;#222222&amp;quot;&lt;/span&gt;
    &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;urgentTextColor&lt;/span&gt;       &lt;span class="ow"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;yellow&amp;quot;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;-- shell prompt theme&lt;/span&gt;
&lt;span class="nf"&gt;mySP&lt;/span&gt; &lt;span class="ow"&gt;=&lt;/span&gt; &lt;span class="n"&gt;defaultXPConfig&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;bgColor&lt;/span&gt;               &lt;span class="ow"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;black&amp;quot;&lt;/span&gt;
    &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;fgColor&lt;/span&gt;               &lt;span class="ow"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;white&amp;quot;&lt;/span&gt;
    &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;bgHLight&lt;/span&gt;              &lt;span class="ow"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;gray&amp;quot;&lt;/span&gt;
    &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;fgHLight&lt;/span&gt;              &lt;span class="ow"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;black&amp;quot;&lt;/span&gt;
    &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;borderColor&lt;/span&gt;           &lt;span class="ow"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;orange&amp;quot;&lt;/span&gt;
    &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;promptBorderWidth&lt;/span&gt;     &lt;span class="ow"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;
    &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;position&lt;/span&gt;              &lt;span class="ow"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;Bottom&lt;/span&gt;
    &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;height&lt;/span&gt;                &lt;span class="ow"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;40&lt;/span&gt;
    &lt;span class="c1"&gt;--, autoComplete        = Just 1000&lt;/span&gt;
    &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;historySize&lt;/span&gt;           &lt;span class="ow"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;-- dynamicLog theme (suppress everything but layout)&lt;/span&gt;
&lt;span class="nf"&gt;myPP&lt;/span&gt; &lt;span class="ow"&gt;=&lt;/span&gt; &lt;span class="n"&gt;defaultPP&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;ppLayout&lt;/span&gt;  &lt;span class="ow"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;\&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="ow"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kr"&gt;case&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="kr"&gt;of&lt;/span&gt;
      &lt;span class="s"&gt;&amp;quot;Hinted ResizableTall&amp;quot;&lt;/span&gt;        &lt;span class="ow"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;[|]&amp;quot;&lt;/span&gt;
      &lt;span class="s"&gt;&amp;quot;Mirror Hinted ResizableTall&amp;quot;&lt;/span&gt; &lt;span class="ow"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;[-]&amp;quot;&lt;/span&gt;
      &lt;span class="s"&gt;&amp;quot;Hinted Tabbed Simplest&amp;quot;&lt;/span&gt;      &lt;span class="ow"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;[T]&amp;quot;&lt;/span&gt;
      &lt;span class="s"&gt;&amp;quot;Mosaic&amp;quot;&lt;/span&gt;                      &lt;span class="ow"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;[M]&amp;quot;&lt;/span&gt;
      &lt;span class="s"&gt;&amp;quot;Full&amp;quot;&lt;/span&gt;                 &lt;span class="ow"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;[ ]&amp;quot;&lt;/span&gt;
      &lt;span class="kr"&gt;_&lt;/span&gt;                      &lt;span class="ow"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ppCurrent&lt;/span&gt;             &lt;span class="ow"&gt;=&lt;/span&gt; &lt;span class="n"&gt;const&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;&amp;quot;&lt;/span&gt;
    &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ppVisible&lt;/span&gt;             &lt;span class="ow"&gt;=&lt;/span&gt; &lt;span class="n"&gt;const&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;&amp;quot;&lt;/span&gt;
    &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ppHidden&lt;/span&gt;              &lt;span class="ow"&gt;=&lt;/span&gt; &lt;span class="n"&gt;const&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;&amp;quot;&lt;/span&gt;
    &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ppHiddenNoWindows&lt;/span&gt;     &lt;span class="ow"&gt;=&lt;/span&gt; &lt;span class="n"&gt;const&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;&amp;quot;&lt;/span&gt;
    &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ppUrgent&lt;/span&gt;              &lt;span class="ow"&gt;=&lt;/span&gt; &lt;span class="n"&gt;const&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;&amp;quot;&lt;/span&gt;
    &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ppTitle&lt;/span&gt;               &lt;span class="ow"&gt;=&lt;/span&gt; &lt;span class="n"&gt;const&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;&amp;quot;&lt;/span&gt;
    &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ppWsSep&lt;/span&gt;               &lt;span class="ow"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;&amp;quot;&lt;/span&gt;
    &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ppSep&lt;/span&gt;                 &lt;span class="ow"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;&amp;quot;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;-- layouts&lt;/span&gt;
&lt;span class="nf"&gt;myLayout&lt;/span&gt; &lt;span class="ow"&gt;=&lt;/span&gt; &lt;span class="n"&gt;avoidStruts&lt;/span&gt; &lt;span class="o"&gt;$&lt;/span&gt; &lt;span class="n"&gt;toggleLayouts&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;noBorders&lt;/span&gt; &lt;span class="kt"&gt;Full&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;smartBorders&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tiled&lt;/span&gt; &lt;span class="o"&gt;|||&lt;/span&gt; &lt;span class="n"&gt;mosaic&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;|||&lt;/span&gt; &lt;span class="kt"&gt;Mirror&lt;/span&gt; &lt;span class="n"&gt;tiled&lt;/span&gt; &lt;span class="o"&gt;|||&lt;/span&gt; &lt;span class="n"&gt;layoutHints&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tabbed&lt;/span&gt; &lt;span class="n"&gt;shrinkText&lt;/span&gt; &lt;span class="n"&gt;myTab&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
    &lt;span class="kr"&gt;where&lt;/span&gt;
        &lt;span class="n"&gt;tiled&lt;/span&gt;   &lt;span class="ow"&gt;=&lt;/span&gt; &lt;span class="n"&gt;layoutHints&lt;/span&gt; &lt;span class="o"&gt;$&lt;/span&gt; &lt;span class="kt"&gt;ResizableTall&lt;/span&gt; &lt;span class="n"&gt;nmaster&lt;/span&gt; &lt;span class="n"&gt;delta&lt;/span&gt; &lt;span class="n"&gt;ratio&lt;/span&gt; &lt;span class="kt"&gt;[]&lt;/span&gt;
        &lt;span class="n"&gt;nmaster&lt;/span&gt; &lt;span class="ow"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
        &lt;span class="n"&gt;delta&lt;/span&gt;   &lt;span class="ow"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;
        &lt;span class="n"&gt;ratio&lt;/span&gt;   &lt;span class="ow"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;

&lt;span class="c1"&gt;-- special windows&lt;/span&gt;
&lt;span class="nf"&gt;myManageHook&lt;/span&gt; &lt;span class="ow"&gt;=&lt;/span&gt; &lt;span class="n"&gt;composeAll&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="n"&gt;className&lt;/span&gt; &lt;span class="o"&gt;=?&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;MPlayer&amp;quot;&lt;/span&gt;                &lt;span class="o"&gt;--&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;doFloat&lt;/span&gt;
    &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;className&lt;/span&gt; &lt;span class="o"&gt;=?&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;Gimp-2.6&amp;quot;&lt;/span&gt;               &lt;span class="o"&gt;--&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;doFloat&lt;/span&gt;
    &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;className&lt;/span&gt; &lt;span class="o"&gt;=?&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;Gnome-panel&amp;quot;&lt;/span&gt;            &lt;span class="o"&gt;--&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;doIgnore&lt;/span&gt;
    &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;className&lt;/span&gt; &lt;span class="o"&gt;=?&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;XVkbd&amp;quot;&lt;/span&gt;                  &lt;span class="o"&gt;--&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;doIgnore&lt;/span&gt;
    &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;className&lt;/span&gt; &lt;span class="o"&gt;=?&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;Cellwriter&amp;quot;&lt;/span&gt;             &lt;span class="o"&gt;--&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;doIgnore&lt;/span&gt;
    &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;className&lt;/span&gt; &lt;span class="o"&gt;=?&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;Gtkdialog&amp;quot;&lt;/span&gt;              &lt;span class="o"&gt;--&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;doFloat&lt;/span&gt;
    &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;resource&lt;/span&gt;  &lt;span class="o"&gt;=?&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;desktop_window&amp;quot;&lt;/span&gt;         &lt;span class="o"&gt;--&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;doIgnore&lt;/span&gt;
    &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;className&lt;/span&gt;  &lt;span class="o"&gt;=?&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;Xmessage&amp;quot;&lt;/span&gt;              &lt;span class="o"&gt;--&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;doCenterFloat&lt;/span&gt;
    &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;className&lt;/span&gt; &lt;span class="o"&gt;=?&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;Unity-2d-panel&amp;quot;&lt;/span&gt;         &lt;span class="o"&gt;--&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;doIgnore&lt;/span&gt;
    &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;isFullscreen&lt;/span&gt;                          &lt;span class="o"&gt;--&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;doFullFloat&lt;/span&gt;
    &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;isDialog&lt;/span&gt;                              &lt;span class="o"&gt;--&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;doCenterFloat&lt;/span&gt;
    &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;title&lt;/span&gt; &lt;span class="o"&gt;=?&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;RSSOwl&amp;quot;&lt;/span&gt;                     &lt;span class="o"&gt;--&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;doIgnore&lt;/span&gt;
    &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;title&lt;/span&gt; &lt;span class="o"&gt;=?&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;Dbeaver&amp;quot;&lt;/span&gt;                    &lt;span class="o"&gt;--&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;doIgnore&lt;/span&gt;
    &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;title&lt;/span&gt; &lt;span class="o"&gt;=?&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;Xmind-bin&amp;quot;&lt;/span&gt;                  &lt;span class="o"&gt;--&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;doIgnore&lt;/span&gt;
    &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;title&lt;/span&gt; &lt;span class="o"&gt;=?&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;AptanaStudio3&amp;quot;&lt;/span&gt;              &lt;span class="o"&gt;--&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;doIgnore&lt;/span&gt;
    &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;resource&lt;/span&gt; &lt;span class="o"&gt;=?&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;sun-awt-X11-XDialogPeer&amp;quot;&lt;/span&gt; &lt;span class="o"&gt;--&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;doCenterFloat&lt;/span&gt;
    &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;title&lt;/span&gt; &lt;span class="o"&gt;=?&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;screenkey&amp;quot;&lt;/span&gt;                  &lt;span class="o"&gt;--&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;doIgnore&lt;/span&gt;
    &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;className&lt;/span&gt; &lt;span class="o"&gt;=?&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;Gloobus-preview&amp;quot;&lt;/span&gt;        &lt;span class="o"&gt;--&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;doCenterFloat&lt;/span&gt;
    &lt;span class="c1"&gt;--                                      x y w h&lt;/span&gt;
    &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;scratchpadManageHook&lt;/span&gt; &lt;span class="o"&gt;$&lt;/span&gt; &lt;span class="kt"&gt;W&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;RationalRect&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="mf"&gt;0.42&lt;/span&gt;
    &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;manageDocks&lt;/span&gt; &lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;+&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;manageHook&lt;/span&gt; &lt;span class="n"&gt;defaultConfig&lt;/span&gt;

&lt;span class="c1"&gt;-- let Gnome know about Xmonad actions&lt;/span&gt;
&lt;span class="nf"&gt;myLogHook&lt;/span&gt; &lt;span class="ow"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ewmhDesktopsLogHookCustom&lt;/span&gt; &lt;span class="n"&gt;scratchpadFilterOutWorkspace&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;updatePointer&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Relative&lt;/span&gt; &lt;span class="mf"&gt;0.5&lt;/span&gt; &lt;span class="mf"&gt;0.5&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nf"&gt;myConfig&lt;/span&gt; &lt;span class="ow"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ewmh&lt;/span&gt; &lt;span class="n"&gt;defaultConfig&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;terminal&lt;/span&gt;           &lt;span class="ow"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;terminator&amp;quot;&lt;/span&gt;
    &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;borderWidth&lt;/span&gt;        &lt;span class="ow"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;
    &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;normalBorderColor&lt;/span&gt;  &lt;span class="ow"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;black&amp;quot;&lt;/span&gt;
    &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;focusedBorderColor&lt;/span&gt; &lt;span class="ow"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;orange&amp;quot;&lt;/span&gt;
    &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;focusFollowsMouse&lt;/span&gt;  &lt;span class="ow"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;True&lt;/span&gt;
    &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;modMask&lt;/span&gt;            &lt;span class="ow"&gt;=&lt;/span&gt; &lt;span class="n"&gt;mod4Mask&lt;/span&gt;
    &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;keys&lt;/span&gt;               &lt;span class="ow"&gt;=&lt;/span&gt; &lt;span class="n"&gt;myKeys&lt;/span&gt;
    &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;mouseBindings&lt;/span&gt;      &lt;span class="ow"&gt;=&lt;/span&gt; &lt;span class="n"&gt;myMouseBindings&lt;/span&gt;
    &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;layoutHook&lt;/span&gt;         &lt;span class="ow"&gt;=&lt;/span&gt; &lt;span class="n"&gt;myLayout&lt;/span&gt;
    &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;manageHook&lt;/span&gt;         &lt;span class="ow"&gt;=&lt;/span&gt; &lt;span class="n"&gt;myManageHook&lt;/span&gt;
    &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;startupHook&lt;/span&gt;        &lt;span class="ow"&gt;=&lt;/span&gt; &lt;span class="n"&gt;gnomeRegister&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;-- need to override ewmh&amp;#39;s logHook cause I&amp;#39;m using Scratchpad&lt;/span&gt;
&lt;span class="nf"&gt;main&lt;/span&gt; &lt;span class="ow"&gt;=&lt;/span&gt; &lt;span class="n"&gt;xmonad&lt;/span&gt; &lt;span class="o"&gt;$&lt;/span&gt; &lt;span class="n"&gt;myConfig&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;logHook&lt;/span&gt;            &lt;span class="ow"&gt;=&lt;/span&gt; &lt;span class="n"&gt;myLogHook&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;</summary><category term="linux"></category><category term="ubuntu"></category><category term="unity"></category><category term="tiling"></category><category term="twm"></category><category term="productividad"></category><category term="xmonad"></category><category term="gnome"></category><category term="haskell"></category></entry><entry><title>Productividad en el escritorio Linux: Tiling</title><link href="http://joedicastro.com/productividad-en-el-escritorio-linux-tiling.html" rel="alternate"></link><updated>2011-11-25T00:00:00+01:00</updated><author><name>joe di castro</name></author><id>http://joedicastro.com/productividad-en-el-escritorio-linux-tiling.html</id><summary type="html">&lt;p&gt;Los &lt;strong&gt;Gestores de Ventanas de Mosaico&lt;/strong&gt; (&lt;a href="http://en.wikipedia.org/wiki/Tiling_window_manager"&gt;Tiling Windows Manager&lt;/a&gt;) son el 
gran desconocido entre la mayoría de usuarios de Linux. Y sin embargo, a mi 
juicio, suponen el salto más importante en productividad en el escritorio linux 
que se haya producido en mucho tiempo. Permiten manejar todo el escritorio sin 
apenas despegar las manos del teclado y sin verse relegado a emplear únicamente 
la consola. Las ventanas se distribuyen ellas solas de forma automática por la 
pantalla y no es necesario emplear el ratón para moverlas y redimensionarlas 
(aunque puede seguir empleándose, naturalmente).&lt;/p&gt;
&lt;p&gt;A diferencia de un gestor de ventanas tradicional, a los que estamos normalmente 
acostumbrados, las ventanas no se apilan y flotan unas encima de otras, si no 
que se redistribuyen por toda la pantalla de acorde a un esquema previo (variable) 
sin solaparse entre ellas, quedando todas a la vista de forma simultanea. De ahí 
el símil del mosaico, donde el mismo seria el escritorio y donde las ventanas 
serian las teselas que lo componen. Puede no ser fácil visualizar de entrada en 
que consiste exactamente esto y como funciona, luego los vídeos nos ayudarán a 
entender mejor de lo que estoy hablando. Aunque uno no aprecia realmente las 
ventajas que aportan hasta que prueba uno. &lt;/p&gt;
&lt;h2 id="+que_ventajas_tienen"&gt;¿Que ventajas tienen?&lt;/h2&gt;
&lt;p&gt;Primero mencionar que esto no es una idea nueva, ni mucho menos, esto ya lo 
inventó en 1981 &lt;strong&gt;Xerox&lt;/strong&gt; (el inventor a su vez de los entornos de ventanas) e 
incluso MS Windows 1.0 lo empleaba. La razón de que algo desarrollado desde hace
dos décadas aún no haya llamado demasiado la atención hasta ahora, es bien 
sencilla, el triunfo de un dispositivo de entrada más amigable e intuitivo que 
el teclado: el ratón. La metáfora de el entorno de ventanas con el escritorio 
junto con la del ratón como la extensión de nuestro brazo, propiciaba que fuera 
lo más natural emplearlo para mover las ventanas y redimensionarlas. Pero que 
sea más fácil o intuitivo no quiere decir que sea más eficiente, nadie en su sano 
juicio escribiría un documento empleando el ratón sobre un teclado virtual ni 
realizaría retoque fotográfico empleando únicamente el teclado. ¿Pero cuantos 
dirían que el teclado es el medio más eficiente para manejar las ventanas?&lt;/p&gt;
&lt;p&gt;En una pantalla de grandes dimensiones y elevada resolución (e.g. un 
monitor de 24" y 1920x1200) es muy frecuente andar redimensionando continuamente 
las ventanas para acomodarlas y redistribuirlas dentro del escritorio. Nos sobra 
espacio por todos los lados y la mayoría de las aplicaciones que en monitores y 
resoluciones menores empleábamos maximizadas, ahora son incomodas de usar a 
pantalla completa (e.g. navegador web). ¿No sería útil que las ventanas se 
recolocaran y redimensionaran automáticamente por si solas? Más o menos esto es 
lo que viene a hacer un gestor de ventanas de mosaico. &lt;/p&gt;
&lt;p&gt;Pero cuales son las ventajas reales de este sistema:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Las ventanas se organizan solas&lt;/strong&gt;, muchas veces no necesitas mover o 
redimensionar una ventana, simplemente aparece en un lugar adecuado y con las 
dimensiones adecuadas. Una vez le que coges el truco te organizas de manera que 
esto ocurre con cierta naturalidad.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Se aprovecha todo el espacio de la pantalla&lt;/strong&gt;. Las ventanas se ajustan a todo 
el espacio libre disponible del escritorio. Aún así pueden emplearse paneles, 
docks, ...&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Las ventanas normalmente apenas tienen marco y carecen de barra de titulo y 
decoraciones, por lo tanto se ahorra espacio. Combinado con un menú integrado 
como el &lt;em&gt;Global Menu&lt;/em&gt; de Unity, &lt;strong&gt;se ahorra mucho espacio&lt;/strong&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;No es un método excluyente&lt;/strong&gt;, aún se pueden emplear con toda normalidad las 
ventanas flotantes como habitualmente. Incluso pueden convivir entre ellas sin 
problema alguno.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;En un sistema de múltiples monitores se facilita mucho el manejo de las 
múltiples ventanas&lt;/strong&gt; y el tener simultáneamente abiertas un número elevado de 
ellas es algo realmente sencillo. &lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Es posible automatizar el tamaño, posición y escritorio virtual en donde se 
situara la ventana(s) de una aplicación al iniciarse&lt;/strong&gt;, lo que abre un mundo de 
posibilidades. &lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Es posible tener múltiples escritorios virtuales (usualmente 9 por monitor) 
con características distintas en cada uno&lt;/strong&gt;. Puede ser un esquema distinto 
(desde una simple rejilla a un escritorio normal) o un escritorio para cada tarea 
(e.g. 1 - navegar, 2 - correo y redes sociales, 3 - trabajo, 4 - terminales, ...)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Mover las ventanas, redimensionarlas, lanzar aplicaciones, cerrarlas, ... y 
manejar los escritorios virtuales desde el teclado&lt;/strong&gt; es mucho más eficiente que 
emplear el ratón para lo mismo.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Te olvidas de buscar una ventana entre la pila de ventanas flotantes&lt;/strong&gt; sobre 
el escritorio, es sencillo incluso encontrar una ventana dentro de otro 
escritorio virtual (con unos gestores más que otros). Los escritorios modernos 
permiten mostrar un mosaico con todas las aplicaciones abiertas (Exposé, Scale, 
...), ¿pero que ocurre cuando tienes abiertas cerca de 30? con un twm la localizas enseguida si lo configuras adecuadamente, en mi caso menos de 2 segundos.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Te permite organizar las ventanas en función de múltiples patrones distintos:&lt;/strong&gt; 
rejillas, columnas, filas, espirales, por pestañas, etc... de forma automática, 
acomodándose a múltiples situaciones distintas.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Se pueden emplear de forma autónoma, sin un escritorio detrás&lt;/strong&gt; (gnome, kde, 
...) lo que permite tener un sistema muy ligero ideal para equipos poco potentes. &lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Puede ser una manera de mejorar la accesibilidad del escritorio&lt;/strong&gt; para 
aquellos que encuentren dificultades para manejar un ratón pero puedan defenderse 
con un teclado.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="inconvenientes"&gt;Inconvenientes&lt;/h2&gt;
&lt;p&gt;No son para todo el mundo. Soy plenamente consciente de ello, una buena mayoría 
de los usuarios son reticentes a aprenderse los atajos de teclado de una 
aplicación (hay quien no sabe cortar y pegar desde él), así que entiendo 
perfectamente que siquiera lleguen a plantearse el emplearlos. De hecho los 
tímidos avances que están realizando los escritorios en este sentido implican el 
uso del ratón para implantar un sucedáneo de este sistema. También es una 
cuestión de gustos, habrá a quien simplemente la desagradará el como funciona 
este sistema.&lt;/p&gt;
&lt;p&gt;Por otro lado los profesionales multimedia que requieren un uso elevado del ratón 
probablemente prefieran seguir empleando un gestor de ventanas tradicional, 
aunque los gestores de mosaico pueden moverse con soltura en este entorno. &lt;/p&gt;
&lt;p&gt;También es cierto que la mayoría de ellos requieren un esfuerzo por nuestra 
parte para configurarlos a nuestro gusto. Aunque es precisamente esto lo que nos 
permite configurarlos exactamente a nuestro gusto, con muchas posibilidades de 
configuración. Pero es una barrera de entrada importante para los gestores más 
potentes y completos. Alguno hasta requiere conocimientos en algún lenguaje de 
programación (C, Lua, Haskell)&lt;/p&gt;
&lt;p&gt;Aunque por un lado pueden suponer una ventaja en la accesibilidad a determinados 
discapacitados, pueden suponer un escollo para otros, pues no están tan adaptados 
como puedan estarlo los escritorios tradicionales para determinadas 
discapacidades.&lt;/p&gt;
&lt;h2 id="tipos_de_gestores"&gt;Tipos de gestores&lt;/h2&gt;
&lt;p&gt;Básicamente existen dos tipos de gestores de ventanas de mosaico, en función de 
el modo de organización de las ventanas. &lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;&lt;em&gt;Manuales&lt;/em&gt;&lt;/strong&gt;, requieren que el usuario sea quien decida la posición (a veces 
el tamaño) de las ventanas, con el inconveniente de la perdida de tiempo a costa 
de lograr un control más preciso.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;&lt;em&gt;Automáticos&lt;/em&gt;&lt;/strong&gt;, ajustan automáticamente la posición y el tamaño de las 
ventanas en función del esquema por defecto. Posteriormente podemos variar tanto 
el esquema como la posición y tamaño de las ventanas individuales.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="adaptaci+n_de_la_idea_en_los_escritorios_m+s_comunes"&gt;Adaptación de la idea en los escritorios más comunes&lt;/h2&gt;
&lt;p&gt;Tal y como ocurre con los lanzadores de aplicaciones, los escritorios más 
tradiciones de linux comienzan a incorporar esta idea a sus desarrollos, en 
mayor o menor medida. Unity, Compiz, Gnome y KDE se han atrevido a dar un paso 
en esta dirección en sus últimas versiones:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Compiz Grid&lt;/strong&gt;, es un plugin de Compiz que permite realizar un tiling 
primitivo, empleando el ratón o el teclado. Divide la pantalla en una rejilla de 
3 x 3 y permite que las ventanas se ajusten al tamaño de una cuadricula o varias. 
Manejarlo desde el teclado es muy sencillo, simplemente pulsamos Ctrl + Alt + una 
tecla del teclado numérico del 1 al 9 según la cuadricula a la que la queramos 
ajustar la ventana. Para cambiar el tamaño de la ventana mantenemos pulsadas las 
teclas Ctrl + Alt y volvemos a pulsar la tecla numérica las veces que sea 
necesario hasta tener el tamaño que deseemos. Es decir, si pulsamos Ctrl+Alt+7 
nos sitúa la ventana en la parte superior izquierda de la pantalla ocupando 1/9 
de la misma. &lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Unity&lt;/strong&gt;, emplea el plugin Grid de compiz anterior, pero modificado desde la 
versión 11.10 para que se comporte como ellos desean. En este vídeo se puede ver 
como se comporta este plugin manejándolo desde el teclado. Personalmente opino 
que mejor lo hubieran dejado como estaba.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;div style="text-align:center"&gt;
&lt;iframe src="http://player.vimeo.com/video/32746967?title=0&amp;amp;byline=0&amp;amp;portrait=0&amp;amp;color=59a5d1" width="667" height="417"&gt;&lt;/iframe&gt;&lt;p&gt;Demostración del tiling básico del plugin de Compiz, Grid, en Ubuntu 11.10 con Unity&lt;/p&gt;
&lt;/div&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Gnome Shell&lt;/strong&gt;, tiene un tiling muy básico parecido al que emplea Unity. Te 
permite maximizar una ventana o ajustarla a la mitad derecha/izquierda de la 
pantalla simplemente arrastrándola al borde correspondiente.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;KDE&lt;/strong&gt; a partir de la versión 4.4 tiene un tiling bastante conseguido, aunque 
no tan avanzado como los gestores dedicados. Desde luego es el escritorio que ha 
apostado más fuerte por este sistema y el que mejores resultados obtiene. Aunque 
no viene activarlo por defecto, es necesario activarlo a mano. De momento solo 
permite elegir entre dos esquemas predeterminados: columnas y espiral (fibonacci).&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="aplicaciones_que_funcionan_como_un_gestor"&gt;Aplicaciones que funcionan como un gestor&lt;/h3&gt;
&lt;p&gt;Para aquellos que no quieran instalar un gestor o no empleen uno de los entornos 
de escritorio anteriores, siempre existe la posibilidad de emplear alguna de 
estas aplicaciones que emulan el comportamiento básico de un gestor de ventanas
de mosaico. Ambos son del tipo manual.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="http://ssokolow.github.com/quicktile/"&gt;QuickTile&lt;/a&gt;, es un script en python que ofrece las mismas funcionalidades 
que el plugin Grid de Compiz. &lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="http://www.giuspen.com/x-tile/"&gt;x tile&lt;/a&gt;, es una aplicación instalable (viene en los repositorios de 
algunas distros) que ofrece más opciones que la anterior. &lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="gestores_m+s_populares"&gt;Gestores más populares&lt;/h2&gt;
&lt;p&gt;Hay muchos y para todos los gustos. Incluso los hay disponibles para otros SOs 
como Mac OS X o Windows (es muy famoso el WinSplit-Revolution para Windows, en 
el que se basa el plugin Grid, aunque los primeros gestores para linux son del 
2000 y WinSplit aparece en el 2007). Me centrare aquí solo en los más populares.&lt;/p&gt;
&lt;h3 id="awesomeaw"&gt;&lt;a href="http://awesome.naquadah.org/"&gt;awesome&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Automático. Escrito en &lt;em&gt;C&lt;/em&gt; y &lt;em&gt;Lua&lt;/em&gt;, es configurable y extensible en &lt;em&gt;Lua&lt;/em&gt;. Basado 
en &lt;strong&gt;dwm&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Uno de los dos grandes, junto con &lt;strong&gt;Xmonad&lt;/strong&gt;. Es quizá el que más opciones 
presenta hoy en día. Está basado en XCB en vez de Xlib, lo que le proporciona 
mayor agilidad. Aporta su propia barra de información, bandeja del sistema 
(systray) y lanzador de aplicaciones integrado. Tiene un soporte de ratón 
superior a la competencia. Pero su desarrollo no es aún solido, varía demasiado a 
lo largo de las distintas versiones y te obliga a reconfigurarlo cada vez que 
sale una nueva.&lt;/p&gt;
&lt;br /&gt;

&lt;h3 id="bluetilebt"&gt;&lt;a href="http://bluetile.org/"&gt;bluetile&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Automático. Escrito en &lt;em&gt;Haskell&lt;/em&gt;. Basado en &lt;strong&gt;Xmonad&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Es uno de los más sencillos de utilizar y de los más sencillos de probar. Es el 
más adecuado para probar por primera vez para los que se inician en esto. Está 
pensado para emplear conjuntamente con Gnome y pensando para que los están 
acostumbrados al ratón lo usen indistintamente con el teclado. En el siguiente 
vídeo realizado por su autor, Jan Vornberger, se puede ver como funciona:&lt;/p&gt;
&lt;div style="text-align:center"&gt;
&lt;iframe src="http://player.vimeo.com/video/6661713?title=0&amp;amp;byline=0&amp;amp;portrait=0" width="667" height="500"&gt;&lt;/iframe&gt;
&lt;/div&gt;

&lt;br /&gt;

&lt;h3 id="dwmdwm"&gt;&lt;a href="http://dwm.suckless.org/"&gt;dwm&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Automático. Escrito, configurable y extensible en &lt;em&gt;C&lt;/em&gt;. &lt;/p&gt;
&lt;p&gt;Uno de los más ligeros, rápidos y simples. Su configuración se hace modificando 
su código fuente en &lt;em&gt;C&lt;/em&gt; y luego hay que recompilarlo y reiniciarlo. Esto lo 
limita a usuarios avanzados con conocimientos de programación. Su diseño 
minimalista sido la inspiración para los dos más grandes: Awesome y Xmonad. &lt;/p&gt;
&lt;p&gt;Para lanzar las aplicaciones se desarrolló &lt;strong&gt;dmenu&lt;/strong&gt; un lanzador basado en texto, 
que es muy empleado en otros gestores. Funciona al igual que los lanzadores 
gráficos como Gnome Do. &lt;/p&gt;
&lt;br /&gt;

&lt;h3 id="i3i3"&gt;&lt;a href="http://i3wm.org/"&gt;i3&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Manual. Escrito en &lt;em&gt;C&lt;/em&gt; y configurable con ficheros de texto. Basado en &lt;strong&gt;wmii&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Muy sencillo y que presume de estar bien documentado. Fácil de usar y configurar. 
Es manual, lo que le permite crear esquemas muy flexibles y personalizados, pero 
que por otro lado le resta comodidad. &lt;/p&gt;
&lt;br /&gt;

&lt;h3 id="wmiiwmii"&gt;&lt;a href="http://wmii.suckless.org/"&gt;wmii&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Manual. Escrito en &lt;em&gt;C&lt;/em&gt; y configurable en &lt;em&gt;bash&lt;/em&gt;, &lt;em&gt;rc&lt;/em&gt;, &lt;em&gt;ruby&lt;/em&gt; o &lt;em&gt;python&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Escrito por el mismo desarrollador que &lt;strong&gt;dwm&lt;/strong&gt;. Digamos que es el gestor 
equivalente a &lt;strong&gt;dwm&lt;/strong&gt; pero manual. &lt;/p&gt;
&lt;br /&gt;

&lt;h3 id="xmonadxmnd"&gt;&lt;a href="http://xmonad.org/"&gt;xmonad&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Automático. Escrito, extensible y configurable en &lt;em&gt;Haskell&lt;/em&gt;. Inspirado en &lt;strong&gt;dwm&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Muy potente, estable y con muchas opciones. Es altamente personalizable, con 
muchas extensiones disponibles. Se integra muy bien con Gnome y KDE. Tiene un 
desarrollo muy activo y una gran comunidad de usuarios detrás. Es el que empleo 
desde hace meses y le he dedicado un &lt;a href="http://joedicastro.com/productividad-en-el-escritorio-linux-xmonad.html"&gt;articulo completo&lt;/a&gt;.&lt;/p&gt;
&lt;br /&gt;

&lt;p&gt;Todos estos gestores están incluidos en los repositorios de Ubuntu, Debian, 
Gentoo y Arch Linux, y la mayoría están disponibles también en otras 
distribuciones. &lt;/p&gt;
&lt;p&gt;Como curiosidad, mencionar otros dos, que aunque no son muy populares, están desarrollados en Python: &lt;a href="http://sourceforge.net/projects/tritium/"&gt;tritium&lt;/a&gt; y &lt;a href="http://qtile.org/"&gt;qtile&lt;/a&gt;&lt;/p&gt;</summary><category term="linux"></category><category term="ubuntu"></category><category term="unity"></category><category term="tiling"></category><category term="twm"></category><category term="productividad"></category><category term="xmonad"></category></entry><entry><title>Productividad en el escritorio Linux: Introducción</title><link href="http://joedicastro.com/productividad-en-el-escritorio-linux-introduccion.html" rel="alternate"></link><updated>2011-11-24T00:00:00+01:00</updated><author><name>joe di castro</name></author><id>http://joedicastro.com/productividad-en-el-escritorio-linux-introduccion.html</id><summary type="html">&lt;p&gt;En el último lustro se ha producido una renovación en el &lt;a href="http://es.wikipedia.org/wiki/Escritorio_Linux"&gt;escritorio Linux&lt;/a&gt; buscando innovar, mejorar su funcionamiento, equiparlo a las evoluciones de 
otros SOs (Mac OS X) y porque no, intentar acercar a más usuarios al mundo linux. &lt;/p&gt;
&lt;p&gt;Quizá la primera innovación importante fue el gestor de ventanas &lt;a href="http://es.wikipedia.org/wiki/Compiz"&gt;Compiz&lt;/a&gt; 
en el 2006 que tuvo una elevada repercusión en su momento por lo novedoso y lo 
espectacular de sus efectos 3D empleando OpenGL. También ese mismo año hacia su 
aparición &lt;a href="http://es.wikipedia.org/wiki/LXDE"&gt;LXDE&lt;/a&gt;, un entorno de escritorio ligero y rápido que supone un 
paso adelante dentro de las opciones poco ávidas de recursos. Luego dos años más 
tarde, &lt;a href="http://es.wikipedia.org/wiki/KDE"&gt;KDE&lt;/a&gt; decide renovarse por completo con un nuevo diseño y nueva 
tecnología, convirtiéndose en el escritorio más avanzado del momento. Y este año 
&lt;a href="http://es.wikipedia.org/wiki/GNOME"&gt;Gnome&lt;/a&gt; ha presentado también un gran cambio, no tan radical a nivel 
tecnologico como el de KDE, pero si presentando un cambio de paradigma con su 
&lt;a href="http://es.wikipedia.org/wiki/GNOME_Shell"&gt;Gnome Shell&lt;/a&gt;, aún algo inmaduro. &lt;a href="http://es.wikipedia.org/wiki/Ubuntu"&gt;Ubuntu&lt;/a&gt; se ha desmarcado de 
gnome creando su propia alternativa empleando su propio Shell, &lt;a href="http://es.wikipedia.org/wiki/Unity_%28entorno_de_escritorio%29"&gt;Unity&lt;/a&gt;, 
con un enfoque parecido.&lt;/p&gt;
&lt;p&gt;Todos estos cambios han traído avances tanto en el diseño como en la 
espectacularidad del escritorio linux, no teniendo nada que envidiarle en diseño 
ni usabilidad a la referencia tradicional en este campo, Mac OS X. Bien, ahora 
tenemos unos entornos de escritorio elegantes, completos y amigables, y cada vez 
más orientados al usuario final. De hecho, se ha evolucionado tanto en este 
sentido, que hemos pasado del tópico de que Linux era solo para expertos frente 
a una linea de comandos, a la situación actual en la que probablemente sea el 
escritorio más amigable y con menor curva de aprendizaje (para aquel que se 
inicia y nunca haya tenido contacto previo con un ordenador). &lt;/p&gt;
&lt;h2 id="+se_ha_mejorado_la_productividad"&gt;¿Se ha mejorado la productividad?&lt;/h2&gt;
&lt;p&gt;¿Pero que ocurre con los profesionales? ¿En que ha mejorado para ellos? Y cuando 
hablo de profesionales me refiero a todos aquellos que pasan la mayor parte de 
su jornada delante de la pantalla de un ordenador. ¿Que han aportado todos estos 
cambios a la productividad de estos usuarios, a su trabajo diario? Personalmente 
opino que estos cambios no han aportado absolutamente nada, puede que incluso 
hayan supuesto un paso atrás en algunos casos. Personalmente, &lt;strong&gt;Unity&lt;/strong&gt; me 
parece una broma de mal gusto, está bien que se piense en los usuarios domésticos 
(aquellos que principalmente hacen uso de internet y multimedia), pero por favor, 
el resto no merecemos ser tratados como idiotas. Y Gnome Shell, bueno, dentro de 
lo malo, tiene la excusa de ser aún un desarrollo muy temprano, y aparenta ser 
más prometedor.&lt;/p&gt;
&lt;p&gt;Desde mi punto de vista, los dos grandes avances en productividad en el 
escritorio linux que se han producido en los últimos años, provienen de dos 
ideas que han tenido un éxito muy dispar, pero que tienen en común estar 
basados en proyectos muy pequeños lejos de la repercusión de los grandes 
entornos de escritorio. Estas dos ideas son los &lt;strong&gt;Lanzadores de aplicaciones&lt;/strong&gt; y
 los &lt;strong&gt;Gestores de ventanas de mosaico&lt;/strong&gt;&lt;/p&gt;
&lt;h2 id="lanzadores_de_aplicaciones"&gt;Lanzadores de aplicaciones.&lt;/h2&gt;
&lt;p&gt;Es una idea genial, &lt;em&gt;lanzar aplicaciones a partir de su nombre&lt;/em&gt;, simple y 
rápido. Antes de nada, dejar claro que el ratón es enemigo de la productividad, 
cualquiera con la suficiente experiencia sabe que realizar acciones a través del 
teclado es más efectivo que realizar las mismas a través del ratón. Alguien que 
conozca los atajos de teclado de una aplicación realizara el mismo trabajo en 
menos tiempo que otro que deba emplear el ratón para todo. Y el uso del ratón es 
además el principal causante del &lt;a href="http://es.wikipedia.org/wiki/S%C3%ADndrome_del_t%C3%BAnel_carpiano"&gt;Sindrome del túnel carpiano&lt;/a&gt; entre los 
informáticos, y los que lo hemos sufrido -en mayor o menor medida- sabemos que 
no es ninguna broma. De hecho aunque los &lt;a href="http://es.wikipedia.org/wiki/Dock"&gt;docks&lt;/a&gt; son visualmente más 
atractivos y repletos de llamativos iconos, no son tan eficientes como los 
lanzadores cuando manejamos un buen número de distintas aplicaciones.&lt;/p&gt;
&lt;p&gt;La idea del lanzador de aplicaciones proviene de la aplicación &lt;a href="http://es.wikipedia.org/wiki/Quicksilver_%28software%29"&gt;Quicksilver&lt;/a&gt; 
desarrollada en 2006 para el SO de Apple. Dos años después aparecería la primera 
aplicación similar para linux, &lt;strong&gt;Gnome Do&lt;/strong&gt;. Estas aplicaciones han tenido un 
gran éxito y son de sobra conocidas, por lo que no voy a profundizar más en
ellas, simplemente apuntar que gracias a los plugins realizan bastantes más 
tareas que lanzar aplicaciones. Actualmente para linux hay disponibles los 
siguientes lanzadores de aplicaciones:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="http://do.davebsd.com/"&gt;Gnome Do&lt;/a&gt;, programado en Mono, es probablemente el mejor lanzador 
disponible hoy día. Es muy rápido y con gran cantidad de plugins. &lt;/li&gt;
&lt;li&gt;&lt;a href="https://launchpad.net/synapse-project"&gt;Synapse&lt;/a&gt;, programado en Vala. Es una buena alternativa a Do, 
rapidísimo y muy ligero.&lt;/li&gt;
&lt;li&gt;&lt;a href="http://www.launchy.net/"&gt;Launchy&lt;/a&gt;, es multiplataforma y está escrito en C++. Más popular en 
sistemas Windows que en Linux.&lt;/li&gt;
&lt;li&gt;&lt;a href="http://katapult.kde.org/"&gt;Katapult&lt;/a&gt;, es un lanzador pensado para KDE. Escrito en C++&lt;/li&gt;
&lt;li&gt;&lt;a href="http://www.simonschneegans.de/?page_id=12"&gt;Gnome Pie&lt;/a&gt;, a medio camino entre un lanzador y un dock. Más espectacular 
que eficaz, escrito en Vala.&lt;/li&gt;
&lt;li&gt;&lt;a href="http://kaizer.se/wiki/kupfer/"&gt;Kupfer&lt;/a&gt;, la última incorporación notable. Escrito en Python. Es el 
que empleo actualmente. Completo, rápido, ligero y no requiere de un 
&lt;a href="http://es.wikipedia.org/wiki/Gestor_de_composici%C3%B3n_de_ventanas"&gt;composition manager&lt;/a&gt; para funcionar correctamente.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Tal es el éxito que han tenido estas aplicaciones que tanto Gnome 3 como Unity 
lo incluyen como pilar básico en sus Shells (combinado con un dock) y KDE 4 
integra una aplicación similar denominada &lt;strong&gt;KRunner&lt;/strong&gt;.&lt;/p&gt;
&lt;h2 id="gestores_de_ventanas_de_mosaico"&gt;Gestores de ventanas de mosaico.&lt;/h2&gt;
&lt;p&gt;Merecen un &lt;a href="http://joedicastro.com/productividad-en-el-escritorio-linux-tiling.html"&gt;articulo aparte&lt;/a&gt; para hablar de ellos.&lt;/p&gt;</summary><category term="linux"></category><category term="unity"></category><category term="gnome"></category><category term="kde"></category><category term="productividad"></category><category term="desktop"></category></entry><entry><title>Fabric &amp; Rsync para realizar Backups</title><link href="http://joedicastro.com/fabric-rsync-para-realizar-backups.html" rel="alternate"></link><updated>2011-07-13T20:21:00+02:00</updated><author><name>joe di castro</name></author><id>http://joedicastro.com/fabric-rsync-para-realizar-backups.html</id><summary type="html">&lt;p&gt;En el &lt;a href="http://joedicastro.com/sincronizar-dos-directorios-con-fabric-y-rsync.html"&gt;anterior articulo&lt;/a&gt; empleaba &lt;a href="http://fabfile.org/"&gt;fabric&lt;/a&gt; y &lt;a href="http://es.wikipedia.org/wiki/Rsync"&gt;rsync&lt;/a&gt; para sincronizar 
un directorio local y uno remoto en ambas direcciones. Además le añadía las 
funcionalidades de &lt;a href="http://joedicastro.com/logger-informes-legibles-para-tus-scripts-python.html"&gt;logger&lt;/a&gt; y &lt;a href="http://joedicastro.com/notificaciones-de-escritorio-en-ubuntu-desde-python.html"&gt;notify&lt;/a&gt; para proporcionar información sobre 
el proceso durante y después de su ejecución. Y comenzaba el articulo recordando 
a &lt;a href="http://joedicastro.com/sincronizar-una-carpeta-local-y-una-remota-a-traves-de-ftp-lftp-mirror.html"&gt;lftp-mirror&lt;/a&gt;, el script que había creado para realizar la sincronización a 
través de FTP. Pero &lt;strong&gt;lftp-mirror&lt;/strong&gt; realiza algo más que la sincronización, pues 
también permite realizar el archivado del directorio local en ficheros 
comprimidos y lanzar varias tareas en una sola ejecución.&lt;/p&gt;
&lt;p&gt;Ahora he añadido esta funcionalidad al fichero &lt;strong&gt;fabric&lt;/strong&gt; creado anteriormente.
Así empleando este fichero podemos realizar el Backup periódico de varios 
servidores en una sola operación y de forma completamente automática (basta con 
programar su ejecución). Se sincronizan los dos directorios y se crea un archivo 
comprimido del directorio local por cada día de la semana. De este modo siempre 
tenemos una copia del estado del directorio remoto de los últimos siete días. Y 
al final del proceso en nuestro correo, un email con el informe del resultado por 
cada una de las tareas ejecutadas.&lt;/p&gt;
&lt;p&gt;En este fichero, &lt;strong&gt;rsync_fabric.py&lt;/strong&gt;, disponemos de tres posibles tareas:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="gp"&gt;$&lt;/span&gt; fab -l

&lt;span class="go"&gt;     A Fabric file for sync two directories (remote ⇄ local) with rsync.&lt;/span&gt;

&lt;span class="go"&gt;Available commands:&lt;/span&gt;

&lt;span class="go"&gt;    backup  Sync from remote to local &amp;amp; archive the local directory.&lt;/span&gt;
&lt;span class="go"&gt;    down    Sync from remote to local.&lt;/span&gt;
&lt;span class="go"&gt;    up      Sync from local to remote.&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Con la primera realizamos el backup (sincronización + archivado) y 
con las siguientes solo la sincronización desde o hacia el servidor. Una de las 
ventajas de fabric es que nos permite concatenar tareas fácilmente desde la 
línea de comandos, así podemos lanzar varias sincronizaciones de forma 
simultanea. Para poder realizar esto, creo una configuración de sincronización 
por defecto y después creo una función para cada una las tareas adicionales que 
simplemente redefinen los valores de estas variables globales. Por ejemplo:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="c"&gt;# Variables globales de sincronización predefenidas&lt;/span&gt;
&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;host_string&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;username@example.com&amp;quot;&lt;/span&gt;
&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;remote&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;/my_directory&amp;quot;&lt;/span&gt;
&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;local&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;/home/my_user/backups/my_directory&amp;quot;&lt;/span&gt;

&lt;span class="c"&gt;# Redefinimos estas variables para otra configuración de sincronización. Por &lt;/span&gt;
&lt;span class="c"&gt;# supuesto, pueden tratarse de servidores distintos.&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;_databases&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="k"&gt;global&lt;/span&gt; &lt;span class="n"&gt;env&lt;/span&gt;
    &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;host_string&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;username@example.com&amp;quot;&lt;/span&gt;
    &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;remote&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;/databases&amp;quot;&lt;/span&gt;
    &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;local&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;/home/my_user/backups/databases&amp;quot;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Veamos ejemplos de como podemos utilizar estas tareas:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="gp"&gt;#&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;Si queremos sincronizar el contenido local hacia el remoto, por ejemplo &lt;/span&gt;
&lt;span class="gp"&gt;#&lt;/span&gt;&lt;span class="s2"&gt; para subir los ficheros al servidor por primera vez. Empleando los valores &lt;/span&gt;
&lt;span class="gp"&gt;#&lt;/span&gt;&lt;span class="s2"&gt; por defecto. El modificador -w lo empleo para que no se detenga en los &lt;/span&gt;
&lt;span class="gp"&gt;#&lt;/span&gt;&lt;span class="s2"&gt; errores, que de ocurrir, los veremos luego en el informe final.&amp;quot;&lt;/span&gt;
&lt;span class="gp"&gt;$&lt;/span&gt; fab -w up
&lt;span class="go"&gt;[localhost] local: rsync -pthrvz --delete /home/my_user/backups/my_directory/ &lt;/span&gt;
&lt;span class="go"&gt; username@example.com:my_directory&lt;/span&gt;

&lt;span class="go"&gt;Done.&lt;/span&gt;
&lt;span class="gp"&gt;#&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;Pero también podemos especificar una tarea distinta a la por defecto de &lt;/span&gt;
&lt;span class="gp"&gt;#&lt;/span&gt;&lt;span class="s2"&gt; este modo. Sincronizando desde el servidor a nuestro directorio local las &lt;/span&gt;
&lt;span class="gp"&gt;#&lt;/span&gt;&lt;span class="s2"&gt; bases de datos.&amp;quot;&lt;/span&gt;
&lt;span class="gp"&gt;$&lt;/span&gt; fab -w down:databases
&lt;span class="go"&gt;[localhost] local: rsync -pthrvz --delete username@example.com:databases/ &lt;/span&gt;
&lt;span class="go"&gt;/home/my_user/backups/databases&lt;/span&gt;

&lt;span class="go"&gt;Done.&lt;/span&gt;
&lt;span class="gp"&gt;#&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;Y por supuesto, podemos realizar varias tareas a la vez.&amp;quot;&lt;/span&gt;
&lt;span class="gp"&gt;$&lt;/span&gt;  fab -w down backup:databases
&lt;span class="go"&gt;[localhost] local: rsync -pthrvz --delete username@example.com:my_directory/ &lt;/span&gt;
&lt;span class="go"&gt;/home/my_user/backups/my_directory&lt;/span&gt;
&lt;span class="go"&gt;[localhost] local: rsync -pthrvz --delete username@example.com:databases/ &lt;/span&gt;
&lt;span class="go"&gt;/home/my_user/backups/databases&lt;/span&gt;

&lt;span class="go"&gt;Done.&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;No empleo contraseña alguna, ni en el fichero ni en la línea de comandos, podría 
hacerse perfectamente, pero prefiero emplear una clave &lt;a href="http://es.wikipedia.org/wiki/RSA"&gt;RSA&lt;/a&gt; &lt;a href="http://es.wikipedia.org/wiki/Criptograf%C3%ADa_asim%C3%A9trica"&gt;pública&lt;/a&gt; 
autorizada para las sesiones SSH en el servidor. Es bastante más seguro y cómodo. 
En los ejemplos no se ve la salida de &lt;em&gt;rsync&lt;/em&gt;, pues es capturada (así como los 
erores) para ser mostrada a posteriori en los informes. &lt;/p&gt;
&lt;p&gt;Un ejemplo de informe sería el siguiente:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;START TIME =====================================================================
                                                    miércoles 13/07/11, 19:48:55
================================================================================

SCRIPT =========================================================================
fab (ver. Unknown)
Fabric Rsync
http://code.joedicastro.com/python-recipes

Syncing username@example.com:databases to /home/my_user/backups/databases
================================================================================

RSYNC OUTPUT ___________________________________________________________________

receiving file list ... done

sent 20 bytes  received 825 bytes  153.64 bytes/sec
total size is 827.76M  speedup is 979595.42

ROTATE COMPRESSED COPIES _______________________________________________________

Created file:

/home/my_user/backups/databases_13jul2011_19:49_mié.tar.gz

Deleted old file:

databases_13jul2011_19:37_mié.tar.gz

DISK SPACE USED ================================================================
                                                                        1.60 GiB
================================================================================

END TIME =======================================================================
                                                    miércoles 13/07/11, 19:50:02
================================================================================
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Que como podemos ver, ha tardado poco más de un minuto en sincronizar 827.56 
Megabytes y el total de espacio ocupado por el directorio y los siete archivos 
comprimidos es de 1.60 Gibibytes (1,72 Gigabytes). &lt;/p&gt;
&lt;h2 id="ventajas"&gt;Ventajas&lt;/h2&gt;
&lt;p&gt;Las ventajas de sincronizarlo con &lt;strong&gt;rsync + ssh&lt;/strong&gt; vs &lt;strong&gt;ftp&lt;/strong&gt;, como ya comenté en el 
anterior articulo son enormes. Se ahorra muchísimo tiempo y ancho de banda, lo 
que ayuda a no saturar la red y no tener que planificar con tanto cuidado las 
ventanas de backup. Por ejemplo he realizado unas pruebas y para las mismas 
condiciones: &lt;strong&gt;mismo servidor, mismo directorio, mismo horario y condiciones de 
red; la sincronización remoto → local a través de FTP emplea entre 35 y 45 
minutos y cuando lo hacemos a través de rsync emplea entre 2 y 4 minutos&lt;/strong&gt;. Ahí 
es nada, estamos hablando de un proceso ~13 veces más rápido. &lt;/p&gt;
&lt;h2 id="c+digo"&gt;Código&lt;/h2&gt;
&lt;p&gt;El código del fichero fabric es el siguiente:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="c"&gt;#!/usr/bin/env python&lt;/span&gt;
&lt;span class="c"&gt;# -*- coding: utf8 -*-&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;os&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;glob&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;tarfile&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;time&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;get_size&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;get_size&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;_get_size&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;get_size&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;best_unit_size&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;_best_unit_size&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;logger&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Logger&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;_logger&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;notify&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;notify&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;_notify&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;fabric.api&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;local&lt;/span&gt;

&lt;span class="n"&gt;LOG&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;_logger&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="c"&gt;#===============================================================================&lt;/span&gt;
&lt;span class="c"&gt;# RSYNC HOSTS&lt;/span&gt;
&lt;span class="c"&gt;#===============================================================================&lt;/span&gt;

&lt;span class="c"&gt;# Your default host. No need any more if only wants a host.&lt;/span&gt;
&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;host_string&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;username@host&amp;quot;&lt;/span&gt;
&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;remote&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;/your/remote/path&amp;quot;&lt;/span&gt;
&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;local&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;/your/local/path&amp;quot;&lt;/span&gt;

&lt;span class="c"&gt;# If wants to use various hosts, then define the previous variables like this, &lt;/span&gt;
&lt;span class="c"&gt;# one function per host. &lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;_host_1&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;Host variables for host_1.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
    &lt;span class="k"&gt;global&lt;/span&gt; &lt;span class="n"&gt;env&lt;/span&gt;
    &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;host_string&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;username@host_1&amp;quot;&lt;/span&gt;
    &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;remote&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;/your/remote/path/in/host_1&amp;quot;&lt;/span&gt;
    &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;local&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;/your/local/path/for/host_1&amp;quot;&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;_host_2&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;Host variables for host_2.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
    &lt;span class="k"&gt;global&lt;/span&gt; &lt;span class="n"&gt;env&lt;/span&gt;
    &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;host_string&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;username@host_2&amp;quot;&lt;/span&gt;
    &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;remote&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;/your/remote/path/in/host_2&amp;quot;&lt;/span&gt;
    &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;local&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;/your/local/path/for/host_2&amp;quot;&lt;/span&gt;

&lt;span class="c"&gt;# ...&lt;/span&gt;
&lt;span class="c"&gt;#&lt;/span&gt;
&lt;span class="c"&gt;# def _host_n():&lt;/span&gt;
&lt;span class="c"&gt;#     &amp;quot;&amp;quot;&amp;quot;Host variables for host_n.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="c"&gt;#     global env&lt;/span&gt;
&lt;span class="c"&gt;#     env.host_string = &amp;quot;username@host_n&amp;quot;&lt;/span&gt;
&lt;span class="c"&gt;#     env.remote = &amp;quot;/your/remote/path/in/host_n&amp;quot;&lt;/span&gt;
&lt;span class="c"&gt;#     env.local = &amp;quot;/your/local/path/for/host_n&amp;quot;&lt;/span&gt;

&lt;span class="c"&gt;#===============================================================================&lt;/span&gt;
&lt;span class="c"&gt;# END RSYNC HOSTS&lt;/span&gt;
&lt;span class="c"&gt;#===============================================================================&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;_log_start&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;Create the Start time info block for the log.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
    &lt;span class="c"&gt;# Init the log for multiple hosts. Do not repeat the previous logs.&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;LOG&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
        &lt;span class="n"&gt;LOG&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;LOG&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;Start time&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;_log_end&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;task&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;Create the End time info block and send &amp;amp; write the log.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
    &lt;span class="n"&gt;_notify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;Rsync&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;Ended&amp;quot;&lt;/span&gt; &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;ok&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;LOG&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;End time&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;LOG&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;free&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;linesep&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;LOG&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;LOG&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;Fabric Rsync ({0})&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;task&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;_check_local&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;Create local directory if no exists.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;exists&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;local&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mkdir&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;local&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;_rsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;source&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;target&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;delete&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;Process the _rsync command.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
    &lt;span class="n"&gt;_log_start&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;LOG&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;header&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;Fabric Rsync&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;http://code.joedicastro.com/python-recipes&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
               &lt;span class="s"&gt;&amp;quot;Syncing {0} to {1}&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;source&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;target&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="n"&gt;_notify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;Rsync&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;Start syncing {0} to {1}&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;source&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;target&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;info&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;out&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;local&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;rsync -pthrvz {2} {0}/ {1}&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;
                &lt;span class="n"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;source&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;target&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;--delete&amp;quot;&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;delete&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;yes&amp;quot;&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
                &lt;span class="n"&gt;capture&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;_notify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;Rsync&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;Finished synchronization&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;ok&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;LOG&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;Rsync Output&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;out&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;out&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;failed&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;LOG&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;Rsync Errors&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;out&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;stderr&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;_compress&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;Compress a local directory into a gz file.&lt;/span&gt;

&lt;span class="sd"&gt;    Creates a file for each weekday, an removes the old files if exists&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
    &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;chdir&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pardir&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="n"&gt;dir2gz&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;basename&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;old_gzs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;glob&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;glob&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;{0}*{1}.tar.gz&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dir2gz&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;strftime&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;%a&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
    &lt;span class="n"&gt;gz_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;{0}_{1}.tar.gz&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dir2gz&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;strftime&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;&lt;/span&gt;&lt;span class="si"&gt;%d&lt;/span&gt;&lt;span class="s"&gt;%b%Y_%H:%M_%a&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="n"&gt;gz_file&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;tarfile&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;gz_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;w:gz&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;gz_file&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;arcname&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;dir2gz&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;gz_file&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;close&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;output&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;linesep&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;join&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;Created file:&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;getcwd&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
                                                                &lt;span class="n"&gt;gz_name&lt;/span&gt;&lt;span class="p"&gt;)])&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;old_gz&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;old_gzs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;remove&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;old_gz&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;output&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;linesep&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;join&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;linesep&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;Deleted old file:&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;old_gz&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;output&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;_archive&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;Archive the local directory in a gz file for each weekday.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
    &lt;span class="n"&gt;_notify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;Rsync&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;Compressing folder...&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;info&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;LOG&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;Rotate compressed copies&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_compress&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;local&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="n"&gt;_notify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;Rsync&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;Finished compression&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;ok&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;_get_diskspace&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;Get the disk space used by the local directory and archives.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
    &lt;span class="n"&gt;gz_size&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="n"&gt;_get_size&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;gz&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;gz&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;glob&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;glob&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;{0}*.gz&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;
                                                     &lt;span class="n"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;local&lt;/span&gt;&lt;span class="p"&gt;))])&lt;/span&gt;
    &lt;span class="n"&gt;log_size&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;_get_size&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;LOG&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;exists&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;LOG&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
    &lt;span class="n"&gt;local_size&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;_get_size&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;local&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;size&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;_best_unit_size&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;local_size&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;gz_size&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;log_size&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;LOG&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;block&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;Disk space used&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;{0:&amp;gt;76.2f} {1}&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;s&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;u&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]))&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;up&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;server&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;dlt&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;yes&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;Sync from local to remote.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
    &lt;span class="nb"&gt;globals&lt;/span&gt;&lt;span class="p"&gt;()[&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;_&amp;quot;&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;server&lt;/span&gt;&lt;span class="p"&gt;]()&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;server&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;
    &lt;span class="n"&gt;_rsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;local&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;:&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;join&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;host_string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;remote&lt;/span&gt;&lt;span class="p"&gt;]),&lt;/span&gt; &lt;span class="n"&gt;dlt&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;_log_end&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;server&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;down&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;server&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;dlt&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;yes&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;archive&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;Sync from remote to local.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
    &lt;span class="nb"&gt;globals&lt;/span&gt;&lt;span class="p"&gt;()[&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;_&amp;quot;&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;server&lt;/span&gt;&lt;span class="p"&gt;]()&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;server&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;
    &lt;span class="n"&gt;_check_local&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;_rsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;:&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;join&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;host_string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;remote&lt;/span&gt;&lt;span class="p"&gt;]),&lt;/span&gt; &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;local&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;dlt&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;archive&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;_log_end&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;server&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;backup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;server&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;Sync from remote to local &amp;amp; archive the local directory.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
    &lt;span class="n"&gt;down&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;server&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;archive&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;_archive&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;_get_diskspace&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;_log_end&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;server&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;El fichero siempre actualizado puede ser encontrado en &lt;a href="https://bitbucket.org/joedicastro/python-recipes/src/tip/src/rsync_fabfile.py"&gt;mi repositorio&lt;/a&gt; &lt;/p&gt;</summary><category term="python"></category><category term="fabric"></category><category term="rsync"></category><category term="backup"></category><category term="sincronizar"></category></entry><entry><title>Sincronizar dos directorios con Fabric y Rsync</title><link href="http://joedicastro.com/sincronizar-dos-directorios-con-fabric-y-rsync.html" rel="alternate"></link><updated>2011-07-06T22:02:00+02:00</updated><author><name>joe di castro</name></author><id>http://joedicastro.com/sincronizar-dos-directorios-con-fabric-y-rsync.html</id><summary type="html">&lt;p&gt;Anteriormente habíamos visto como &lt;a href="http://joedicastro.com/sincronizar-una-carpeta-local-y-una-remota-a-traves-de-ftp-lftp-mirror.html"&gt;sincronizar un directorio remoto y uno local 
empleando solamente FTP&lt;/a&gt;. Ahora vamos a ver la forma de hacerlo empleando 
&lt;a href="http://es.wikipedia.org/wiki/Ssh"&gt;ssh&lt;/a&gt; y &lt;a href="http://es.wikipedia.org/wiki/Rsync"&gt;rsync&lt;/a&gt;. Para ello vamos a utilizar otra vez &lt;strong&gt;Python&lt;/strong&gt; y una 
herramienta muy valiosa para cualquier &lt;a href="http://es.wikipedia.org/wiki/Administrador_de_sistemas"&gt;sysadmin&lt;/a&gt; que se precie como es 
&lt;a href="http://fabfile.org/"&gt;fabric&lt;/a&gt; (que descubrí gracias a Manuel Viera en &lt;a href="http://python.majibu.org/preguntas/11/libreria-para-emplear-con-ssh"&gt;esta pregunta en majibu&lt;/a&gt;).
Evidentemente realizar la sincronización con rsync esta a años luz de hacerlo 
con FTP, la velocidad de sincronización, el tiempo empleado y la cantidad de 
datos a mover son mucho menores. FTP es algo que debería utilizarse únicamente 
cuando no disponemos de acceso via SSH.&lt;/p&gt;
&lt;p&gt;La gran ventaja de &lt;strong&gt;fabric&lt;/strong&gt; es que nos permite ahorrarnos el tener que 
implementar el acceso SSH con &lt;a href="http://www.lag.net/paramiko/"&gt;paramiko&lt;/a&gt; y la entrada de opciones y 
argumentos con &lt;em&gt;argparse&lt;/em&gt;. Gracias a esto los scripts necesarios son mucho más 
cortos y limpios y su utilización es bastante más sencilla. Fabric ya incorpora 
una función para emplear rsync, &lt;code&gt;rsync_project&lt;/code&gt;, dentro de su modulo de proyectos 
contribuidos &lt;code&gt;fabric.contrib.project&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Una forma de implementar esta sincronización en ambas direcciones empleando esta 
función predefinida sería esta:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;fabric.api&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;hosts&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;local&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;fabric.contrib.project&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;rsync_project&lt;/span&gt;

&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;host_string&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;username@host&amp;quot;&lt;/span&gt;
&lt;span class="n"&gt;REMOTE_PATH&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;/your/remote/path&amp;quot;&lt;/span&gt;
&lt;span class="n"&gt;LOCAL_PATH&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;/your/local/path&amp;quot;&lt;/span&gt;

&lt;span class="nd"&gt;@hosts&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;host_string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;rsync_up&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dlt&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;yes&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;rsync_project&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;REMOTE_PATH&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;LOCAL_PATH&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;/&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
                  &lt;span class="n"&gt;delete&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;dlt&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;yes&amp;quot;&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;rsync_down&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dlt&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;yes&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;local&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;rsync -pthrvz {0}:{1}/ {2} {3}&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;
          &lt;span class="n"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;host_string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;REMOTE_PATH&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;LOCAL_PATH&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
          &lt;span class="s"&gt;&amp;quot;--delete&amp;quot;&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;dlt&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;yes&amp;quot;&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Y luego solo tendríamos que llamar a la función deseada:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="gp"&gt;#&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;Para sincronizar de remoto a local&amp;quot;&lt;/span&gt;
&lt;span class="gp"&gt;$&lt;/span&gt; fab rsync_down
&lt;/pre&gt;&lt;/div&gt;


&lt;blockquote&gt;
&lt;p&gt;Hay que tener en cuenta un detalle con fabric. Cuando se le pasa un parámetro, 
este es siempre convertido a una cadena. Luego al pasarle &lt;code&gt;True&lt;/code&gt; o &lt;code&gt;False&lt;/code&gt; no 
se convierte en un valor booleano, sino una cadena &lt;code&gt;"True"&lt;/code&gt;o &lt;code&gt;"False"&lt;/code&gt;. De ahí 
que compruebe si el parámetro coincide con &lt;code&gt;"yes"&lt;/code&gt; en vez de un valor booleano.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;El problema con la función rsync predefinida de fabric es que esta pensada 
únicamente para subir archivos a un servidor remoto, es decir, es una 
sincronización en una sola dirección, por eso implemento la sincronización en 
sentido contrario sin emplearla y empleando &lt;code&gt;local&lt;/code&gt;. La autentificación de la 
sesión SSH puede realizarse especificando la contraseña dentro del propio fichero, 
pero va en contra del sentido común emplear un método tan inseguro como este. Lo 
lógico es emplear autorizaciones de sesiones SSH sin contraseña por medio de una 
&lt;a href="http://es.wikipedia.org/wiki/Criptograf%C3%ADa_asim%C3%A9trica"&gt;clave pública&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Podríamos prescindir de la librería incorporada dentro de fabric y tendríamos 
algo como esto:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;fabric.api&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;local&lt;/span&gt;

&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;host_string&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;username@host&amp;quot;&lt;/span&gt;
&lt;span class="n"&gt;REMOTE_PATH&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;/your/remote/path&amp;quot;&lt;/span&gt;
&lt;span class="n"&gt;LOCAL_PATH&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;/your/local/path&amp;quot;&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;_rsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;source&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;target&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;delete&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;Process the _rsync command.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
    &lt;span class="n"&gt;output&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;local&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;rsync -pthrvz {0}/ {1} {2}&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;
                   &lt;span class="n"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;source&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;target&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;--delete&amp;quot;&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;delete&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;yes&amp;quot;&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;up&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dlt&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;yes&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;Sync from local to remote.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
    &lt;span class="n"&gt;_rsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;LOCAL_PATH&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;:&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;join&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;host_string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;REMOTE_PATH&lt;/span&gt;&lt;span class="p"&gt;]),&lt;/span&gt; &lt;span class="n"&gt;dlt&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;down&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dlt&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;yes&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;Sync from remote to local.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
    &lt;span class="n"&gt;_rsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;:&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;join&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;host_string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;REMOTE_PATH&lt;/span&gt;&lt;span class="p"&gt;]),&lt;/span&gt; &lt;span class="n"&gt;LOCAL_PATH&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;dlt&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Pero... un momento, si estamos empleado un comando local, no empleamos 
&lt;code&gt;rsync_project&lt;/code&gt; y empleamos una clave pública para el acceso SSH, entonces no 
estamos empleando &lt;strong&gt;paramiko&lt;/strong&gt;, ¿de que nos sirve emplear fabric?. Bueno, en 
realidad &lt;code&gt;rsync_project&lt;/code&gt; también emplea &lt;code&gt;local&lt;/code&gt;, por lo que no emplea paramiko. 
Pero las ventajas vienen de que, por ejemplo, este mismo script se podría modificar 
fácilmente para ejecutar rsync en el servidor en vez de en nuestra maquina local, 
 empleando &lt;code&gt;run&lt;/code&gt; en vez de &lt;code&gt;local&lt;/code&gt;. Además podemos emplear el mismo fichero para 
 añadir varias tareas más a realizar en el servidor, aparte de la sincronización. 
Podríamos prescindir de fabric y hacer esto mismo con un script con un número 
similar de líneas, pero esto nos permite centralizar todas las tareas más comunes 
sobre ese servidor en un único fichero. Por ejemplo podríamos añadir una tarea 
para hacer un respaldo previo de una base de datos en el servidor, empleando un 
comando remoto en el servidor, luego hacer la sincronización separada de la BDD 
y el resto de ficheros y finalmente eliminar ese respaldo. Puede haber cientos 
de razones para preferir emplear fabric antes de un script independiente para 
la sincronización.&lt;/p&gt;
&lt;h2 id="ejecuci+n_desatendida_de_la_sincronizaci+n"&gt;Ejecución desatendida de la sincronización&lt;/h2&gt;
&lt;p&gt;Si queremos programar esta tarea, no sería mala idea que nos avisara de cuando 
comienza a ejecutarse y del resultado de la misma. Para ello puedo emplear 
&lt;a href="http://joedicastro.com/logger-informes-legibles-para-tus-scripts-python.html"&gt;Logger&lt;/a&gt; y &lt;a href="http://joedicastro.com/notificaciones-de-escritorio-en-ubuntu-desde-python.html"&gt;notify&lt;/a&gt;, para implementar esta funcionalidad.&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;logger&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Logger&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;_logger&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;notify&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;notify&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;_notify&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;fabric.api&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;local&lt;/span&gt;

&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;host_string&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;username@host&amp;quot;&lt;/span&gt;
&lt;span class="n"&gt;REMOTE_PATH&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;/your/remote/path&amp;quot;&lt;/span&gt;
&lt;span class="n"&gt;LOCAL_PATH&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;/your/local/path&amp;quot;&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;_rsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;source&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;target&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;delete&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;Process the _rsync command.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
    &lt;span class="n"&gt;log&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;_logger&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;header&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;Fabric Rsync&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;http://code.joedicastro.com/python-recipes&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
               &lt;span class="s"&gt;&amp;quot;Syncing {0} to {1}&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;source&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;target&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;Start time&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;_notify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;Rsync&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;Start syncing {0} to {1}&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;source&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;target&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;info&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;output&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;local&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;rsync -pthrvz {0}/ {1} {2}&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;
                   &lt;span class="n"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;source&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;target&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;--delete&amp;quot;&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;delete&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;yes&amp;quot;&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
                   &lt;span class="n"&gt;capture&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;_notify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;Rsync&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;Finished&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;ok&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;Output&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;output&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;output&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;failed&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;Error&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;output&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;stderr&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;End time&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;Fabric Rsync&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;up&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dlt&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;yes&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;Sync from local to remote.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
    &lt;span class="n"&gt;_rsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;LOCAL_PATH&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;:&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;join&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;host_string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;REMOTE_PATH&lt;/span&gt;&lt;span class="p"&gt;]),&lt;/span&gt; &lt;span class="n"&gt;dlt&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;down&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dlt&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;yes&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;Sync from remote to local.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
    &lt;span class="n"&gt;_rsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;:&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;join&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;host_string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;REMOTE_PATH&lt;/span&gt;&lt;span class="p"&gt;]),&lt;/span&gt; &lt;span class="n"&gt;LOCAL_PATH&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;dlt&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;De esta forma, nos avisaría con una notificación en el escritorio de su inicio y 
fin, y al acabarse la sincronización, tendríamos un informe en nuestro correo 
parecido a este:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;SCRIPT =========================================================================
fab (ver. Unknown)
Fabric Rsync

Syncing username@host:/your/remote/path to /your/local/path
================================================================================

START TIME =====================================================================
                                                   miércoles 06/07/11, 21:50:48
================================================================================

OUTPUT _________________________________________________________________________

receiving file list ... done
./
index.php

sent 48 bytes  received 200 bytes  45.09 bytes/sec
total size is 99  speedup is 0.40

END TIME =======================================================================
                                                   miércoles 06/07/11, 21:50:54
================================================================================
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Este fichero está disponible en &lt;a href="https://bitbucket.org/joedicastro/python-recipes/src/tip/src/rsync_fabfile.py"&gt;mi repositorio&lt;/a&gt;.&lt;/p&gt;</summary><category term="python"></category><category term="fabric"></category><category term="rsync"></category><category term="sincronizar"></category></entry><entry><title>Pelican - Repositorio</title><link href="http://joedicastro.com/pelican-repositorio.html" rel="alternate"></link><updated>2011-07-05T23:02:00+02:00</updated><author><name>joe di castro</name></author><id>http://joedicastro.com/pelican-repositorio.html</id><summary type="html">&lt;p&gt;Como complemento a la &lt;a href="http://joedicastro.com/tag/Pelican.html"&gt;serie de artículos&lt;/a&gt; que he publicado sobre &lt;a href="http://docs.notmyidea.org/alexis/pelican/"&gt;Pelican&lt;/a&gt;, 
el software que genera este blog, añado el repositorio, &lt;a href="http://joedicastro.com/pelican-configuracion-y-personalizacion.html"&gt;como había prometido&lt;/a&gt;, 
del contenido del mismo. El repositorio emplea el sistema de control de versiones 
&lt;a href="http://mercurial.selenic.com/"&gt;Mercurial&lt;/a&gt; y está alojado en &lt;a href="https://bitbucket.org/"&gt;Bitbucket&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Las ventajas de disponer del contenido del blog en un repositorio son las de 
poder enmendar un error con suma facilidad y en muy poco tiempo, además de la de 
poder trabajar con distintas versiones del mismo (pruebas y producción). Además 
el repositorio en Bitbucket me proporciona una copia de seguridad adicional del 
sitio sin esfuerzo alguno. Y si alguien está interesado en crear su propio blog 
con Pelican y quiere saber como he realizado el mio, ahí tiene las claves. Salvo 
el propio Pelican (que no tendría mucho sentido) todo el material empleado para 
generarlo está en el. Y disponiendo del fichero &lt;strong&gt;fabric&lt;/strong&gt;, se pueden descargar 
Pelican e instalar el entorno virtual en un minuto. &lt;/p&gt;
&lt;p&gt;Para automatizar todas las tareas, incluso las más comunes del repositorio, he 
añadido al fichero fabric &lt;em&gt;fabfile.py&lt;/em&gt; dos tareas más:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;commit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;Make a commit to the local mercurial repository.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
    &lt;span class="n"&gt;local&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;hg add&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;local&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;hg commit -m &amp;#39;{0}&amp;#39;&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;Make a push to the remote mercurial repository.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
    &lt;span class="n"&gt;local&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;hg push ssh://hg@bitbucket.org/joedicastro/joedicastro.com&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Con estas puedo hacer un &lt;code&gt;commit&lt;/code&gt; y un &lt;code&gt;push&lt;/code&gt; a Bitbucket en un solo paso, por 
ejemplo:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="go"&gt;fab commit:&amp;quot;Añadido articulo: Pelican - Repositorio&amp;quot; push&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;También he cambiado la página que generaba los archivos del blog, ya que no me 
gustaba el formato anterior: una fecha, un articulo. He pasado de esto:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="cp"&gt;{%&lt;/span&gt; &lt;span class="k"&gt;extends&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;base.html&amp;quot;&lt;/span&gt; &lt;span class="cp"&gt;%}&lt;/span&gt;&lt;span class="x"&gt;&lt;/span&gt;
&lt;span class="cp"&gt;{%&lt;/span&gt; &lt;span class="k"&gt;block&lt;/span&gt; &lt;span class="nv"&gt;content&lt;/span&gt; &lt;span class="cp"&gt;%}&lt;/span&gt;&lt;span class="x"&gt;&lt;/span&gt;
&lt;span class="x"&gt;&amp;lt;section id=&amp;quot;content&amp;quot; class=&amp;quot;body&amp;quot;&amp;gt;&lt;/span&gt;
&lt;span class="x"&gt;&amp;lt;h1&amp;gt;Archivos de &lt;/span&gt;&lt;span class="cp"&gt;{{&lt;/span&gt; &lt;span class="nv"&gt;SITENAME&lt;/span&gt; &lt;span class="cp"&gt;}}&lt;/span&gt;&lt;span class="x"&gt;&amp;lt;/h1&amp;gt;&lt;/span&gt;

&lt;span class="x"&gt;&amp;lt;dl&amp;gt;&lt;/span&gt;
&lt;span class="cp"&gt;{%&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="nv"&gt;article&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="nv"&gt;dates&lt;/span&gt; &lt;span class="cp"&gt;%}&lt;/span&gt;&lt;span class="x"&gt;&lt;/span&gt;
&lt;span class="x"&gt;    &amp;lt;dt&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;{{&lt;/span&gt; &lt;span class="nv"&gt;article.locale_date&lt;/span&gt; &lt;span class="cp"&gt;}}&lt;/span&gt;&lt;span class="x"&gt;&amp;lt;/dt&amp;gt;&lt;/span&gt;
&lt;span class="x"&gt;    &amp;lt;dd&amp;gt;&amp;lt;a href=&amp;#39;&lt;/span&gt;&lt;span class="cp"&gt;{{&lt;/span&gt; &lt;span class="nv"&gt;article.url&lt;/span&gt; &lt;span class="cp"&gt;}}&lt;/span&gt;&lt;span class="x"&gt;&amp;#39;&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;{{&lt;/span&gt; &lt;span class="nv"&gt;article.title&lt;/span&gt; &lt;span class="cp"&gt;}}&lt;/span&gt;&lt;span class="x"&gt;&amp;lt;/a&amp;gt;&amp;lt;/dd&amp;gt;&lt;/span&gt;
&lt;span class="cp"&gt;{%&lt;/span&gt; &lt;span class="k"&gt;endfor&lt;/span&gt; &lt;span class="cp"&gt;%}&lt;/span&gt;&lt;span class="x"&gt;&lt;/span&gt;
&lt;span class="x"&gt;&amp;lt;/dl&amp;gt;&lt;/span&gt;
&lt;span class="x"&gt;&amp;lt;/section&amp;gt;&lt;/span&gt;
&lt;span class="cp"&gt;{%&lt;/span&gt; &lt;span class="k"&gt;endblock&lt;/span&gt; &lt;span class="cp"&gt;%}&lt;/span&gt;&lt;span class="x"&gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;a esto:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="cp"&gt;{%&lt;/span&gt; &lt;span class="k"&gt;extends&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;base.html&amp;quot;&lt;/span&gt; &lt;span class="cp"&gt;%}&lt;/span&gt;&lt;span class="x"&gt;&lt;/span&gt;
&lt;span class="cp"&gt;{%&lt;/span&gt; &lt;span class="k"&gt;block&lt;/span&gt; &lt;span class="nv"&gt;content&lt;/span&gt; &lt;span class="cp"&gt;%}&lt;/span&gt;&lt;span class="x"&gt;&lt;/span&gt;
&lt;span class="x"&gt;&amp;lt;section id=&amp;quot;content&amp;quot; class=&amp;quot;body&amp;quot;&amp;gt;&lt;/span&gt;
&lt;span class="x"&gt;&amp;lt;h1&amp;gt;Archivos de &lt;/span&gt;&lt;span class="cp"&gt;{{&lt;/span&gt; &lt;span class="nv"&gt;SITENAME&lt;/span&gt; &lt;span class="cp"&gt;}}&lt;/span&gt;&lt;span class="x"&gt;&amp;lt;/h1&amp;gt;&lt;/span&gt;
&lt;span class="cp"&gt;{%&lt;/span&gt;- &lt;span class="k"&gt;set&lt;/span&gt; &lt;span class="nv"&gt;years_month&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;{}&lt;/span&gt; -&lt;span class="cp"&gt;%}&lt;/span&gt;&lt;span class="x"&gt;&lt;/span&gt;
&lt;span class="cp"&gt;{%&lt;/span&gt;- &lt;span class="k"&gt;set&lt;/span&gt; &lt;span class="nv"&gt;months&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="s1"&gt;:&amp;#39;Enero&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="m"&gt;2&lt;/span&gt;&lt;span class="s1"&gt;:&amp;#39;Febrero&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="m"&gt;3&lt;/span&gt;&lt;span class="s1"&gt;:&amp;#39;Marzo&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="m"&gt;4&lt;/span&gt;&lt;span class="s1"&gt;:&amp;#39;Abril&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="m"&gt;5&lt;/span&gt;&lt;span class="s1"&gt;:&amp;#39;Mayo&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; 
                  &lt;span class="m"&gt;6&lt;/span&gt;&lt;span class="s1"&gt;:&amp;#39;Junio&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="m"&gt;7&lt;/span&gt;&lt;span class="s1"&gt;:&amp;#39;Julio&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="m"&gt;8&lt;/span&gt;&lt;span class="s1"&gt;:&amp;#39;Agosto&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="m"&gt;9&lt;/span&gt;&lt;span class="s1"&gt;:&amp;#39;Septiembre&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; 
                  &lt;span class="m"&gt;10&lt;/span&gt;&lt;span class="s1"&gt;:&amp;#39;Octubre&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="m"&gt;11&lt;/span&gt;&lt;span class="s1"&gt;:&amp;#39;Noviembre&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="m"&gt;12&lt;/span&gt;&lt;span class="s1"&gt;:&amp;#39;Diciembre&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt; -&lt;span class="cp"&gt;%}&lt;/span&gt;&lt;span class="x"&gt; &lt;/span&gt;
&lt;span class="cp"&gt;{%&lt;/span&gt;- &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="nv"&gt;article&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="nv"&gt;dates&lt;/span&gt; -&lt;span class="cp"&gt;%}&lt;/span&gt;&lt;span class="x"&gt;&lt;/span&gt;
&lt;span class="x"&gt;  &lt;/span&gt;&lt;span class="cp"&gt;{%&lt;/span&gt;- &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nv"&gt;article.date.year&lt;/span&gt; &lt;span class="k"&gt;not&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="nv"&gt;years_month&lt;/span&gt; -&lt;span class="cp"&gt;%}&lt;/span&gt;&lt;span class="x"&gt;&lt;/span&gt;
&lt;span class="x"&gt;    &lt;/span&gt;&lt;span class="cp"&gt;{%&lt;/span&gt;- &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="nv"&gt;years_month.update&lt;/span&gt;&lt;span class="o"&gt;({&lt;/span&gt;&lt;span class="nv"&gt;article.date.year&lt;/span&gt;&lt;span class="o"&gt;:[&lt;/span&gt;&lt;span class="nv"&gt;article.date.month&lt;/span&gt;&lt;span class="o"&gt;]})&lt;/span&gt; -&lt;span class="cp"&gt;%}&lt;/span&gt;&lt;span class="x"&gt;&lt;/span&gt;
&lt;span class="x"&gt;  &lt;/span&gt;&lt;span class="cp"&gt;{%&lt;/span&gt;- &lt;span class="k"&gt;else&lt;/span&gt; -&lt;span class="cp"&gt;%}&lt;/span&gt;&lt;span class="x"&gt;&lt;/span&gt;
&lt;span class="x"&gt;    &lt;/span&gt;&lt;span class="cp"&gt;{%&lt;/span&gt;- &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nv"&gt;article.date.month&lt;/span&gt; &lt;span class="k"&gt;not&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="nv"&gt;years_month&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;article.date.year&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; -&lt;span class="cp"&gt;%}&lt;/span&gt;&lt;span class="x"&gt; &lt;/span&gt;
&lt;span class="x"&gt;      &lt;/span&gt;&lt;span class="cp"&gt;{%&lt;/span&gt;- &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="nv"&gt;years_month&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;article.date.year&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="nv"&gt;.append&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;article.date.month&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; -&lt;span class="cp"&gt;%}&lt;/span&gt;&lt;span class="x"&gt;&lt;/span&gt;
&lt;span class="x"&gt;    &lt;/span&gt;&lt;span class="cp"&gt;{%&lt;/span&gt;- &lt;span class="k"&gt;endif&lt;/span&gt; -&lt;span class="cp"&gt;%}&lt;/span&gt;&lt;span class="x"&gt;&lt;/span&gt;
&lt;span class="x"&gt;  &lt;/span&gt;&lt;span class="cp"&gt;{%&lt;/span&gt;- &lt;span class="k"&gt;endif&lt;/span&gt; -&lt;span class="cp"&gt;%}&lt;/span&gt;&lt;span class="x"&gt;&lt;/span&gt;
&lt;span class="cp"&gt;{%&lt;/span&gt;- &lt;span class="k"&gt;endfor&lt;/span&gt; &lt;span class="cp"&gt;%}&lt;/span&gt;&lt;span class="x"&gt;&lt;/span&gt;

&lt;span class="cp"&gt;{%&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="nv"&gt;year&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="nv"&gt;years_month&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="nf"&gt;sort&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;reverse&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kp"&gt;True&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; -&lt;span class="cp"&gt;%}&lt;/span&gt;&lt;span class="x"&gt;&lt;/span&gt;
&lt;span class="x"&gt;  &amp;lt;h2 class=&amp;quot;year&amp;quot;&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;{{&lt;/span&gt; &lt;span class="nv"&gt;year&lt;/span&gt; &lt;span class="cp"&gt;}}&lt;/span&gt;&lt;span class="x"&gt;&amp;lt;/h2&amp;gt;&amp;lt;dl&amp;gt;  &lt;/span&gt;
&lt;span class="x"&gt;  &lt;/span&gt;&lt;span class="cp"&gt;{%&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="nv"&gt;month&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="nv"&gt;years_month&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;year&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; -&lt;span class="cp"&gt;%}&lt;/span&gt;&lt;span class="x"&gt;&lt;/span&gt;
&lt;span class="x"&gt;    &amp;lt;dt class=&amp;quot;month&amp;quot;&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;{{&lt;/span&gt; &lt;span class="nv"&gt;months&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;month&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="cp"&gt;}}&lt;/span&gt;&lt;span class="x"&gt;&amp;lt;/dt&amp;gt;&lt;/span&gt;
&lt;span class="x"&gt;    &lt;/span&gt;&lt;span class="cp"&gt;{%&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="nv"&gt;article&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="nv"&gt;dates&lt;/span&gt; -&lt;span class="cp"&gt;%}&lt;/span&gt;&lt;span class="x"&gt;&lt;/span&gt;
&lt;span class="x"&gt;      &lt;/span&gt;&lt;span class="cp"&gt;{%&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nv"&gt;article.date.year&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="nv"&gt;year&lt;/span&gt; &lt;span class="k"&gt;and&lt;/span&gt; &lt;span class="nv"&gt;article.date.month&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="nv"&gt;month&lt;/span&gt; -&lt;span class="cp"&gt;%}&lt;/span&gt;&lt;span class="x"&gt;&lt;/span&gt;
&lt;span class="x"&gt;        &amp;lt;dd&amp;gt;&amp;lt;span class=&amp;quot;day&amp;quot;&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;{{&lt;/span&gt; &lt;span class="nv"&gt;article.locale_date.split&lt;/span&gt;&lt;span class="o"&gt;()[&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="cp"&gt;}}&lt;/span&gt;&lt;span class="x"&gt;&amp;lt;/span&amp;gt;  &amp;lt;a href=&amp;#39;&lt;/span&gt;&lt;span class="cp"&gt;{{&lt;/span&gt; &lt;span class="nv"&gt;article.url&lt;/span&gt; &lt;span class="cp"&gt;}}&lt;/span&gt;&lt;span class="x"&gt;&amp;#39;&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;{{&lt;/span&gt; &lt;span class="nv"&gt;article.title&lt;/span&gt; &lt;span class="cp"&gt;}}&lt;/span&gt;&lt;span class="x"&gt;&amp;lt;/a&amp;gt;&amp;lt;/dd&amp;gt;&lt;/span&gt;
&lt;span class="x"&gt;      &lt;/span&gt;&lt;span class="cp"&gt;{%&lt;/span&gt;- &lt;span class="k"&gt;endif&lt;/span&gt; &lt;span class="cp"&gt;%}&lt;/span&gt;&lt;span class="x"&gt;&lt;/span&gt;
&lt;span class="x"&gt;    &lt;/span&gt;&lt;span class="cp"&gt;{%&lt;/span&gt;- &lt;span class="k"&gt;endfor&lt;/span&gt; &lt;span class="cp"&gt;%}&lt;/span&gt;&lt;span class="x"&gt;&lt;/span&gt;
&lt;span class="x"&gt;  &lt;/span&gt;&lt;span class="cp"&gt;{%&lt;/span&gt; &lt;span class="k"&gt;endfor&lt;/span&gt; &lt;span class="cp"&gt;%}&lt;/span&gt;&lt;span class="x"&gt;&amp;lt;/dl&amp;gt;&lt;/span&gt;
&lt;span class="cp"&gt;{%&lt;/span&gt; &lt;span class="k"&gt;endfor&lt;/span&gt; &lt;span class="cp"&gt;%}&lt;/span&gt;&lt;span class="x"&gt;&lt;/span&gt;
&lt;span class="x"&gt;&amp;lt;/section&amp;gt;&lt;/span&gt;
&lt;span class="cp"&gt;{%&lt;/span&gt; &lt;span class="k"&gt;endblock&lt;/span&gt; &lt;span class="cp"&gt;%}&lt;/span&gt;&lt;span class="x"&gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Donde los artículos están archivados por año, mes y día, con un formato que 
personalmente me agrada bastante más.&lt;/p&gt;
&lt;p&gt;El repositorio de este blog se encuentra en 
&lt;a href="https://bitbucket.org/joedicastro/joedicastro.com/"&gt;https://bitbucket.org/joedicastro/joedicastro.com/&lt;/a&gt;&lt;/p&gt;</summary><category term="pelican"></category><category term="python"></category><category term="markdown"></category><category term="restructuredtext"></category><category term="blog"></category><category term="mercurial"></category><category term="hg"></category></entry><entry><title>Pelican - Configuración y personalización</title><link href="http://joedicastro.com/pelican-configuracion-y-personalizacion.html" rel="alternate"></link><updated>2011-06-30T01:21:00+02:00</updated><author><name>joe di castro</name></author><id>http://joedicastro.com/pelican-configuracion-y-personalizacion.html</id><summary type="html">&lt;p&gt;Con este articulo cierro la serie que le he dedicado a &lt;a href="http://joedicastro.com/tag/Pelican.html"&gt;Pelican&lt;/a&gt; como 
herramienta idónea para crear blogs estáticos con mantenimiento cero. Aquí voy a 
tratar de la parte más compleja de Pelican, la que nos permite personalizarlo y 
adaptarlo a nuestros gustos y necesidades. El primer nivel de personalización se 
obtiene a través del fichero de configuración (aunque no es necesario, es 
recomendable emplearlo). El segundo nivel vendría mediante la personalización del 
tema empleado, bien creando uno nuevo o bien modificando el que viene por 
defecto. El último nivel vendría de modificar el propio Pelican. Todos son 
posibles y están a nuestro alcance, gracias a que es Software Libre. &lt;/p&gt;
&lt;p&gt;Si bien yo he personalizado mi sitio con respecto al sitio por defecto, y aún 
quedan ciertas cosas que aún quiero cambiar, no he necesitado de momento más que 
modificar el fichero de configuración y el tema que viene por defecto. En 
Pelican no he tocado nada exceptuando el activar por defecto &lt;strong&gt;Markdown Extra&lt;/strong&gt;, 
algo que Alexis tuvo como gentileza &lt;a href="https://github.com/ametaireau/pelican/pull/138"&gt;aceptar como modificación&lt;/a&gt; a Pelican. Es 
posible que en el futuro algunas de las cosas que tengo en mente me lleven a 
modificarlo, y espero poder así contribuir en alguna medida a su desarrollo. &lt;/p&gt;
&lt;h2 id="configuraci+n"&gt;Configuración&lt;/h2&gt;
&lt;p&gt;La configuración se hace a través de un fichero python, que en el ejemplo por 
defecto se llama &lt;code&gt;pelican.conf.py&lt;/code&gt; y en nuestro ejemplo estaría en 
&lt;em&gt;myblog.com/site/pelican.conf.py&lt;/em&gt;. Para llamar al fichero de configuración se 
utiliza la opción &lt;code&gt;-s&lt;/code&gt; y en nuestro ejemplo sería:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="gp"&gt;$&lt;/span&gt; pelican -s ./site/pelican.conf.py
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Este es un tema que está muy bien resuelto en la &lt;a href="http://docs.notmyidea.org/alexis/pelican/settings.html"&gt;documentación&lt;/a&gt;. De todos 
modos voy a hablar de unos cuantos campos interesantes y alguno no incluido 
dentro de la documentación. Entre paréntesis indico los valores por defecto.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;OUTPUT_PATH&lt;/strong&gt; (&lt;code&gt;'output/'&lt;/code&gt;) y &lt;strong&gt;PATH&lt;/strong&gt; (&lt;code&gt;None&lt;/code&gt;)&lt;/p&gt;
&lt;p&gt;Empleando estos dos valores, conseguimos que solo tengamos que especificar el 
fichero de configuración en la linea de comandos empleada para llamar a Pelican.
El primero define el directorio de salida donde queremos que se genere nuestro 
sitio y el segundo el directorio origen de nuestro contenido. &lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;SITESUBTITLE&lt;/strong&gt; (&lt;code&gt;u''&lt;/code&gt;)&lt;/p&gt;
&lt;p&gt;Un subtitulo que se puede poner al blog, e.g. un lema. No aparece documentado.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;JINJA_EXTENSIONS&lt;/strong&gt; (&lt;code&gt;[]&lt;/code&gt;)&lt;/p&gt;
&lt;p&gt;Puede ser necesario habilitar ciertas extensiones de Jinja2 para personalizar 
el tema que vamos a emplear. Por ejemplo, para mostrar la nube de etiquetas 
con las etiquetas ordenadas alfabéticamente, he habilitado una extensión que 
necesitaba para el proceso de esta manera:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="n"&gt;JINJA_EXTENSIONS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;jinja2.ext.do&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;STATIC_PATHS&lt;/strong&gt; (&lt;code&gt;['images',]&lt;/code&gt;)&lt;/p&gt;
&lt;p&gt;Te permite especificar los contenidos estáticos que quieres añadir a tu blog 
y que acabaran colgando de la carpeta &lt;em&gt;static&lt;/em&gt;. Yo por ejemplo lo empleo 
para alojar las imágenes en &lt;em&gt;static/pictures&lt;/em&gt; y el mapa flash que utilizo en 
el articulo &lt;a href="http://joedicastro.com/combatir-el-spam-en-drupal.html"&gt;Combatir el spam en Drupal&lt;/a&gt; en &lt;em&gt;static/ammap&lt;/em&gt; así:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="n"&gt;STATIC_PATHS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;pictures&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;ammap&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;]&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;TAG_FEED&lt;/strong&gt; (&lt;code&gt;None&lt;/code&gt;) y &lt;strong&gt;TAG_FEED_RSS&lt;/strong&gt; (&lt;code&gt;None&lt;/code&gt;)&lt;/p&gt;
&lt;p&gt;Te permiten establecer fuentes Atom y RSS por etiqueta, que es una opción 
muy interesante para quienes solo les interesa un tema de los que trates o 
para formar parte de un &lt;a href="http://es.wikipedia.org/wiki/Planeta_%28agregador%29"&gt;Planet&lt;/a&gt; sin mezclar temas. Eso si, lo lógico es 
modificar luego el tema para que aparezcan disponibles. &lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;DIRECT_TEMPLATES&lt;/strong&gt; (&lt;code&gt;('index', 'tags', 'categories', 'archives')&lt;/code&gt;)&lt;/p&gt;
&lt;p&gt;Estas son las plantillas del tema que generan una página por si mismas. No 
viene documentada, pero yo he cambiado los valores por defecto para añadir 
una página personalizada a la que redirijo los errores 404 (Página no 
encontrada) de esta manera:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="n"&gt;DIRECT_TEMPLATES&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;index&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;tags&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;categories&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;archives&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;notfound&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;PIWIK_URL&lt;/strong&gt; y &lt;strong&gt;PIWIK_SITE_ID&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Te permiten configurar tu sitio para emplear el magnifico sistema de 
analíticas web &lt;a href="http://piwik.org/"&gt;Piwik&lt;/a&gt;. Yo hace unos dos años que lo empleo en mis sitios, 
porque tiene la ventaja de que no dependes de un script de un dominio externo. 
Esto te ahorra consultas DNS y que la página se quede esperando a cargar 
completamente cuando estos fallan. De esta manera tienes el control total 
sobre las estadísticas de tu web sin tener que renunciar a la calidad de 
otros servicios externos.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;FILES_TO_COPY&lt;/strong&gt; (&lt;code&gt;()&lt;/code&gt;)&lt;/p&gt;
&lt;p&gt;Básica si queremos emplear ficheros como &lt;em&gt;.htaccess&lt;/em&gt; o &lt;em&gt;robots.txt&lt;/em&gt;. No está 
documentada tampoco. El uso de la misma consiste en una tupla de tuplas con 
los valores de las rutas del fichero origen y su destino. En este sitio por 
ejemplo:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="n"&gt;FILES_TO_COPY&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;extra/robots.txt&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;robots.txt&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
                 &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;extra/favicon.ico&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;favicon.ico&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
                 &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;extra/.htaccess&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;.htaccess&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;),)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="personalizaci+n"&gt;Personalización&lt;/h2&gt;
&lt;p&gt;Pero si realmente queremos personalizar el sitio, tanto en funcionalidad básica 
como en su aspecto, nos toca modificar el tema por defecto. Aquí podemos optar por 
tres vías: Crear el nuestro propio, empezar un tema casi desde cero, 
eligiendo el tema por defecto &lt;code&gt;simple&lt;/code&gt; (solo texto) o partir de la base del tema 
por defecto &lt;code&gt;notmyidea&lt;/code&gt;. Este último es el que hemos estado empleado en nuestro 
ejemplo y el que yo modifiqué hasta llegar donde lo tengo ahora. &lt;/p&gt;
&lt;p&gt;Para comprender de que consta un tema y como podemos personalizarlo, lo mejor es 
analizar el tema &lt;code&gt;notmyidea&lt;/code&gt;.&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="go"&gt;notmyidea/&lt;/span&gt;
&lt;span class="go"&gt;├── static&lt;/span&gt;
&lt;span class="go"&gt;│   ├── css&lt;/span&gt;
&lt;span class="go"&gt;│   │   ├── main.css&lt;/span&gt;
&lt;span class="go"&gt;│   │   ├── pygment.css&lt;/span&gt;
&lt;span class="go"&gt;│   │   ├── reset.css&lt;/span&gt;
&lt;span class="go"&gt;│   │   └── wide.css&lt;/span&gt;
&lt;span class="go"&gt;│   └── images&lt;/span&gt;
&lt;span class="go"&gt;│       └── icons&lt;/span&gt;
&lt;span class="go"&gt;│           ├── delicious.png&lt;/span&gt;
&lt;span class="go"&gt;│           ├── lastfm.png&lt;/span&gt;
&lt;span class="go"&gt;│           ├── linkedin.png&lt;/span&gt;
&lt;span class="go"&gt;│           ├── rss.png&lt;/span&gt;
&lt;span class="go"&gt;│           └── twitter.png&lt;/span&gt;
&lt;span class="go"&gt;└── templates&lt;/span&gt;
&lt;span class="go"&gt;    ├── analytics.html&lt;/span&gt;
&lt;span class="go"&gt;    ├── archives.html&lt;/span&gt;
&lt;span class="go"&gt;    ├── article.html&lt;/span&gt;
&lt;span class="go"&gt;    ├── article_infos.html&lt;/span&gt;
&lt;span class="go"&gt;    ├── base.html&lt;/span&gt;
&lt;span class="go"&gt;    ├── categories.html&lt;/span&gt;
&lt;span class="go"&gt;    ├── category.html&lt;/span&gt;
&lt;span class="go"&gt;    ├── comments.html&lt;/span&gt;
&lt;span class="go"&gt;    ├── disqus_script.html&lt;/span&gt;
&lt;span class="go"&gt;    ├── github.html&lt;/span&gt;
&lt;span class="go"&gt;    ├── index.html&lt;/span&gt;
&lt;span class="go"&gt;    ├── page.html&lt;/span&gt;
&lt;span class="go"&gt;    ├── pagination.html&lt;/span&gt;
&lt;span class="go"&gt;    ├── piwik.html&lt;/span&gt;
&lt;span class="go"&gt;    ├── skribit_tab_script.html&lt;/span&gt;
&lt;span class="go"&gt;    ├── skribit_widget_script.html&lt;/span&gt;
&lt;span class="go"&gt;    ├── tag.html&lt;/span&gt;
&lt;span class="go"&gt;    ├── taglist.html&lt;/span&gt;
&lt;span class="go"&gt;    ├── tags.html&lt;/span&gt;
&lt;span class="go"&gt;    ├── translations.html&lt;/span&gt;
&lt;span class="go"&gt;    └── twitter.html&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;&lt;em&gt;static/css&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Aquí es donde se guardan las &lt;a href="http://es.wikipedia.org/wiki/CSS"&gt;Hojas de estilo en cascada (CSS)&lt;/a&gt; que emplea el 
tema. Aunque la el fichero &lt;em&gt;wide.css&lt;/em&gt; no es empleado por el tema, solo para la 
versión wide del mismo. Yo la eliminé como primera medida. Luego tenemos 
&lt;em&gt;main.css&lt;/em&gt; que es la hoja de estilo principal, &lt;em&gt;pygment.css&lt;/em&gt; que es la que emplea 
Pygments para el resaltado de sintaxis y &lt;em&gt;reset.css&lt;/em&gt; para resetear los navegadores 
a unos valores comunes por defecto. &lt;/p&gt;
&lt;p&gt;Si lo que queremos es tener un sitio con un buen rendimiento, deberíamos dar 
como mínimo dos pasos, consolidar todas la hojas de estilo en una sola y 
minimizar el tamaño de esta. Consolidarla en este caso es realmente sencillo, 
simplemente debemos insertar el contenido de &lt;em&gt;reset.css&lt;/em&gt; al principio del 
fichero &lt;em&gt;main.css&lt;/em&gt; y &lt;em&gt;pygment.css&lt;/em&gt; al final del mismo. Depués hay que 
eliminar los imports de estas hojas en &lt;em&gt;main.css&lt;/em&gt;*. Luego para minimizar 
su tamaño eliminar comentarios y lineas en blanco y poner una regla por línea. &lt;/p&gt;
&lt;p&gt;Otro paso que dí para mejorar el rendimiento es no usar las web fonts 
externas (ni internas), que aunque le dan un aspecto inmejorable, hay que 
reconocerlo, requieren cargar otro recurso externo, desde mi punto de vista 
innecesario.&lt;/p&gt;
&lt;p&gt;Lo mejor en estos casos es seguir siempre las recomendaciones de estas dos 
magnificas herramientas: &lt;a href="http://developer.yahoo.com/yslow/"&gt;YSlow&lt;/a&gt; de &lt;strong&gt;Yahoo&lt;/strong&gt; y &lt;a href="http://code.google.com/speed/page-speed/"&gt;Page Speed&lt;/a&gt; de 
&lt;strong&gt;Google&lt;/strong&gt;. Afortunadamente hay sitios web como &lt;a href="http://gtmetrix.com/"&gt;GTmetrix&lt;/a&gt; o 
&lt;a href="http://www.webpagetest.org/"&gt;WebPagetest&lt;/a&gt; que nos permiten ver los resultados de ambas herramientas 
en nuestro sitio web sin necesidad de instalarlas (aunque para el desarrollo 
es más que recomendable). Si por ejemplo emplearamos &lt;strong&gt;GTmetrix&lt;/strong&gt; los valores 
que arroja un sitio con el tema por defecto son:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Page Speed (tema por defecto)&lt;/th&gt;
&lt;th&gt;YSlow (tema por defecto)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;B (80%)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;B (85%)&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Valores bastante mejorables. Los valores actuales para este sitio, una vez 
personalizado el tema y configurado correctamente el &lt;em&gt;.htaccess&lt;/em&gt; son:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Page Speed (tema personalizado)&lt;/th&gt;
&lt;th&gt;YSlow (tema personalizado)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;A (91%)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;A (95%)&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Y si nos fijamos en &lt;a href="http://gtmetrix.com/reports/joedicastro.com/BeFSzvAa"&gt;los resultados&lt;/a&gt;, las penalizaciones de rendimiento 
vienen de los scrips externos de Piwik y Disqus, principalmente de este 
último. Sin emplear estos scripts los valores suben a:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Page Speed (sin scripts)&lt;/th&gt;
&lt;th&gt;YSlow (sin scripts)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;A (100%)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;A (98%)&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;En resumen, una optimización más que correcta, solo hay que ver los valores 
de las páginas del Top 1000 que ofrecen en GTmetrix. Obtener una optimización 
como esta con un CMS no es imposible (había conseguido unos valores solo un 
poquito peores con Drupal) pero si bastante más complicado. La experiencia 
del usuario depende mucho de estos valores, hay que contar con que no todo 
el mundo dispone de buenos accesos a Internet.&lt;/p&gt;
&lt;p&gt;Evidentemente, después cada uno retocara el fichero CSS resultante a su 
gusto para personalizar el aspecto del sitio. En este caso estoy empleando
CSS 3 y valida correctamente. &lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p style="text-align:center;"&gt;
    &lt;a href="http://jigsaw.w3.org/css-validator/validator?uri=http%3A%2F%2Fjoedicastro.com%2F&amp;amp;profile=css3&amp;amp;usermedium=all&amp;amp;warning=1&amp;amp;vextwarning=&amp;amp;lang=es"&gt;
        &lt;img style="border:0;width:88px;height:31px"
            src="pictures/valid-css3.png"
            alt="¡CSS 3 Válido!" title="¡CSS 3 Válido!" /&gt;
    &lt;/a&gt;
&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;static/images/icons&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Son los iconos empleados por la hoja de estilo para los enlaces sociales de 
la parte inferior de la página. Yo no los empleo. ¿Entonces como se ven? 
Bueno lo primero que hice fue crear un &lt;a href="http://www.w3schools.com/css/css_image_sprites.asp"&gt;sprite&lt;/a&gt; para cargar una sola 
imagen en lugar de 6, es decir menos solicitudes al servidor. Después 
incorporé esta imagen &lt;a href="http://www.websiteoptimization.com/speed/tweak/inline-images/"&gt;dentro del propio fichero CSS con un Data URL&lt;/a&gt;, y 
gracias a haberla optimizado hasta ocupar solamente ~3k, me compensaba 
hacerlo. Una consulta menos al servidor. Lo mismo hice después con el 
logotipo (1K) y al final no se carga ninguna imagen por defecto en el blog, 
0 consultas. Los navegadores modernos soportan esto sin problemas, los que 
no lo hacen, no muestran la imagen, solo el enlace, que tampoco es un ningún 
problema.&lt;/p&gt;
&lt;p&gt;Así es como queda en &lt;em&gt;main.css&lt;/em&gt; la imagen que da vida a los iconos de los 
enlaces a la redes sociales:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="nf"&gt;#extras&lt;/span&gt; &lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nt"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;social&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="nt"&gt;a&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="k"&gt;background-repeat&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="k"&gt;no-repeat&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="k"&gt;background-image&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="sx"&gt;url(&amp;quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAACSCAMAAAB7VbqAAAAAwFBMVEWQBQCWFxU5VpedVy+tUE5LZaBPZZZDabDdYSpke75vfZBkfbJZgc+heztshKvldjBgld91j9+Uj4p5ksZ/lLzthzLqhz+MmrAcvuaDofihpYRzq/D4mDeUp9KetkSirsnQpKWpvV/qqHvHsJjnqY/3qWK1ubbIun2jwLHEu7xm1O+6xoOtv++7wte0yNzxwH563e/3xJjK06ya5vXY3p7U2+3e3tbm6IT63cLn7czr7fbZ9Prz7+r8+O/3+fb9//z6WVfkAAADNElEQVRIx9WV4XqiPBCFoyu4aVjQBrW2ImBX3aJYdQERIbn/u9qTRKzd7/n+b88jxHlnMswEY4hIGYUcLZYKklLbtixqNBymhNl2yG/2lBFqu1LKKjD2dEioxfIKKOfaVgA+HkoptsoGoDxPA8orKZTtEIdyNWE4zXEbUgVokAq4p1JuqQHIX8G9lYK2EdOpkNuhlJxec+TTrcxpJVENAOK3iK9oLkOLEmbqGQZ4dMAtRlJj617QZErQvqpHiVLVvvhL/y6oFx40HhktaqJt9X2mtSDwHg7L1p6NAJZY9eZwB8aHsgF61zaAnr8spXxX9piMR8umxAQQZBp5ZDTSOZYgzXjkKTCbvTeKNPLgKaCTK/dCNldQlu9wL0aNHH9XADnK2UGWXil/GoDJyFx6P69gNlPPWy698cIDWOj68DzvO+Qt0D7MsXcV2v86by55+qSEPP3+pKf/ARfxF5Dy8gH2+/0N7Pc3oCUMSJIWyESBRANxgX4nyQ1cEiMDEkR8BheT4jYl2bdJW5DsdczlA9z09N/2v86bw1Vs4vl8c6xbcI7ncQxkCBFFPM8a0QAfDTjOM70453lc630bx2ezXJt5oUARx40BmQF3EfFroXJs5gaU30inF+G3vombBnuu7JFer9OJ1JQMZcQ/SK8WEekRVRYqjb+RSMq60yHF8VzXRVHAWUsV0TZVIEekctzafEPKTnS/o+qog7z361FHkfhK/5ZV0MrnPAgqEvBWYZoGPCSt6aujLGScuC5j+LAdNkAVALCrUuVndwARvhoJs3C3WSXUFB8HjtW1LFxCvz3WtQC03Bw5fOsDdEPl794BPMW9Ap3DAMuyCc5nJQ2sO2AB+BgYCU1dCkwwhjjnfNUO8ycTl/lf6pxTtzzfQXl1BduXAQ5/7jiDl50CgcMnq6Ku69WEOQHAgD8+Pq5WbysMLle78tdq8jDR4oMc4HzKsuPuBRoMLKHB6+spG2hgrwCydQZpQH0NTmsgB/4BTVe1AphyNCBXoMk2p9MvB7bT1xEitB+eFXCch3WhSl/Z/T6dcMddr581KAD6fdd9fnyoTbdvrN93+v2uXbTt17sQyvV6/AE7UZJYs2UOngAAAABJRU5ErkJggg==&amp;quot;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="k"&gt;padding-left&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="m"&gt;25px&lt;/span&gt;&lt;span class="p"&gt;;}&lt;/span&gt;
&lt;span class="nc"&gt;.social&lt;/span&gt; &lt;span class="nt"&gt;a&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nt"&gt;href&lt;/span&gt;&lt;span class="o"&gt;*=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;atom.xml&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="k"&gt;background-position&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="m"&gt;3px&lt;/span&gt; &lt;span class="m"&gt;6px&lt;/span&gt;&lt;span class="p"&gt;;}&lt;/span&gt;
&lt;span class="nc"&gt;.social&lt;/span&gt; &lt;span class="nt"&gt;a&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nt"&gt;href&lt;/span&gt;&lt;span class="o"&gt;*=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;rss.xml&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="k"&gt;background-position&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="m"&gt;3px&lt;/span&gt; &lt;span class="m"&gt;-20px&lt;/span&gt;&lt;span class="p"&gt;;}&lt;/span&gt;
&lt;span class="nc"&gt;.social&lt;/span&gt; &lt;span class="nt"&gt;a&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nt"&gt;href&lt;/span&gt;&lt;span class="o"&gt;*=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;twitter.com&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="k"&gt;background-position&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="m"&gt;3px&lt;/span&gt; &lt;span class="m"&gt;-46px&lt;/span&gt;&lt;span class="p"&gt;;}&lt;/span&gt;
&lt;span class="nc"&gt;.social&lt;/span&gt; &lt;span class="nt"&gt;a&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nt"&gt;href&lt;/span&gt;&lt;span class="o"&gt;*=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;identi.ca&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="k"&gt;background-position&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="m"&gt;3px&lt;/span&gt; &lt;span class="m"&gt;-72px&lt;/span&gt;&lt;span class="p"&gt;;}&lt;/span&gt;
&lt;span class="nc"&gt;.social&lt;/span&gt; &lt;span class="nt"&gt;a&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nt"&gt;href&lt;/span&gt;&lt;span class="o"&gt;*=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;facebook.com&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="k"&gt;background-position&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="m"&gt;3px&lt;/span&gt; &lt;span class="m"&gt;-98px&lt;/span&gt;&lt;span class="p"&gt;;}&lt;/span&gt;
&lt;span class="nc"&gt;.social&lt;/span&gt; &lt;span class="nt"&gt;a&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nt"&gt;href&lt;/span&gt;&lt;span class="o"&gt;*=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;bitbucket.org&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="k"&gt;background-position&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="m"&gt;3px&lt;/span&gt; &lt;span class="m"&gt;-124px&lt;/span&gt;&lt;span class="p"&gt;;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;static/templates&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Estas son las plantillas que dan vida al tema y emplean Jinja2. Los nombres 
son bastante descriptivos de la función que realiza cada una de ellas. En mi 
caso hay una serie de ellas que no empleo, como son: &lt;em&gt;analytics.html&lt;/em&gt;, 
&lt;em&gt;article_infos.html&lt;/em&gt;, &lt;em&gt;github.html&lt;/em&gt;, &lt;em&gt;skribit_tab_script.html&lt;/em&gt;, 
&lt;em&gt;skribit_widget_script.html&lt;/em&gt;, &lt;em&gt;translations.html&lt;/em&gt; y &lt;em&gt;twitter.html&lt;/em&gt;. He 
realizado bastantes modificaciones en las plantillas y me extendería demasiado 
explicándolo, además sigo realizando cambios. Aún no lo tengo todo como yo 
lo quiero, y aún voy a personalizar y optimizar más cosas. Cuando lo tenga 
todo listo, pienso publicar un repositorio con el contenido que genera este 
blog, tanto a efectos de copia de seguridad, como de que le pueda servir a 
alguien como guía para montar el suyo propio. &lt;/p&gt;
&lt;p&gt;De todos modos si puedo decir que he añadido una que no existía, 
&lt;em&gt;notfound.html&lt;/em&gt; para los errores 404 y pienso crear una nueva para los 
errores 403. Además he creado los enlaces para los feeds RSS y Atom de las 
etiquetas y eliminado el bloque de información que aparecía en cada articulo.
Además en la plantilla &lt;em&gt;tags.html&lt;/em&gt; que por defecto viene vacía, he creado lo 
necesario para generar la nube de etiquetas. &lt;em&gt;tags.html&lt;/em&gt; queda entonces así:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="cp"&gt;{%&lt;/span&gt; &lt;span class="k"&gt;extends&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;base.html&amp;quot;&lt;/span&gt; &lt;span class="cp"&gt;%}&lt;/span&gt;
&lt;span class="cp"&gt;{%&lt;/span&gt; &lt;span class="k"&gt;block&lt;/span&gt; &lt;span class="nv"&gt;content&lt;/span&gt; &lt;span class="cp"&gt;%}&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;section&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;content&amp;quot;&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;body&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;h1&amp;gt;&lt;/span&gt;etiquetas&lt;span class="nt"&gt;&amp;lt;/h1&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;ul&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;cloud&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="cp"&gt;{%&lt;/span&gt;- &lt;span class="k"&gt;set&lt;/span&gt; &lt;span class="nv"&gt;all_tags&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;{}&lt;/span&gt; -&lt;span class="cp"&gt;%}&lt;/span&gt;
    &lt;span class="cp"&gt;{%&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="nv"&gt;ctag&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="nv"&gt;tag_cloud&lt;/span&gt; -&lt;span class="cp"&gt;%}&lt;/span&gt;
      &lt;span class="cp"&gt;{%&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="nv"&gt;all_tags.update&lt;/span&gt;&lt;span class="o"&gt;({&lt;/span&gt;&lt;span class="nv"&gt;ctag.0&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="nv"&gt;ctag.1&lt;/span&gt;&lt;span class="o"&gt;})&lt;/span&gt; &lt;span class="cp"&gt;%}&lt;/span&gt;
    &lt;span class="cp"&gt;{%&lt;/span&gt;- &lt;span class="k"&gt;endfor&lt;/span&gt; &lt;span class="cp"&gt;%}&lt;/span&gt;
    &lt;span class="cp"&gt;{%&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="nv"&gt;tag&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="nv"&gt;all_tags&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="nf"&gt;sort&lt;/span&gt; &lt;span class="cp"&gt;%}&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;li&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;tag-&lt;/span&gt;&lt;span class="cp"&gt;{{&lt;/span&gt; &lt;span class="nv"&gt;all_tags&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;tag&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="cp"&gt;}}&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;a&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;tag/&lt;/span&gt;&lt;span class="cp"&gt;{{&lt;/span&gt; &lt;span class="nv"&gt;tag&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot; &amp;quot;&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;%20&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="cp"&gt;}}&lt;/span&gt;&lt;span class="s"&gt;.html&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;{{&lt;/span&gt; &lt;span class="nv"&gt;tag&lt;/span&gt; &lt;span class="cp"&gt;}}&lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;&lt;/span&gt;
    &lt;span class="cp"&gt;{%&lt;/span&gt; &lt;span class="k"&gt;endfor&lt;/span&gt; &lt;span class="cp"&gt;%}&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/ul&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/section&amp;gt;&lt;/span&gt;
&lt;span class="cp"&gt;{%&lt;/span&gt; &lt;span class="k"&gt;endblock&lt;/span&gt; &lt;span class="cp"&gt;%}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;También he realizado las modificaciones pertinentes para que el contenido 
validara perfectamente en HTML 5. &lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p style="text-align:center;"&gt;
    &lt;a href="http://validator.w3.org/check?uri=http%3A%2F%2Fjoedicastro.com%2F"&gt;
        &lt;img style="border:0;width:88px;height:31px"
            src="pictures/valid-html5.png"
            alt="¡HTML 5 Válido!" title="¡HTML 5 Válido!" /&gt;
    &lt;/a&gt;
&lt;/p&gt;

&lt;p&gt;Pero como ya he dicho es un proceso que aún no he finalizado. Cuando lo 
termine y publique el repositorio, actualizaré también este articulo.&lt;/p&gt;</summary><category term="pelican"></category><category term="python"></category><category term="markdown"></category><category term="restructuredtext"></category><category term="blog"></category><category term="html"></category><category term="css"></category><category term="jinja2"></category></entry><entry><title>Pelican - Publicación y automatización</title><link href="http://joedicastro.com/pelican-publicacion-y-automatizacion.html" rel="alternate"></link><updated>2011-06-28T23:54:00+02:00</updated><author><name>joe di castro</name></author><id>http://joedicastro.com/pelican-publicacion-y-automatizacion.html</id><summary type="html">&lt;p&gt;Una vez que sabemos como &lt;a href="http://joedicastro.com/pelican-introduccion-e-instalacion.html"&gt;instalar Pelican&lt;/a&gt; y &lt;a href="http://joedicastro.com/pelican-creacion-de-contenido.html"&gt;crear contenido&lt;/a&gt; con él, es 
hora de saber como convertir ese contenido en un blog real disponible en 
internet. Es decir, saber como publicar ese contenido. Como hemos visto hasta 
ahora, al constar básicamente de simples ficheros HTML, un servidor de 
archivos es más que suficiente para servir el blog. Esto nos abre un gran 
abanico de posibilidades, desde emplear un potente (y barato) servidor de 
ficheros en la &lt;em&gt;nube&lt;/em&gt; como &lt;strong&gt;Amazon S3&lt;/strong&gt; (&lt;a href="http://jdacpelican.s3-website-us-east-1.amazonaws.com/"&gt;un ejemplo&lt;/a&gt;), pasando por las 
páginas web estáticas que nos permiten repositorios como &lt;strong&gt;Bitbucket&lt;/strong&gt; o 
&lt;strong&gt;GitHub&lt;/strong&gt; (&lt;a href="http://marsam.github.com/blog/"&gt;un ejemplo&lt;/a&gt;), por los tradiciones hostings compartidos (e.g. 
este blog), hasta un servidor casero sencillo montado sobre un &lt;a href="http://es.wikipedia.org/wiki/Network-attached_storage"&gt;NAS&lt;/a&gt;. &lt;/p&gt;
&lt;p&gt;Aún cuando es posible emplear un simple servidor de archivos para alojar el blog, 
siempre es mejor contar con un servidor web detrás (Apache, nginx, lighttpd, 
...) que nos permita hacer redirecciones para nuestras antiguas páginas si ya 
disponíamos de un blog anterior o manejar los errores HTTP &lt;a href="http://es.wikipedia.org/wiki/Error_404"&gt;404&lt;/a&gt; o &lt;a href="http://es.wikipedia.org/wiki/Anexo:C%C3%B3digos_de_estado_HTTP"&gt;403&lt;/a&gt; 
de forma personalizada. &lt;/p&gt;
&lt;h2 id="publicar_el_contenido"&gt;Publicar el contenido&lt;/h2&gt;
&lt;p&gt;Publicar el contenido de una web es tan sencillo como volcar el contenido del 
directorio que nos genera Pelican (en nuestro ejemplo sería 
&lt;em&gt;myblog.com/site/output/*&lt;/em&gt;) en el directorio destino de nuestro servidor web. 
Dependiendo del método que hayamos elegido para alojar nuestro blog, puede ser 
tan sencillo como una copia de archivos o emplear FTP (SFTP) ó &lt;a href="http://es.wikipedia.org/wiki/SSH"&gt;SSH&lt;/a&gt; (&lt;a href="http://es.wikipedia.org/wiki/SCP"&gt;SCP&lt;/a&gt;, 
&lt;a href="http://en.wikipedia.org/wiki/Unison_%28file_synchronizer%29"&gt;Unison&lt;/a&gt; ó &lt;a href="http://es.wikipedia.org/wiki/Rsync"&gt;rsync&lt;/a&gt;). Aquí el tema radica no en la primera vez que 
vayamos a subir el contenido al servidor, si no en las sucesivas, a medida que 
vayamos creando contenido nuevo. No tendría ningún sentido volver a subir todo 
el contenido cada vez, si no solamente el nuevo o el que haya cambiado. Para eso 
necesitamos sincronizar los dos directorios. &lt;/p&gt;
&lt;p&gt;Si solamente disponemos de acceso FTP (o SFTP) a nuestro servidor, entonces 
tendremos que emplear una herramienta que nos permita la sincronización sobre 
FTP, como puede ser &lt;strong&gt;lftp&lt;/strong&gt;. Y el proceso se puede automatizar con un script 
como el que describo en &lt;a href="http://joedicastro.com/sincronizar-una-carpeta-local-y-una-remota-a-traves-de-ftp-lftp-mirror.html"&gt;Sincronizar una carpeta local y una remota a través de 
FTP: lftp-mirror&lt;/a&gt;. Si disponemos de acceso a través de SSH, entonces la 
elección es clarisima, &lt;strong&gt;rsync&lt;/strong&gt;. Más adelante explico una manera de emplearlo 
de forma automática.&lt;/p&gt;
&lt;p&gt;Cualquiera de ambas soluciones nos permite subir el contenido en apenas segundos, 
(sobre todo en el caso de rsync) cuando se trata de añadir un articulo nuevo, 
por ejemplo. Y lo mismo a la hora de hacer una rectificación, es tan inmediato 
como lo pueden ser plataformas como Wordpress, Drupal y similares. Además, con 
las potentes herramientas que existen para hacer cambios múltiples en varios 
ficheros de texto a la vez, se pueden realizar tareas casi imposibles con una 
plataforma de blogs tradicional sin recurrir a consultas SQL en la BDD o a 
plugins externos. Una de estas herramientas, sin recurrir a &lt;code&gt;find&lt;/code&gt;, &lt;code&gt;grep&lt;/code&gt;, 
&lt;code&gt;awk&lt;/code&gt; y &lt;code&gt;sed&lt;/code&gt;, puede ser &lt;a href="http://regexxer.sourceforge.net/"&gt;regexxer&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id="generar_el_contenido_en_el_propio_servidor"&gt;Generar el contenido en el propio servidor&lt;/h2&gt;
&lt;p&gt;Si el alojamiento que hemos escogido nos permite instalar programas python, 
entonces tenemos la posibilidad de instalar Pelican en el servidor remoto. De 
esta formar podríamos subir únicamente los archivos markdown o reStructuredText 
al servidor y generar allí mismo el contenido web. De este modo la cantidad de 
datos a subir sería ridícula y un simple comando FTP nos serviría. Luego bien 
podríamos lanzar Pelican a través de una consola SSH o bien dependiendo del 
servidor, tener un &lt;a href="http://es.wikipedia.org/wiki/Demonio_%28inform%C3%A1tica%29"&gt;demonio&lt;/a&gt; corriendo que cuando detectara un cambio en el 
sistema de ficheros lanzará un script que generara el contenido con Pelican. &lt;/p&gt;
&lt;p&gt;Otra posibilidad que me convence más, es de la poder instalar un repositorio en 
el servidor con un software de control de versiones, como Git o Mercurial. La 
idea sería tener un repositorio local, y al hacer un push hacia el repositorio 
remoto, a través de un &lt;em&gt;hook&lt;/em&gt; activar la generación de la página con Pelican. 
Esto nos permitiría además poder tener varias copias del repositorio (por 
ejemplo en GitHub o Bitbucket) y por lo tanto de la web, haciendo "innecesarias" 
las copias de seguridad. &lt;/p&gt;
&lt;h2 id="automatizar_todos_los_procesos"&gt;Automatizar todos los procesos&lt;/h2&gt;
&lt;p&gt;Pero lo ideal es poder automatizar todas las tareas que hemos visto hasta ahora, 
empleando unos pocos comandos para realizarlas sin esfuerzo alguno (bueno, 
a menos que tengas tú &lt;a href="http://es.wikipedia.org/wiki/Negro_%28escritor%29"&gt;ghostwriter&lt;/a&gt; particular, me temo que los artículos los 
seguirás teniendo que escribir tú). Para poder realizar esto disponemos de la 
fantástica y potente herramienta &lt;a href="http://fabfile.org"&gt;Fabric&lt;/a&gt; (el &lt;a href="http://en.wikipedia.org/wiki/Capistrano"&gt;Capistrano&lt;/a&gt; para Python) 
que nos permite ejecutar comandos locales o remotos en múltiples servidores. Esto 
nos permite hacer despliegues de software sin apenas esfuerzo en distintas 
máquinas, copiar ficheros o ejecutar tareas repetitivas empleado una corta 
serie de comandos. Una grandísima herramienta para administradores de sistema y 
desarrolladores.&lt;/p&gt;
&lt;p&gt;Lo único que necesitamos es instalar &lt;strong&gt;fabric&lt;/strong&gt; y crear un fichero llamado 
&lt;code&gt;fabfile.py&lt;/code&gt; donde especificaremos las tareas que queremos programar. Para 
instalar la última versión estable de fabric, lo mejor es emplear &lt;code&gt;easy_install&lt;/code&gt; 
o &lt;code&gt;pip&lt;/code&gt;&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="gp"&gt;$&lt;/span&gt; pip install fabric
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Una vez creado el fichero &lt;code&gt;fabfile.py&lt;/code&gt;, lo único que tendremos que hacer para 
ejecutar una tarea del mismo, sería escribir el comando &lt;code&gt;fab&lt;/code&gt; seguido del nombre 
que le hayamos dado a la tarea (este sería el funcionamiento básico). Y la tarea 
se ejecutaría inmediatamente. &lt;/p&gt;
&lt;p&gt;Para comprender mejor como funciona Fabric, muestro aquí el contenido actual de 
mi fichero &lt;code&gt;fabfile.py&lt;/code&gt;&lt;br /&gt;
&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="c"&gt;#!/usr/bin/env python&lt;/span&gt;
&lt;span class="c"&gt;# -*- coding: utf8 -*-&lt;/span&gt;

&lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="sd"&gt;    fabfile.py: A fabric script for generate my personal blog&lt;/span&gt;
&lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;

&lt;span class="n"&gt;__author__&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;joe di castro &amp;lt;joe@joedicastro.com&amp;gt;&amp;quot;&lt;/span&gt;
&lt;span class="n"&gt;__license__&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;GNU General Public License version 3&amp;quot;&lt;/span&gt;
&lt;span class="n"&gt;__date__&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;28/06/2011&amp;quot;&lt;/span&gt;
&lt;span class="n"&gt;__version__&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;0.2&amp;quot;&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;os&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;fabric.api&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;fabric.contrib.project&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;rsync_project&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;fabric.contrib.console&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;confirm&lt;/span&gt;

&lt;span class="n"&gt;PELICAN_REPOSITORY&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;git://github.com/ametaireau/pelican.git&amp;quot;&lt;/span&gt;
&lt;span class="n"&gt;PROD&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;joedicastro.com&amp;quot;&lt;/span&gt;
&lt;span class="n"&gt;PROD_PATH&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;/home/joedicastro/webapps/joedicastro&amp;quot;&lt;/span&gt;
&lt;span class="n"&gt;LOCAL_WEB&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;~/www&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;PROD&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;ROOT_PATH&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;abspath&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dirname&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;__file__&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="n"&gt;ENV_PATH&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ROOT_PATH&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;env&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;PELICAN&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ROOT_PATH&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;pelican&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;CONFIG_FILE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ROOT_PATH&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;site/pelican.conf.py&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;OUTPUT&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ROOT_PATH&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;site/output&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;_valid_HTML&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;Remove the obsolete rel=&amp;quot;&amp;quot; and rev=&amp;quot;&amp;quot; links in footnotes.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;dirs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;files&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;walk&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;OUTPUT&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;fil&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;files&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;fil&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;:]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;.html&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="n"&gt;local&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;sed -i {0} -r -e &amp;#39;s/re[l|v]=&lt;/span&gt;&lt;span class="se"&gt;\&amp;quot;&lt;/span&gt;&lt;span class="s"&gt;footnote&lt;/span&gt;&lt;span class="se"&gt;\&amp;quot;&lt;/span&gt;&lt;span class="s"&gt;//g&amp;#39; {0}&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;
                      &lt;span class="n"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;fil&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot; &amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;r&amp;quot;\ &amp;quot;&lt;/span&gt;&lt;span class="p"&gt;))))&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;_make_env&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;Make a virtual enviroment&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
    &lt;span class="n"&gt;local&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;virtualenv {0}&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ENV_PATH&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;_del_env&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;Delete a virtual enviroment.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
    &lt;span class="n"&gt;local&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;rm -rf {0}&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ENV_PATH&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;_clone_pelican&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;Clone Pelican from repository.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
    &lt;span class="n"&gt;local&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;git clone {0}&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;PELICAN_REPOSITORY&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;_install&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;Install Pelican in the virtual enviroment.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
    &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;lcd&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;PELICAN&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;local&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;{0}/bin/python setup.py install&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ENV_PATH&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;_browse&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;Browse the local Apache site.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
    &lt;span class="n"&gt;local&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;firefox -new-window http://localhost/joedicastro.com 2&amp;gt;/dev/null &amp;amp;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;_gen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;autoreload&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;Generate the site from source.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
    &lt;span class="n"&gt;local&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;{0}/bin/pelican {2} -s {1}&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ENV_PATH&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;CONFIG_FILE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                                              &lt;span class="s"&gt;&amp;quot;-r&amp;quot;&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;autoreload&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;_clean&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="s"&gt;&amp;quot;Remove the output folder.&amp;quot;&lt;/span&gt;
    &lt;span class="n"&gt;local&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;rm -rf {0}&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;OUTPUT&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;_local_deploy&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;Deploy to the local apache web server.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
    &lt;span class="n"&gt;local&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;rm  -rf {0}&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;LOCAL_WEB&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="n"&gt;local&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;cp -r {0} {1}&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;OUTPUT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;LOCAL_WEB&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;pull_pelican&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;Update Pelican to last revision from repository.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
    &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;lcd&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;PELICAN&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;local&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;git pull&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;bootstrap&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;Get Pelican and install it in a virtual enviroment.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
    &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;settings&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;warn_only&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;_del_env&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;_make_env&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;settings&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;warn_only&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;_clone_pelican&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;_install&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;regen&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;Regenerate the site from source.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
    &lt;span class="n"&gt;_clean&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;_gen&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;_valid_HTML&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;_local_deploy&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="nd"&gt;@hosts&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;my_user@&amp;quot;&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;PROD&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;publish&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;Publish into remote web server with rsync.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
    &lt;span class="n"&gt;regen&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;_browse&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;confirm&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;¿Estas seguro de querer publicarlo?&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;rsync_project&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;PROD_PATH&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;OUTPUT&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;/&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;delete&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;Create a new blog article.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
    &lt;span class="n"&gt;local&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;gedit --new-window {0}/site/source/blog/{1}.md 2&amp;gt;/dev/null &amp;amp;&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;
          &lt;span class="n"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ROOT_PATH&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot; &amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;\ &amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
    &lt;span class="n"&gt;local&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;firefox --new-window {0}/index.html &amp;amp;&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;OUTPUT&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="n"&gt;_gen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;img4web&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;delete&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;source&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;Optimize .jpg &amp;amp; .png images and copy them into source pictures dir.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
    &lt;span class="n"&gt;local&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;./img4web.py -d {0} {1} {2}&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;
          &lt;span class="n"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ROOT_PATH&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;site/source/pictures&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
                 &lt;span class="s"&gt;&amp;quot;--delete&amp;quot;&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;delete&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                 &lt;span class="s"&gt;&amp;quot;-s {0}&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;source&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;source&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Ahora, veremos el funcionamiento básico que nos permite este script. Primero 
vemos las tareas que tenemos disponibles:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="gp"&gt;$&lt;/span&gt; fab -l
&lt;span class="go"&gt;    fabfile.py: A fabric script for generate my personal blog&lt;/span&gt;

&lt;span class="go"&gt;Available commands:&lt;/span&gt;

&lt;span class="go"&gt;    bootstrap     Get Pelican and install it in a virtual enviroment.&lt;/span&gt;
&lt;span class="go"&gt;    img4web       Optimize .jpg &amp;amp; .png images and copy them into source pict...&lt;/span&gt;
&lt;span class="go"&gt;    new           Create a new blog article.&lt;/span&gt;
&lt;span class="go"&gt;    publish       Publish into remote server with rsync.&lt;/span&gt;
&lt;span class="go"&gt;    pull_pelican  Update Pelican to last revision from repository.&lt;/span&gt;
&lt;span class="go"&gt;    regen         Regenerate the site from source.&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Veamos que hacen cada una de ellas:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;&lt;em&gt;bootstrap&lt;/em&gt;&lt;/strong&gt; Si observamos el código, veremos que lo hace es, en este orden: 
eliminar cualquier entorno virtual previo, crear un entorno virtual nuevo, 
descargar Pelican desde el repositorio (si no lo hemos hecho anteriormente) e 
instalar Pelican dentro de este entorno virtual. Y todo esto en un solo paso, casi 
todos los comandos que explicaba en &lt;a href="http://joedicastro.com/pelican-introduccion-e-instalacion.html"&gt;Pelican - Introducción e Instalación&lt;/a&gt; con 
solo  teclear &lt;code&gt;fab bootstrap&lt;/code&gt;. Así de fácil. Con este comando podemos tanto crear 
una instalación de Pelican desde cero, como actualizar la instalación de Pelican 
después de actualizar este a la última versión con &lt;strong&gt;&lt;em&gt;pull-pelican&lt;/em&gt;&lt;/strong&gt;. Siguiendo 
con nuestro ejemplo, lo que haría este comando es crear los directorios &lt;em&gt;env&lt;/em&gt; y 
&lt;em&gt;pelican&lt;/em&gt; dentro de &lt;em&gt;myblog.com&lt;/em&gt; con el entorno virtual creado y pelican 
instalado.&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="gp"&gt;$&lt;/span&gt; fab bootstrap
&lt;/pre&gt;&lt;/div&gt;


&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;&lt;em&gt;img4web&lt;/em&gt;&lt;/strong&gt; Este comando hace uso del script que describía en &lt;a href="http://joedicastro.com/optimizar-imagenes-para-la-web.html"&gt;Optimizar 
imágenes para la web&lt;/a&gt; para hacer precisamente eso, reducir el peso de las 
imágenes que empleo en los artículos. Lo que hago es a medida que voy escribiendo 
el articulo es ir guardando las imágenes en el directorio raíz (en nuestro 
ejemplo, &lt;em&gt;myblog.com/&lt;/em&gt;) y cuando lo termino, simplemente ejecuto el comando 
&lt;code&gt;fab img4web&lt;/code&gt; y este me optimiza las imágenes, me guarda las optimizadas en el 
directorio de imágenes del contenido (&lt;em&gt;myblog.com/site/source/pictures/&lt;/em&gt;) y me 
elimina las imágenes originales del directorio raíz. Cuando termina me muestra un 
resumen con la cantidad de imágenes procesadas y el ahorro en espacio conseguido. 
Espacio en disco ahorrado que se resume en menos ancho de banda consumido y en 
páginas web que se cargan más rápido.&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="gp"&gt;$&lt;/span&gt; fab img4web
&lt;/pre&gt;&lt;/div&gt;


&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;&lt;em&gt;new&lt;/em&gt;&lt;/strong&gt; Con este creo o edito los artículos del blog. Realiza 
tres funciones: me abre una venta de Gedit con el articulo que le indico con la 
extensión &lt;code&gt;.md&lt;/code&gt;, me abre una ventana de Firefox que me muestra el fichero 
&lt;em&gt;index.html&lt;/em&gt; del directorio del sitio generado por Pelican 
(&lt;em&gt;myblog.com/site/output/index.html&lt;/em&gt;) y finalmente me activa Pelican con la 
opción &lt;code&gt;autoreload&lt;/code&gt;. Luego empleando el plugin &lt;strong&gt;Grid&lt;/strong&gt; de Compiz, divido la 
pantalla en dos mitades y coloco a la izquierda Gedit y a la derecha Firefox. 
Esto me permite, como explicaba en &lt;a href="http://joedicastro.com/de-drupal-a-pelican.html"&gt;De Drupal a Pelican&lt;/a&gt; editar el contenido 
y previsualizar el resultado casi en tiempo real, disponiendo al mismo tiempo de 
un buen corrector ortográfico. &lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="gp"&gt;$&lt;/span&gt; fab new:&lt;span class="s2"&gt;&amp;quot;Articulo de prueba&amp;quot;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;&lt;em&gt;publish&lt;/em&gt;&lt;/strong&gt; El más importante, el que sube los artículos al servidor web. 
Publicar el contenido de la web es tan sencillo como ejecutar este comando. Lo 
que hace es regenerar el contenido (por si hubiera algún cambio sin guardar) y 
luego mostrarme el resultado en firefox. Pero el resultado que me muestra no es 
el del directorio de salida de Pelican, si no de una copia que tengo en un 
servidor Apache local. Esto me permite ver los cambios de manera más fiel a la 
versión web, puesto que hace uso del fichero .htaccess y de las reglas que tengo 
establecidas en él. Finalmente me pregunta si realmente deseo publicar el 
contenido, por si se me hubiera escapado algo. Si le digo que no, aborta la 
publicación, pero si le digo que si, me sincroniza el contenido de la carpeta 
local con la remota empleando &lt;strong&gt;rsync&lt;/strong&gt;. De esta manera solo se transmiten los 
ficheros nuevos, se borran los que se hayan eliminado en local y &lt;strong&gt;solo transmite 
la parte que haya cambiado de los archivos modificados&lt;/strong&gt;. Gracias a esto, 
modificar o añadir contenido es cuestión de segundos. Y sencillisimo.&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="gp"&gt;$&lt;/span&gt; fab publish
&lt;/pre&gt;&lt;/div&gt;


&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;&lt;em&gt;pull_pelican&lt;/em&gt;&lt;/strong&gt; Nos sirve para actualizar Pelican a la última revisión 
disponible en el repositorio oficial. Si después de ejecutarlo, queremos instalar 
la nueva versión en nuestro entorno virtual para poder emplearla, simplemente 
tenemos que volver a ejecutar &lt;code&gt;fab bootstrap&lt;/code&gt; y todo se realizará de forma 
automática.&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="gp"&gt;$&lt;/span&gt; fab pull_pelican
&lt;/pre&gt;&lt;/div&gt;


&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;&lt;em&gt;regen&lt;/em&gt;&lt;/strong&gt; El proceso principal, es el que le pide a Pelican que genere el 
sitio web a partir de nuestro directorio de origen. También realiza varios 
procesos: primero eliminar el directorio de salida actual (para tener una copia 
fresca), genera el nuevo contenido, luego procesa los archivos para que 
validen en HTML5 y finalmente hace una copia del directorio de salida a mi 
servidor local Apache. El procesar los archivos para validar en HTML5 se debe a 
que markdown crea unos enlaces &lt;code&gt;rel = "footnote"&lt;/code&gt; y &lt;code&gt;rev = "footnote"&lt;/code&gt; en las notas 
al pie que se han quedado obsoletos y no son necesarios. De momento es un 
post-procesado, pero puede que finalmente modifique Pelican para que se haga en 
tiempo de generación del sitio. Aunque creo que  el rendimiento de esta manera 
sería menor que emplear el comando &lt;code&gt;sed&lt;/code&gt; que ejecuta este proceso, será cuestión 
de probarlo. También se podría modificar markdown.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Con solo &lt;strong&gt;6&lt;/strong&gt; comandos tengo automatizadas todas las tareas básicas para 
administrar y crear contenido en mi blog. Ni siquiera el potente y buenísimo 
&lt;a href="http://drupal.org/project/drush"&gt;drush&lt;/a&gt; de Drupal me permitía este nivel de automatización (aunque se 
acercaba bastante). De esta manera solo me tengo que preocupar de crear 
artículos y de las posibles personalizaciones que le quiera realizar al tema del 
sitio. Me olvido de todo lo demás, de todo lo que conlleva un CMS. Solo hay una 
manera de bloguear más cómoda para los que a estos les parezca algo complejo, 
servicios como Tumblr. Aunque si quieres tener el control sobre tu sitio, no 
conozco manera más cómoda y con más ventajas que esta. &lt;/p&gt;</summary><category term="pelican"></category><category term="python"></category><category term="markdown"></category><category term="restructuredtext"></category><category term="blog"></category><category term="html"></category><category term="rsync"></category><category term="fabric"></category></entry><entry><title>Pelican - Creación de contenido</title><link href="http://joedicastro.com/pelican-creacion-de-contenido.html" rel="alternate"></link><updated>2011-06-27T18:27:00+02:00</updated><author><name>joe di castro</name></author><id>http://joedicastro.com/pelican-creacion-de-contenido.html</id><summary type="html">&lt;p&gt;Crear contenido con &lt;strong&gt;Pelican&lt;/strong&gt; es lo más sencillo que uno pueda imaginarse. Por 
ejemplo, crear este articulo, se puede hacer del siguiente modo. Abrimos un 
editor de textos cualquiera (por ejemplo vi o gedit) y creamos un archivo nuevo 
llamado Pelican, con la extensión &lt;em&gt;.md&lt;/em&gt; si queremos emplear Markdown o &lt;em&gt;.rst&lt;/em&gt; si 
queremos emplear reStructuredText. Vemos como lo haría, para este blog en 
concreto. Este sería el texto para crearlo en formato reStructuredText:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="gh"&gt;Pelican - Creación de contenido&lt;/span&gt;
&lt;span class="gh"&gt;###############################&lt;/span&gt;

&lt;span class="nc"&gt;:tags:&lt;/span&gt; &lt;span class="nf"&gt;Pelican, python, markdown, reStructuredText, blog, HTML.&lt;/span&gt;

Crear contenido con &lt;span class="gs"&gt;**Pelican**&lt;/span&gt; es lo más sencillo que uno pueda imaginarse. Por 
ejemplo, crear este articulo, se puede hacer del siguiente modo. Abrimos un 
editor de textos cualquiera (por ejemplo vi o gedit) y creamos un archivo nuevo 
llamado Pelican, con la extensión &lt;span class="ge"&gt;*.md*&lt;/span&gt; si queremos emplear Markdown o &lt;span class="ge"&gt;*.rst*&lt;/span&gt; si 
queremos emplear reStructuredText.
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Y este el que usaría para crearlo en markdown, que es el que yo empleo:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;title: Pelican - Creación de contenido
tags: Pelican, python, markdown, reStructuredText, blog, HTML, Piwik, Disqus.

Crear contenido con **Pelican** es lo más sencillo que uno pueda imaginarse. Por 
ejemplo, crear este articulo, se puede hacer del siguiente modo. Abrimos un 
editor de textos cualquiera (por ejemplo vi o gedit) y creamos un archivo nuevo 
llamado Pelican, con la extensión *.md* si queremos emplear Markdown o *.rst* si 
queremos emplear reStructuredText. Vemos como lo haría, para este blog en 
concreto.
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Ahora solo tendríamos que guardar este fichero en la carpeta raíz de nuestro 
contenido y ejecutar Pelican para generar la página web resultante. Así de 
sencillo. Por ejemplo, creamos una carpeta &lt;em&gt;prueba&lt;/em&gt; dentro de la estructura de 
ficheros que habíamos creado en el &lt;a href="http://joedicastro.com/pelican-introduccion-e-instalacion.html"&gt;articulo anterior&lt;/a&gt; (&lt;em&gt;myblog.com/&lt;/em&gt;) y 
guardamos allí este fichero. Ahora ejecutamos pelican dentro del entorno virtual 
y nos creara una carpeta &lt;em&gt;output&lt;/em&gt; donde como resultado tendremos un blog por 
defecto creado como por arte de magia. Es decir, hacemos esto:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="gp"&gt;$&lt;/span&gt; mkdir prueba
&lt;span class="gp"&gt;$&lt;/span&gt; vi prueba/pelican.md
&lt;span class="gp"&gt;$&lt;/span&gt; &lt;span class="nb"&gt;source  &lt;/span&gt;env/bin/activate
&lt;span class="gp"&gt;$&lt;/span&gt; pelican prueba/
&lt;span class="gp"&gt;$&lt;/span&gt; ls
&lt;span class="go"&gt;env  output  pelican  prueba  site  temp&lt;/span&gt;
&lt;span class="gp"&gt;$&lt;/span&gt; firefox output/index.html
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;y obtenemos esto:&lt;/p&gt;
&lt;p style="text-align:center;"&gt;&lt;img src="pictures/sample_article.png" 
alt="pelican sample article" title="pelican sample article" width=700 
height=465 /&gt;&lt;/p&gt;

&lt;h2 id="estructura_del_articulo"&gt;Estructura del articulo&lt;/h2&gt;
&lt;p&gt;En el ejemplo anterior lo único que varía entre ambos formatos (aparte de la 
sintaxis) es la cabecera del mismo. En la cabecera se incluye el titulo del 
mismo (no tiene porque coincidir con el nombre del fichero) y los metadatos. El 
titulo en reStructuredText va subrayado con símbolos &lt;code&gt;#&lt;/code&gt; y en markdown es un 
metadato más. Excepto el título, todos los metadatos son opcionales. Los 
metadatos en &lt;em&gt;.rst&lt;/em&gt; van precedidos de dos puntos &lt;code&gt;:&lt;/code&gt;. Después de los metadatos 
es suficiente con dejar una linea en blanco para comenzar a redactar el 
contenido de nuestro articulo. &lt;/p&gt;
&lt;p&gt;Los metadatos que se pueden incorporar son los siguientes:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;title&lt;/strong&gt; (solo como metadato en markdown): Es el titulo del artículo o página.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;date&lt;/strong&gt;: Es la fecha y hora de publicación. Si no se indica, tomara como tal 
los valores del propio fichero de texto.  El formato es el siguiente: 
año-mes-dia hora:minutos, es decir, &lt;code&gt;%Y-%m-%d %H:%M&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;category&lt;/strong&gt;: La categoría en la que queremos englobar el articulo. Si no se 
especifica, puede coger la categoría por defecto que se haya especificado en el 
fichero de configuración si así se ha establecido (por defecto es &lt;em&gt;misc&lt;/em&gt;). Otra 
forma aún más simple, es crear un directorio dentro de nuestro directorio de 
contenido y colgar los archivos dentro de él. Todos los que se encuentren dentro 
de ese directorio caerán en la categoría que coincida con el nombre del mismo. 
Así es como yo lo hago, con un directorio llamado blog, es idoneo para organizar 
el contenido. &lt;/p&gt;
&lt;p&gt;En el tema por defecto, las categorías se añaden como una entrada al menú 
superior que al ser seleccionada, muestra todos los artículos englobados dentro 
de ella.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;author / authors&lt;/strong&gt;: El autor(es) del articulo. Si se omite, escoge por 
defecto el que se haya establecido en el fichero de configuración. Yo lo omito, 
pero puede ser muy útil para blogs escritos por varios autores.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;tags&lt;/strong&gt;: Las etiquetas con las que etiquetamos a nuestro articulo. El tema 
por defecto solo nos permite seleccionar una y ver todos los artículos 
etiquetados de igual modo, pero es bastante sencillo crear una nube de etiquetas 
como la que empleo en este blog.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;summary&lt;/strong&gt;: Es el resumen que queremos que se vea del articulo cuando se 
emplea la paginación. Por defecto, si no se especifica este campo, los artículos 
se resumen con las primeras 50 palabras del mismo.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;slug&lt;/strong&gt;: Esta es uno de los campos más curiosos, pero al mismo tiempo más 
geniales. Este campo es el nexo que nos permite enlazar varias versiones del 
mismo articulo en distintos idiomas. En el tema por defecto, nos muestra las 
distintas traducciones de un articulo, si es que existen.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;status&lt;/strong&gt;: Esta es un campo especial que solo admite dos valores, &lt;code&gt;draft&lt;/code&gt; 
(borrador) y &lt;code&gt;published&lt;/code&gt; (publicado). Cualquier otro valor es ignorado. Si el 
valor es &lt;code&gt;draft&lt;/code&gt;, el articulo o pagina no aparece publicado ni referenciado de 
algún modo al generar el contenido, y solo está disponible si accedemos a él a 
través del directorio &lt;em&gt;drafts/&lt;/em&gt;. Esto es una ayuda para evitar que por un 
descuido subamos a la web un articulo sin terminar. Yo no lo empleo, ya veremos 
cuando hable de le automatización, como he resuelto este posible problema de 
otro modo. Si lo empleamos, deberíamos añadir el directorio &lt;em&gt;drafts/&lt;/em&gt; a nuestro 
fichero &lt;em&gt;robots.txt&lt;/em&gt; como &lt;code&gt;Disallow&lt;/code&gt; para que no fuera indexado por los 
buscadores (de poco importaría ocultar el el borrador si lo haces accesible a 
través del buscador). &lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Para comprender el resto de opciones disponibles para crear el contenido, voy a 
centrarme en el sitio de ejemplo que viene por defecto con Pelican. Empezaremos 
eliminando las pruebas que acabamos de realizar.&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="gp"&gt;$&lt;/span&gt; rm -r output/
&lt;span class="gp"&gt;$&lt;/span&gt; rm -r prueba/
&lt;span class="gp"&gt;$&lt;/span&gt; ls
&lt;span class="go"&gt;env  pelican  site&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;h2 id="estructura_del_contenido_del_ejemplo"&gt;Estructura del contenido del ejemplo&lt;/h2&gt;
&lt;p&gt;Ahora, en el directorio de contenido del sitio de ejemplo, tenemos la siguiente 
estructura de ficheros:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="gp"&gt;$&lt;/span&gt; &lt;span class="nb"&gt;cd &lt;/span&gt;site/
&lt;span class="gp"&gt;$&lt;/span&gt; tree &lt;span class="nb"&gt;source&lt;/span&gt;/
&lt;span class="go"&gt;source/&lt;/span&gt;
&lt;span class="go"&gt;├── another_super_article-fr.rst&lt;/span&gt;
&lt;span class="go"&gt;├── another_super_article.rst&lt;/span&gt;
&lt;span class="go"&gt;├── cat1&lt;/span&gt;
&lt;span class="go"&gt;│   ├── article1.rst&lt;/span&gt;
&lt;span class="go"&gt;│   ├── article2.rst&lt;/span&gt;
&lt;span class="go"&gt;│   ├── article3.rst&lt;/span&gt;
&lt;span class="go"&gt;│   └── markdown-article.md&lt;/span&gt;
&lt;span class="go"&gt;├── draft_article.rst&lt;/span&gt;
&lt;span class="go"&gt;├── extra&lt;/span&gt;
&lt;span class="go"&gt;│   └── robots.txt&lt;/span&gt;
&lt;span class="go"&gt;├── pages&lt;/span&gt;
&lt;span class="go"&gt;│   └── test_page.rst&lt;/span&gt;
&lt;span class="go"&gt;├── pictures&lt;/span&gt;
&lt;span class="go"&gt;│   ├── Fat_Cat.jpg&lt;/span&gt;
&lt;span class="go"&gt;│   ├── Sushi.jpg&lt;/span&gt;
&lt;span class="go"&gt;│   └── Sushi_Macro.jpg&lt;/span&gt;
&lt;span class="go"&gt;├── super_article.rst&lt;/span&gt;
&lt;span class="go"&gt;├── unbelievable.rst&lt;/span&gt;
&lt;span class="go"&gt;└── unwanted_file&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Donde:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;&lt;em&gt;another_super_article-fr.rst&lt;/em&gt;&lt;/strong&gt; y &lt;strong&gt;&lt;em&gt;another_super_article.rst&lt;/em&gt;&lt;/strong&gt; son dos 
versiones del mismo articulo en inglés y francés. Aunque cuelgan del directorio 
raíz, ambos están englobados dentro del sitio en la categoría &lt;em&gt;bar&lt;/em&gt; a través del
metadato &lt;strong&gt;category&lt;/strong&gt; y enlazados traducciones mediante el metadato &lt;strong&gt;slug&lt;/strong&gt; 
&lt;code&gt;:slug: oh-yeah&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;&lt;em&gt;draft_article.rst&lt;/em&gt;&lt;/strong&gt; es un articulo que no aparece publicado en el sitio 
de ejemplo al estar marcado como borrador con el metadato &lt;strong&gt;status&lt;/strong&gt;, pero al 
que podemos acceder igualmente a través de 
&lt;code&gt;myblog.com/site/source/drafts/a-draft-article.html&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;&lt;em&gt;super_article.rst&lt;/em&gt;&lt;/strong&gt; y  &lt;strong&gt;&lt;em&gt;unbelievable.rst&lt;/em&gt;&lt;/strong&gt; son ejemplos de uso de 
metadados. El primero es un articulo que cuelga de la categoría &lt;em&gt;yeah&lt;/em&gt; y 
que sirve como ejemplo del empleo de &lt;strong&gt;summary&lt;/strong&gt; y del uso de imágenes y 
código. El segundo es un ejemplo de articulo sin más metadatos que la 
fecha del mismo y de como cae en la categoría &lt;strong&gt;misc&lt;/strong&gt; por defecto.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;&lt;em&gt;unwanted_file&lt;/em&gt;&lt;/strong&gt; es un articulo que no se procesa de ningún modo por 
carecer de extensión que lo identifique como markdown o reStructuredText. Como 
consecuencia este fichero ni siquiera se copia al sitio web generado. Puede ser 
un truco útil para emplearlo como fichero de notas personales de cualquier tipo, 
por ejemplo un recordatorio de como hemos definido la estructura de archivos, 
una chuleta como usar marcado o reStructuredText, artículos pendientes, ...&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;&lt;em&gt;cat1&lt;/em&gt;&lt;/strong&gt; Este directorio es un ejemplo de como todos los artículos que 
cuelgan de él son englobados dentro de la categoría &lt;em&gt;cat1&lt;/em&gt; de forma automática. 
Esto nos evita escribir el metadato &lt;strong&gt;category&lt;/strong&gt; en cada uno de ellos y nos 
ayuda a clasificarlos en el directorio. Y nos muestra como se pueden mezclar 
archivos markdown y reStructuredText sin ningún tipo de problemas.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;&lt;em&gt;extra&lt;/em&gt;&lt;/strong&gt; En esta carpeta (puede ser otra, es configurable) englobamos 
archivos que queremos incluir en el sitio, pero que no han de ser procesados. 
Como por ejemplo: .htaccess, .robots.txt, 
&lt;a href="http://humanstxt.org/ES"&gt;.humans.txt&lt;/a&gt;, ... Esto puede cambiarse en el 
fichero de configuración, especificando el origen dentro del directorio de 
nuestro contenido y la ruta en la que queremos que esté en el sitio web. En 
este caso, sería así en el fichero de configuración &lt;code&gt;pelican.conf.py&lt;/code&gt;&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="n"&gt;FILES_TO_COPY&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;extra/robots.txt&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;robots.txt&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;),)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;&lt;em&gt;pages&lt;/em&gt;&lt;/strong&gt; Serían las páginas estáticas del sitio, lo que no son artículos. 
Este nombre de directorio está reservado para esta función por defecto. Por 
ejemplo la página &lt;a href="pages/sobre-mi.html"&gt;&lt;em&gt;sobre mí&lt;/em&gt;&lt;/a&gt; que empleo en este blog, 
sería una de estas páginas estáticas. En estas páginas no están disponibles los 
comentarios. Si se especifica en el fichero de configuración, estas páginas 
aparecerán como entradas en el menú principal del tema por defecto. &lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;&lt;em&gt;pictures&lt;/em&gt;&lt;/strong&gt; Este es uno de los directorios que se pueden emplear para 
albergar contenido estático como son las imágenes. Esto es configurable en el 
fichero de configuración. En este caso lo que se guardan en el son las imágenes 
empleadas en los artículos. Estas acabaran dentro del directorio 
&lt;code&gt;static/pictures/&lt;/code&gt; de nuestro sitio. &lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Y ya está, es una estructura muy sencilla y que genera un resultado bastante 
decente, comparado con los cientos de archivos que emplea (a parte de la BDD) 
cualquier CMS como Wordpress o Drupal.&lt;/p&gt;
&lt;p&gt;Si partiéramos de un blog ya creado, Pelican proporciona una herramienta, que 
en nuestro caso estaría en &lt;em&gt;/myblog.com/pelican/tools/importer.py&lt;/em&gt; y que nos 
permite importar artículos desde Wordpress o desde un fichero RSS y que nos 
genera los contenidos en reStructuredText. Si quisiéramos emplear markdown, 
existe una herramienta externa, &lt;a href="http://www.codefu.org/wiki/Main/Html2markdown"&gt;&lt;strong&gt;html2markdown&lt;/strong&gt;&lt;/a&gt; que nos permite obtener 
un texto en markdown desde una página web. Otra herramienta similar a esta 
última sería &lt;a href="http://www.aaronsw.com/2002/html2text/"&gt;html2text&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;No tiene más secreto que este, crear ficheros de texto plano en formato &lt;em&gt;.md&lt;/em&gt; o 
&lt;em&gt;.rst&lt;/em&gt;, guardarlos en el directorio que empleamos para albergar el contenido 
y generar el blog.&lt;/p&gt;</summary><category term="pelican"></category><category term="python"></category><category term="markdown"></category><category term="restructuredtext"></category><category term="blog"></category><category term="html"></category></entry><entry><title>Pelican - Introducción e Instalación</title><link href="http://joedicastro.com/pelican-introduccion-e-instalacion.html" rel="alternate"></link><updated>2011-06-27T10:21:00+02:00</updated><author><name>joe di castro</name></author><id>http://joedicastro.com/pelican-introduccion-e-instalacion.html</id><summary type="html">&lt;p&gt;&lt;a href="http://docs.notmyidea.org/alexis/pelican/"&gt;Pelican&lt;/a&gt; es un generador de sitios web con contenido estático. Está orientado 
originalmente a la creación de blogs. &lt;strong&gt;Pelican&lt;/strong&gt; (Pelicano) es una anagrama de 
la palabra francesa &lt;em&gt;Calepin&lt;/em&gt; que significa en español "cuaderno de notas" 
(blog). Su creador y desarrollador principal es el programador francés 
&lt;a href="http://blog.notmyidea.org/"&gt;Alexis Métaireau&lt;/a&gt;. Y es la aplicación que genera el contenido para este 
blog. Personalmente le veo muchas ventajas frente a una plataforma de blogs 
convencional como Wordpress o Drupal, como ya comentaba en mi &lt;a href="http://joedicastro.com/de-drupal-a-pelican.html"&gt;anterior articulo&lt;/a&gt;. &lt;/p&gt;
&lt;p&gt;Pelican genera este contenido estático (páginas HTML) a partir de ficheros de 
texto plano formateados en un lenguaje de marcado ligero como &lt;a href="http://es.wikipedia.org/wiki/Markdown"&gt;Markdown&lt;/a&gt; o 
&lt;a href="http://es.wikipedia.org/wiki/ReStructuredText"&gt;reStructuredText&lt;/a&gt;. También crea fuentes RSS y Atom de los artículos. Y 
dependiendo del tema empleado se pueden tener un archivo de los artículos 
publicados, clasificación por categorías, nube de etiquetas, un &lt;a href="http://es.wikipedia.org/wiki/Blogroll"&gt;blogroll&lt;/a&gt;, 
enlaces a redes sociales, botón para tweeter, paginación, traducciones, etc. 
Aunque viene un tema completo por defecto y están disponibles &lt;a href="https://github.com/ametaireau/pelican-themes"&gt;algunos más&lt;/a&gt;, 
se puede crear uno completamente personalizado desde cero. La creación de 
estos temas se basa en el sistema de plantillas &lt;a href="http://jinja.pocoo.org/"&gt;jinja2&lt;/a&gt;, lo que nos 
permite una gran flexibilidad. También disponemos de varios scripts que nos 
proporcionan soporte para comentarios con &lt;a href="http://disqus.com"&gt;Disqus&lt;/a&gt; y de analíticas con 
&lt;a href="http://www.google.com/intl/es/analytics/"&gt;Google Analytics&lt;/a&gt; ó &lt;a href="http://piwik.org/"&gt;Piwik&lt;/a&gt;. También nos permite generar ficheros PDFs de 
los artículos automáticamente y mostrar los enlaces a ellos, siempre que los 
artículos se hayan creado con reStructuredText.&lt;/p&gt;
&lt;p&gt;Es un software muy joven, el primer commit en &lt;a href="https://github.com/ametaireau/pelican"&gt;GitHub&lt;/a&gt; data del 13 de Agosto de 
2010, pero que ha evolucionado bastante rápido y que aún sigue desarrollándose a 
un  buen ritmo. Es posible que por esto aún tenga alguna carencia o algún que 
otro fallo, pero su autor es muy receptivo y acepta de buena gana las sugerencias, 
informes de errores y colaboraciones de desarrollo. Cuenta con una documentación 
bastante decente y actualizada. Alguno que otra cosa no está documentada, pero 
lo veo normal dado el ritmo de desarrollo y de a quien va orientado un software 
como este. Es muy sencilla su instalación y su utilización cuando se ha leído 
esta, pero será mejor que lo explique con un ejemplo sobre la marcha.&lt;/p&gt;
&lt;h2 id="instalaci+n_para_linux"&gt;Instalación (para Linux)&lt;/h2&gt;
&lt;p&gt;La instalación del mismo es relativamente sencilla si no queremos complicarnos 
la vida, siempre tenemos la última versión disponible en el &lt;a href="http://pypi.python.org/pypi/pelican/2.7.2"&gt;PyPi&lt;/a&gt;. Así que 
instalarlo podría ser tan sencillo como esto:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="gp"&gt;$&lt;/span&gt; easy_install pelican
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Aunque recomiendo emplear &lt;a href="http://www.pip-installer.org/en/latest/index.html"&gt;&lt;code&gt;pip&lt;/code&gt;&lt;/a&gt; en lugar de &lt;code&gt;easy_install&lt;/code&gt;, de este modo:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="gp"&gt;$&lt;/span&gt; pip install pelican
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;De todos modos desaconsejo emplear este método, me parece mejor solución, dada 
la velocidad de desarrollo del software, emplear un entorno virtual para su 
instalación. Un &lt;a href="http://www.virtualenv.org/en/latest/index.html"&gt;entorno virtual&lt;/a&gt; nos permite tener una versión de Python 
independiente de la del resto del sistema, con sus propias librerías y con el 
software instalado que nosotros deseemos. Todo esto solo estará disponible cuando 
nos encontremos dentro de este entorno virtual, sin afectar al resto del 
sistema. Y podemos eliminarlo o des/activarlo cuando deseemos. Es bastante más 
sencillo de utilizar de lo que parece y es realmente potente. &lt;/p&gt;
&lt;p&gt;Veamos, paso a paso, la forma en que yo lo instalaría.&lt;/p&gt;
&lt;p&gt;Creamos una carpeta que contendrá Pelican, el entorno virtual y el sitio que 
crearemos. Yo le daría el nombre de la URL del blog, por ejemplo, &lt;em&gt;myblog.com&lt;/em&gt;&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="gp"&gt;$&lt;/span&gt; mdkir myblog.com
&lt;span class="gp"&gt;$&lt;/span&gt; &lt;span class="nb"&gt;cd &lt;/span&gt;myblog.com
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Descargamos la revisión actual del repositorio de Pelican en GitHub. Esto nos 
creara una carpeta llamada &lt;em&gt;pelican&lt;/em&gt; que contendrá una copia del repositorio en 
local (lo cual nos será muy útil en el futuro para actualizarlo). &lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="gp"&gt;$&lt;/span&gt; git clone git://github.com/ametaireau/pelican.git
&lt;span class="go"&gt;Cloning into pelican...&lt;/span&gt;
&lt;span class="go"&gt;remote: Counting objects: 2603, done.&lt;/span&gt;
&lt;span class="go"&gt;remote: Compressing objects: 100% (1143/1143), done.&lt;/span&gt;
&lt;span class="go"&gt;remote: Total 2603 (delta 1541), reused 2420 (delta 1374)&lt;/span&gt;
&lt;span class="go"&gt;Receiving objects: 100% (2603/2603), 687.30 KiB | 397 KiB/s, done.&lt;/span&gt;
&lt;span class="go"&gt;Resolving deltas: 100% (1541/1541), done.&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Ahora vamos a crear nuestro entorno virtual. En caso de no tener instalado &lt;code&gt;pip&lt;/code&gt; 
o &lt;code&gt;virtualenv&lt;/code&gt;, es rematadamente sencillo:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="gp"&gt;$&lt;/span&gt; easy_install pip
&lt;span class="gp"&gt;$&lt;/span&gt; pip install virtualenv
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Voy a llamar &lt;em&gt;env&lt;/em&gt; a nuestro entorno virtual. Lo creamos así.&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="gp"&gt;$&lt;/span&gt; virtualenv env
&lt;span class="go"&gt;New python executable in env/bin/python&lt;/span&gt;
&lt;span class="go"&gt;Installing distribute...........................................................&lt;/span&gt;
&lt;span class="go"&gt;................................................................................&lt;/span&gt;
&lt;span class="go"&gt;.......................................done.&lt;/span&gt;
&lt;span class="gp"&gt;$&lt;/span&gt; ls
&lt;span class="go"&gt;env  pelican&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Ahora activaremos nuestro entorno virtual para "sumergirnos" dentro de él. &lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="gp"&gt;$&lt;/span&gt; &lt;span class="nb"&gt;source &lt;/span&gt;env/bin/activate
&lt;span class="go"&gt;(env)$&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Vemos que aparece el nombre del entorno virtual entre paréntesis antes de la 
línea del &lt;a href="http://es.wikipedia.org/wiki/Prompt"&gt;prompt&lt;/a&gt;. Eso quiere decir que 
nos encontramos dentro de este entorno virtual. Ahora procederemos a instalar 
Pelican. &lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="gp"&gt;$&lt;/span&gt; &lt;span class="nb"&gt;cd &lt;/span&gt;pelican/
&lt;span class="gp"&gt;$&lt;/span&gt; python setup.py install
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Ahora tenemos ya instalado Pelican, &lt;strong&gt;solo dentro de nuestro entorno virtual&lt;/strong&gt;. 
Si probamos a llamar al programa fuera de él, veremos que no está disponible, 
luego necesitaremos activar este entorno cada vez que queramos emplearlo.&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="gp"&gt;$&lt;/span&gt; pelican
&lt;span class="go"&gt;CRITICAL: you need to specify a path containing the content (see pelican --help for &lt;/span&gt;
&lt;span class="go"&gt;more information)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Nos da un error porque no le hemos pasado los parámetros necesarios. Ahora 
desactivamos el entorno y volvemos a llamar al programa y nos dice que no 
existe.&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="gp"&gt;$&lt;/span&gt; deactivate
&lt;span class="gp"&gt;$&lt;/span&gt; pelican
&lt;span class="go"&gt;pelican: orden no encontrada&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Tenemos ahora dentro de &lt;em&gt;myblog.com/&lt;/em&gt; dos directorios, &lt;em&gt;env&lt;/em&gt; y &lt;em&gt;pelican&lt;/em&gt;. 
Podríamos borrar ahora el directorio &lt;em&gt;pelican&lt;/em&gt; porque ya lo tenemos instalado 
dentro del entorno virtual, pero sería bueno conservarlo para poder actualizarlo 
en el futuro mediante &lt;code&gt;git pull&lt;/code&gt;. Yo crearía una tercer directorio, &lt;em&gt;site&lt;/em&gt; donde 
se alojaría nuestro sitio, organizado a su vez en tres subdirectorios: &lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;source&lt;/strong&gt;: sería la carpeta donde alojaríamos los archivos de texto de 
nuestro blog y todo aquello que quisiéramos que formara parte de nuestro sitio: 
imágenes, fichero &lt;a href="http://es.wikipedia.org/wiki/Htaccess"&gt;.htacess&lt;/a&gt;, fichero &lt;a href="http://es.wikipedia.org/wiki/Robots.txt"&gt;robots.txt&lt;/a&gt;, páginas 
personalizadas...&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;output&lt;/strong&gt;: El resultado del procesamiento de Pelican, es decir, nuestro blog.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;theme&lt;/strong&gt; : Sería el tema que usariamos para crear nuestro blog, en el 
 estarían las plantillas &lt;strong&gt;jinja2&lt;/strong&gt;, las hojas de estilo .css, el contenido 
 estático (iconos, imágenes), ...&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Luego, lo haríamos así:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="gp"&gt;$&lt;/span&gt; &lt;span class="nb"&gt;cd&lt;/span&gt; ..
&lt;span class="gp"&gt;$&lt;/span&gt; mkdir -p site/source
&lt;span class="gp"&gt;$&lt;/span&gt; mkdir site/output
&lt;span class="gp"&gt;$&lt;/span&gt; mkdir site/theme
&lt;span class="gp"&gt;$&lt;/span&gt; ls site/
&lt;span class="go"&gt;output  source  theme&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Ya está, tenemos instalado Pelican y creada nuestra estructura para empezar a 
trabajar con él. Para eso nada mejor que crear un primer sitio de ejemplo. 
Entramos en el entorno virtual y creamos el sitio que viene de ejemplo con 
Pelican:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="gp"&gt;$&lt;/span&gt; &lt;span class="nb"&gt;source &lt;/span&gt;env/bin/activate
&lt;span class="gp"&gt;$&lt;/span&gt; pelican pelican/samples/content/ -o site/output -s pelican/samples/pelican.conf.py
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Y ahora si abrimos el fichero &lt;code&gt;myblog.com/site/output/index.html&lt;/code&gt; en un 
navegador, veremos algo como esto (en la imagen se ve la categoría cat1 
seleccionada):&lt;/p&gt;
&lt;p style="text-align:center;"&gt;&lt;img src="pictures/sample_blog.png" 
alt="pelican sample blog" title="pelican sample blog" width=453 height=700 /&gt;&lt;/p&gt;

&lt;p&gt;Es así de fácil de emplear Pelican, simplemente especificarle el directorio donde 
está el origen del contenido, el directorio destino y el fichero de configuración. 
Aunque con el fichero de configuración correctamente creado, solo será necesario 
indicarle la ubicación del mismo. Para comprender mejor la estructura y como 
funciona Pelican, sería buena idea copiar el tema, el fichero de configuración y 
el contenido que han generado este sitio de ejemplo a la carpeta &lt;em&gt;site&lt;/em&gt; para no 
empezar nuestro blog desde cero.&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="gp"&gt;$&lt;/span&gt; cp -r pelican/samples/content/* site/source
&lt;span class="gp"&gt;$&lt;/span&gt; cp -r pelican/pelican/themes/notmyidea/* site/theme/
&lt;span class="gp"&gt;$&lt;/span&gt; cp pelican/samples/pelican.conf.py site/
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;En próximos artículos veremos como crear artículos, publicar el sitio, 
personalizar el tema y automatizar todos estos procesos para hacerlo todo mucho 
más sencillo. &lt;/p&gt;</summary><category term="pelican"></category><category term="python"></category><category term="markdown"></category><category term="restructuredtext"></category><category term="blog"></category><category term="html"></category><category term="piwik"></category><category term="disqus"></category></entry><entry><title>De Drupal a Pelican</title><link href="http://joedicastro.com/de-drupal-a-pelican.html" rel="alternate"></link><updated>2011-06-22T02:10:00+02:00</updated><author><name>joe di castro</name></author><id>http://joedicastro.com/de-drupal-a-pelican.html</id><summary type="html">&lt;p&gt;Este blog no está realizado con ningún &lt;abbr title="Content Management System (en español, &amp;quot;Sistema de gestión de contenidos&amp;quot;)"&gt;CMS&lt;/abbr&gt;, ni siquiera utiliza &lt;abbr title="Base de datos"&gt;BDD&lt;/abbr&gt; alguna, es 
simplemente HTML + CSS y nada más. Es decir, es contenido estático, no dinámico.
Hasta hace 3 días estaba funcionando con el mejor &lt;abbr title="Content Management System (en español, &amp;quot;Sistema de gestión de contenidos&amp;quot;)"&gt;CMS&lt;/abbr&gt; &lt;a href="http://es.wikipedia.org/wiki/PHP"&gt;PHP&lt;/a&gt; que conozco, 
&lt;a href="http://drupal.org"&gt;Drupal&lt;/a&gt;. Pero persiguiendo el camino hacia el minimalismo y la productividad 
(fiel al espíritu &lt;a href="http://es.wikipedia.org/wiki/Principio_KISS"&gt;&lt;abbr title="Keep It Simple, Stupid (en español, &amp;quot;Mantenlo simple, estúpido&amp;quot;). Ver enlace"&gt;&lt;abbr title="Keep It Simple, Stupid (en español, &amp;quot;Mantenlo simple, estúpido&amp;quot;). Ver enlace"&gt;KISS&lt;/abbr&gt;&lt;/abbr&gt;&lt;/a&gt;) que ya inicie cuando &lt;a href="http://joedicastro.com/markdown-la-mejor-opcion-para-crear-contenidos-web.html"&gt;comencé a escribir todos mis 
artículos en Drupal con Markdown&lt;/a&gt;, el siguiente paso era evidente. La pregunta
era muy sencilla, si un blog consta de contenidos que rara vez cambian 
(exceptuando los comentarios) ¿para que necesito un gestor de contenidos 
dinámicos?&lt;/p&gt;
&lt;p&gt;La respuesta es fácil, para nada. Actualmente, gracias a servicios como los de 
&lt;a href="http://disqus.com"&gt;Disqus&lt;/a&gt;, &lt;a href="http://livefyre.com/"&gt;Livefyre&lt;/a&gt;, &lt;a href="http://intensedebate.com/"&gt;IntenseDebate&lt;/a&gt; ó &lt;a href="http://www.aboutecho.com/commenting"&gt;Echo&lt;/a&gt; es posible 
externalizar el único contenido dinámico básico de un blog, los comentarios. 
Todo lo demás puede ser contenido puramente estático, solo HTML y CSS, sin 
renunciar a prácticamente nada de lo que nos ofrece un blog basado en un &lt;abbr title="Content Management System (en español, &amp;quot;Sistema de gestión de contenidos&amp;quot;)"&gt;CMS&lt;/abbr&gt; 
como Wordpress o Drupal. Se pueden emplear scripts externos en javascript si se 
desea, o insertarlos dentro del HTML. Lo que nos permite implementar lo mismo 
que en un blog normal. Además se puede disponer también de feeds RSS y Atom.&lt;br /&gt;
&lt;/p&gt;
&lt;h2 id="elegir_un_generador_de_contenido_est+tico"&gt;Elegir un generador de contenido estático&lt;/h2&gt;
&lt;p&gt;Evidentemente la idea no es crear las paginas HTML a mano, ni de broma, lo lógico 
era seguir empleando la misma estrategia que ya había iniciado con Drupal, 
emplear solo ficheros de texto en formato Markdown que nos generarán el HTML 
necesario de forma automática. Entonces lo que tenía que encontrar era un 
software que me permitiera hacer lo mismo que Drupal, pero sin toda la 
parafernalia que rodea a un &lt;abbr title="Content Management System (en español, &amp;quot;Sistema de gestión de contenidos&amp;quot;)"&gt;CMS&lt;/abbr&gt;. Un generador de sitios web estáticos (a partir 
de markdown) y que a ser posible estuviera escrito en &lt;strong&gt;Python&lt;/strong&gt;, mi lenguaje 
favorito. Como ya adelante en el &lt;a href="http://joedicastro.com/markdown-la-mejor-opcion-para-crear-contenidos-web.html"&gt;artículo sobre Markdown&lt;/a&gt;, existen varias opciones:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="http://docs.notmyidea.org/alexis/pelican/"&gt;Pelican&lt;/a&gt; de Alexis Métaireau, que emplea en su propio &lt;a href="http://blog.notmyidea.org/"&gt;blog&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://www.blogofile.com/"&gt;Blogofile&lt;/a&gt; de Ryan McGuire que también lo usa en su &lt;a href="http://www.enigmacurry.com/"&gt;blog&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://hyde.github.com/"&gt;Hyde&lt;/a&gt; de Lakshmi Vyas. Su &lt;a href="http://ringce.com/blog/"&gt;blog&lt;/a&gt; con Hyde también.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/mitsuhiko/rstblog"&gt;rstblog&lt;/a&gt; de Armin Ronacher. Solo permite reStructuredText, con él crea su 
&lt;a href="http://lucumr.pocoo.org/"&gt;blog&lt;/a&gt;, un ejemplo de elegancia y calidad.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Bueno, tenía varias posibilidades, solo tenía que elegir una que se adaptara 
mejor a mis necesidades. De entrada descarté &lt;strong&gt;rstblog&lt;/strong&gt; porque no permitía el 
empleo de markdown, cuando los otros permitían tanto .rst como .md como formatos 
de entrada. Solo me quedaban 3 candidatos. Así que lo primero que hice antes de 
nada, fue buscar blogs creados con cada uno de ellos, para ver que posibilidades 
reales ofrecían. Encontré ejemplos de blogs de mucha calidad de todos ellos. 
Aunque enseguida me di cuenta de una cosa, en dos de ellos los mejores blogs lo 
eran porque tenían una elevada personalización detrás (artículos de sus autores 
contándolo). Y curiosamente con el tercero, casi todos preferían quedarse con la 
configuración estándar, sin tocar prácticamente nada, y la verdad es que el 
resultado era bastante decente. Luego miré que cargaba cada uno de ellos en la 
página de entrada, y volvía a repetirse la misma tendencia. En los dos primeros 
vi demasiadas hojas de estilo, imágenes y demasiados scripts javascript, en el 
tercero, nuevamente se cargaban menos elementos. Finalmente comparé 
características, modo de funcionamiento y le eché un vistazo rápido al código. 
La impresión era otra vez la misma, dos de ellos, &lt;strong&gt;Hyde&lt;/strong&gt; y &lt;strong&gt;Blogofile&lt;/strong&gt; aunque 
aparentemente potentes, los veía innecesariamente complejos, en cambió 
&lt;strong&gt;Pelican&lt;/strong&gt; era bastante más sencillo. Otra forma de determinar su repercusión 
era contar el número de descargas de cada una de las aplicaciones desde PyPi. 
Los números son los siguientes (a 27 de Junio de 2011), obtenidos con 
&lt;a href="https://github.com/aclark4life/vanity"&gt;Vanity&lt;/a&gt; o &lt;a href="http://pythonpackages.com/"&gt;pythonpackages.com&lt;/a&gt;:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Paquete&lt;/th&gt;
&lt;th&gt;Descargas&lt;/th&gt;
&lt;th&gt;Descargas (2 de Diciembre de 2011)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Blogofile&lt;/td&gt;
&lt;td&gt;2.419&lt;/td&gt;
&lt;td&gt;3.854&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Hyde&lt;/td&gt;
&lt;td&gt;1.945&lt;/td&gt;
&lt;td&gt;4.518&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Pelican&lt;/td&gt;
&lt;td&gt;3.919&lt;/td&gt;
&lt;td&gt;6.138&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;La elección final era Pelican y no me arrepiento en absoluto, la prueba es que 
esté blog está funcionando gracias a él (Gracias Alexis!). Aunque las otras dos 
son también muy buenas opciones, y seguramente serían la primera opción para más 
de uno. Y siempre podría cambiar fácilmente, porque el contenido seguiría estando 
guardado en ficheros de texto con marcado markdown. &lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Actualización&lt;/strong&gt; (2-12-2011): &lt;/p&gt;
&lt;p&gt;La estructura de Pelican es tan sencilla y eficaz, que &lt;a href="http://www.solberg.is/"&gt;Jökull Sólberg&lt;/a&gt; 
ha creado a partir de una versión hospedada del mismo (y modificada) una de las 
plataformas de blogs más simples de utilizar que existen, &lt;a href="http://calepin.co/"&gt;calepin.co&lt;/a&gt;. 
Publicar articulos es tán fácil como crear un archivo markdown y guardarlo en tu 
cuenta de &lt;a href="http://www.dropbox.com/"&gt;Dropbox&lt;/a&gt;. Así de sencillo.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;No entraré en detalles ahora de como instalar y emplear &lt;strong&gt;Pelican&lt;/strong&gt;, eso lo dejo 
para otro próximo articulo, &lt;a href="http://joedicastro.com/pelican-introduccion-e-instalacion.html"&gt;Pelican&lt;/a&gt;. Pero si voy a hacer un repaso de los 
pros y los contras de emplear Pelican frente a un &lt;abbr title="Content Management System (en español, &amp;quot;Sistema de gestión de contenidos&amp;quot;)"&gt;CMS&lt;/abbr&gt; como Drupal para crear un 
blog.&lt;/p&gt;
&lt;h2 id="ventajas_de_pelican_vs_cms"&gt;Ventajas de Pelican vs &lt;abbr title="Content Management System (en español, &amp;quot;Sistema de gestión de contenidos&amp;quot;)"&gt;CMS&lt;/abbr&gt;&lt;/h2&gt;
&lt;h3 id="solo_ficheros_de_texto_no_bdd"&gt;Solo ficheros de texto, No &lt;abbr title="Base de datos"&gt;BDD&lt;/abbr&gt;&lt;/h3&gt;
&lt;p&gt;Simplemente te tienes que preocupar de eso, ficheros de texto, es donde guardas 
el contenido que creas. Todo lo demás lo genera Pelican por ti. Nada de crear y 
gestiónar bases de datos, ni copias de seguridad de la misma y un montón de 
espacio y recursos desaprovechado solamente para generar dinámicamente el mismo 
contenido que te genera Pelican.&lt;/p&gt;
&lt;h3 id="mejor_rendimiento_carga_de_p+gina_m+s_r+pida"&gt;Mejor rendimiento, carga de página más rápida&lt;/h3&gt;
&lt;p&gt;Generar contenido dinámico es más caro en recursos y es más lento (consultas a 
la &lt;abbr title="Base de datos"&gt;BDD&lt;/abbr&gt;). Sobre todo a medida que llenas tu &lt;abbr title="Content Management System (en español, &amp;quot;Sistema de gestión de contenidos&amp;quot;)"&gt;CMS&lt;/abbr&gt; de personalizaciones y plugins. 
¿Que hacen prácticamente todos los sistemas de caché?, generar contenido 
estático para luego servirlo más rápidamente. ¿No es un poco estúpido crear 
contenido que apenas cambia en el tiempo, en un sistema dinámico que genera ese 
contenido cada vez y que para mejorar su rendimiento lo convierte en estático? 
Y ya no hablemos de las múltiples hojas CSS, scripts javascript y enlaces a 
contenido externo que cargan la mayoría de los &lt;abbr title="Content Management System (en español, &amp;quot;Sistema de gestión de contenidos&amp;quot;)"&gt;CMS&lt;/abbr&gt; por defecto. Cada plugin que 
añadimos pone su granito de arena y optimizar todo esto requiere dedicación y 
esfuerzo (o seguir sumando aún más plugins en el mejor de los casos). Con 
Pelican ya tienes directamente el contenido estático y menos recursos que 
descargar. En este blog, sin contar con los ficheros javascript de Disqus y 
Piwik, lo único que se descarga es un fichero HTML, una hoja CSS y las imágenes 
que se incluyen en los artículos (cuando las hay). Es decir sirves el mismo 
contenido pero generando menos tráfico desde tu servidor. &lt;/p&gt;
&lt;h3 id="soporta_mejor_el_tr+fico"&gt;Soporta mejor el tráfico&lt;/h3&gt;
&lt;p&gt;Cuando un sitio web soporta mucho tráfico, emplear un &lt;abbr title="Content Management System (en español, &amp;quot;Sistema de gestión de contenidos&amp;quot;)"&gt;CMS&lt;/abbr&gt; requiere de mucha 
optimización y generalmente de mucha maquina o complejas instalaciones. Y la 
base principal siempre es un sistema de caché que sirva contenido lo más 
estático posible. Se cachea todo lo que se puede, y si es en memoria mejor. Las 
&lt;abbr title="Base de datos"&gt;BDD&lt;/abbr&gt; son un problema aparte, desde soluciones NoSQL a clusters o &lt;abbr title="Base de datos"&gt;BDD&lt;/abbr&gt; distribuidas. 
Con contenido estático no te tienes que preocupar de optimizar los accesos a la 
&lt;abbr title="Base de datos"&gt;BDD&lt;/abbr&gt;, solo de tener un buen servidor web y si quieres, cachear en memoria o 
ampliar máquina. Pero poco más.&lt;/p&gt;
&lt;h3 id="seguridad"&gt;Seguridad&lt;/h3&gt;
&lt;p&gt;Olvídate de problemas de seguridad, los únicos agujeros de seguridad de un sitio 
con contenido estático están del lado del servidor web, de todo lo demás, te 
olvidas. Establece bien los permisos en el sistema de ficheros y punto. El único 
contenido dinámico del sitio (javascript) ni siquiera es algo que deba 
preocuparte, es algo externo que le concierne a &lt;strong&gt;Disqus&lt;/strong&gt; o al sistema de 
analíticas web que elijas (Google Analytics o Piwik).&lt;/p&gt;
&lt;h3 id="olvidarse_de_gestionar_un_cms_mantenimiento_mucho_m+s_sencillo_nulo"&gt;Olvidarse de gestionar un &lt;abbr title="Content Management System (en español, &amp;quot;Sistema de gestión de contenidos&amp;quot;)"&gt;CMS&lt;/abbr&gt;. Mantenimiento mucho más sencillo (nulo)&lt;/h3&gt;
&lt;p&gt;Instalar el &lt;abbr title="Content Management System (en español, &amp;quot;Sistema de gestión de contenidos&amp;quot;)"&gt;CMS&lt;/abbr&gt;, crear la &lt;abbr title="Base de datos"&gt;BDD&lt;/abbr&gt;, encontrar, instalar y probar los plugins que 
necesitas, actualizaciones, actualizaciones de seguridad, personalizaciones, 
temas... Todo lo que rodea a cualquier &lt;abbr title="Content Management System (en español, &amp;quot;Sistema de gestión de contenidos&amp;quot;)"&gt;CMS&lt;/abbr&gt;. Y ya no digamos si hablamos de un 
&lt;abbr title="Content Management System (en español, &amp;quot;Sistema de gestión de contenidos&amp;quot;)"&gt;CMS&lt;/abbr&gt; potente y complejo como Drupal, con cientos de posibilidades. Y sin olvidar 
toda la basura que se va acumulando en las &lt;abbr title="Base de datos"&gt;BDD&lt;/abbr&gt; tras varias actualizaciones y 
múltiples pruebas de plugins, con Pelican siempre tienes un sistema limpio. 
Todo eso lo olvidas con Pelican, lo instalas, personalizas y automatizas una 
sola vez, luego te olvidas de todo lo que no sea escribir (si quieres, nada te 
impide seguir cambiándolo y mejorándolo). Emplea tú tiempo en crear contenido.&lt;/p&gt;
&lt;h3 id="backups_m+s_sencillos"&gt;Backups más sencillos&lt;/h3&gt;
&lt;p&gt;Con un &lt;abbr title="Content Management System (en español, &amp;quot;Sistema de gestión de contenidos&amp;quot;)"&gt;CMS&lt;/abbr&gt; &lt;abbr title="No hacerlas es una decisión nefasta"&gt;deberías hacer Backups&lt;/abbr&gt; del servidor web tanto del sistema de ficheros 
como de la &lt;abbr title="Base de datos"&gt;BDD&lt;/abbr&gt;. Y sería aconsejable tener un servidor web local montado para 
probar los cambios que vayas a hacer en el &lt;abbr title="Content Management System (en español, &amp;quot;Sistema de gestión de contenidos&amp;quot;)"&gt;CMS&lt;/abbr&gt; sin miedo a romper nada. Con 
Pelican ni siquiera necesitas hacer Backups del servidor ni del contenido web. 
Todo lo que necesitas para generarlo ya está en tu ordenador en esos ficheros de 
texto. Incluso si empleas un tema propio, también está en tu equipo. Así que las 
copias de seguridad de tu sitio web no son distintas a las que 
&lt;abbr title="No me digas que aún no las haces, ¿estas de broma?"&gt;regularmente ya haces&lt;/abbr&gt; de tu ordenador personal.&lt;/p&gt;
&lt;h3 id="hosting_en_cualquier_sitio"&gt;Hosting en cualquier sitio&lt;/h3&gt;
&lt;p&gt;Solo tienes que alojar contenido estático, no necesitas &lt;abbr title="Base de datos"&gt;BDD&lt;/abbr&gt; ni soporte para 
ningún lenguaje o librería en particular. Puedes hasta utilizar recursos 
gratuitos como las páginas de &lt;a href="https://github.com/"&gt;GitHub&lt;/a&gt; o &lt;a href="http://bitbucket.org/"&gt;BitBucket&lt;/a&gt; o un sistema 
de ficheros en la nube económico como &lt;a href="http://aws.amazon.com/es/s3/"&gt;Amazon S3&lt;/a&gt; (o 
&lt;a href="http://aws.amazon.com/es/cloudfront/"&gt;Amazon CloudFront&lt;/a&gt;). Solo necesitas eso, servir ficheros, nada más. Hasta 
el hosting más económico te sirve. &lt;/p&gt;
&lt;h3 id="emplear_un_cvs_para_gestionarlo"&gt;Emplear un &lt;abbr title="Control Version System (en español, &amp;quot;Sistema de Control de Versiones&amp;quot;)"&gt;CVS&lt;/abbr&gt; para gestionarlo&lt;/h3&gt;
&lt;p&gt;Poder emplear Git o Mercurial o cualquier otro &lt;abbr title="Control Version System (en español, &amp;quot;Sistema de Control de Versiones&amp;quot;)"&gt;CVS&lt;/abbr&gt; para gestionar los cambios 
del blog no tiene precio. Ningún sistema de revisiones de &lt;abbr title="Content Management System (en español, &amp;quot;Sistema de gestión de contenidos&amp;quot;)"&gt;CMS&lt;/abbr&gt; es tan potente. 
Además tienes la posibilidad de crear un &lt;em&gt;hook&lt;/em&gt; para que al enviar un commit 
después de crear un articulo (o realizar un cambio) se suba el contenido 
automáticamente al servidor. Con esto realizar cualquier cambio o revertir un 
error es algo trivial. Además te permite subir una copia a un sitio como GitHub 
o BitBucket y tenerlo siempre disponible en cualquier sitio con conexión a la 
red. Como maravillosa opción, esto permite que el contenido de un blog, incluso 
de un mismo articulo, sea editado por más de una persona de manera bastante más 
sencilla, potente y menos propensa a errores que con un &lt;abbr title="Content Management System (en español, &amp;quot;Sistema de gestión de contenidos&amp;quot;)"&gt;CMS&lt;/abbr&gt;. &lt;/p&gt;
&lt;h3 id="crear_los_articulos_off-line"&gt;Crear los articulos off-line&lt;/h3&gt;
&lt;p&gt;Eso te permite ir creando los artículos al ritmo que te de la gana, cuando 
quieras y en cualquier sitio con un portátil. No necesitas estar conectado a la 
red. Esto también puede hacerse con un &lt;abbr title="Content Management System (en español, &amp;quot;Sistema de gestión de contenidos&amp;quot;)"&gt;CMS&lt;/abbr&gt;, pero suele ser más complejo 
(exceptuando emplear cortar y pegar) e inseguro (si se habilita el envío remoto 
de artículos). Yo lo había logrado en Drupal empleando markdown, pero seguía 
necesitando un segundo paso on-line para personalizar las etiquetas. &lt;/p&gt;
&lt;h3 id="edici+n_de_art+culos_m+s_c+moda"&gt;Edición de artículos más cómoda&lt;/h3&gt;
&lt;p&gt;Puede parecer que un &lt;abbr title="Content Management System (en español, &amp;quot;Sistema de gestión de contenidos&amp;quot;)"&gt;CMS&lt;/abbr&gt; con su editor WYSIWYG es más cómodo, pero todo lo 
contrario. Ya lo comentaba en el &lt;a href="http://joedicastro.com/markdown-la-mejor-opcion-para-crear-contenidos-web.html"&gt;artículo sobre markdown&lt;/a&gt;. Pero es que 
además me proporciona una mejor experiencia de edición y más potente. Explico 
como redacto yo los artículos para que se entienda mejor. Divido la pantalla en 
dos mitades, a la izquierda el editor de textos y a la derecha el navegador. 
Como editor de textos empleo Gedit, que tiene resaltado de texto para markdown y 
un corrector ortográfico (por esto no uso vim para esto) bastante mejor que el 
de Firefox (que solo examina el texto hasta cierto número de casos dudosos). 
Además Pelican tiene una maravillosa opción, &lt;code&gt;autoreload&lt;/code&gt; que lo hace correr en 
segundo plano y cuando detecta un cambio en uno de los ficheros, vuelve a generar 
el contenido. Entonces en gedit le digo que autoguarde el contenido cada 3 
minutos (o a voluntad, manualmente) y cuando Pelican lo detecta, automáticamente 
regenera los ficheros HTML. Como navegador empleo Firefox y tengo, abierto en 
una pestaña, el fichero &lt;code&gt;index.html&lt;/code&gt; que genera Pelican y empleando la extensión 
&lt;a href="https://addons.mozilla.org/es-ES/firefox/addon/auto-reload/?src=api"&gt;Auto Reload&lt;/a&gt; el contenido de la página (en local) se actualiza 
automáticamente al detectar un cambio en el fichero. Es decir, como en la 
primera página se puede ver el contenido completo del último articulo, lo que 
estoy viendo es una previsualización automática del contenido en la página cada 
3 minutos. Y todo esto en off-line, sin estar conectado a internet. Esto si es 
un verdadero editor WYSIWYG, y no los otros. Además, que demonios, los 
navegadores no se diseñaron para crear texto, cualquier editor de texto es más 
potente.&lt;/p&gt;
&lt;h3 id="control_del_spam"&gt;Control del Spam&lt;/h3&gt;
&lt;p&gt;El Spam, esa lacra que azota toda la web. En Pelican, ese problema, lo tiene que 
gestionar Disqus, no tú. Tú solo tienes que gestionar el poco que se le escape. 
Pero el buscar un plugin, configurarlo y que funcione bien, es algo de lo que no 
tienes que preocuparte. En Drupal tenía este asunto solucionado, pero fue cosa 
de probar varios plugins, hasta que al final &lt;a href="http://joedicastro.com/combatir-el-spam-en-drupal.html"&gt;di con uno que me lo solucionaba de
verdad&lt;/a&gt;. &lt;/p&gt;
&lt;h3 id="recursos_de_cpu_y_ram"&gt;Recursos de CPU y RAM&lt;/h3&gt;
&lt;p&gt;El contenido dinámico consume mucha más memoria RAM y CPU en el servidor que 
servir contenido estático. Al fin y al cabo, en el caso del contenido estático, 
es poco más complejo que servir ficheros. Si tienes que compartir el servidor 
con más proyectos, agradecerás no tener que emplear un &lt;abbr title="Content Management System (en español, &amp;quot;Sistema de gestión de contenidos&amp;quot;)"&gt;CMS&lt;/abbr&gt; para servir el blog.&lt;/p&gt;
&lt;h3 id="resaltado_de_sintaxis_incorporada_con_pygments"&gt;Resaltado de Sintaxis incorporada con Pygments&lt;/h3&gt;
&lt;p&gt;Mientras en la mayoría de &lt;abbr title="Content Management System (en español, &amp;quot;Sistema de gestión de contenidos&amp;quot;)"&gt;CMS&lt;/abbr&gt; necesitas un plugin para habilitar el resaltado de 
sintaxis para código fuente, en Pelican esto viene por defecto empleando el 
excelente &lt;a href="http://pygments.org/"&gt;Pygments&lt;/a&gt;&lt;/p&gt;
&lt;h3 id="cumplimiento_de_est+ndares_web"&gt;Cumplimiento de Estándares Web&lt;/h3&gt;
&lt;p&gt;Con Pelican es relativamente sencillo configurar el tema para que cumpla los 
estándares web y genere contenido valido. Y una vez que lo haces, es para 
siempre, a no ser que modifiques algo en el tema, todo el contenido que generes 
cumplirá con los estándares (a no ser que incluyas HTML dentro que no lo sea). 
De este modo, este sitio valida HTML5, CSS3 y genera feeds RSS y Atom validos. 
Conseguir esto con un &lt;abbr title="Content Management System (en español, &amp;quot;Sistema de gestión de contenidos&amp;quot;)"&gt;CMS&lt;/abbr&gt; y empleando editores WYSIWYG es bastante más complejo
y doloroso. Aunque yo lo había conseguido con Drupal y markdown, tuve que 
modificar un tema casi por completo, casi como crearlo desde cero. &lt;/p&gt;
&lt;h2 id="inconvenientes_de_pelican_vs_cms"&gt;Inconvenientes de Pelican vs &lt;abbr title="Content Management System (en español, &amp;quot;Sistema de gestión de contenidos&amp;quot;)"&gt;CMS&lt;/abbr&gt;&lt;/h2&gt;
&lt;h3 id="comentarios_sin_resaltado_de_sintaxis"&gt;Comentarios sin resaltado de sintaxis&lt;/h3&gt;
&lt;p&gt;Algo que me permitía Drupal y no me permite Disqus (por ahora) era emplear 
markdown en los comentarios y resaltado de sintaxis para el código fuente. Es el 
mayor inconveniente que he encontrado hasta ahora. Pero bueno, tampoco es algo 
imprescindible y esperemos que Disqus lo soporte en un futuro.&lt;/p&gt;
&lt;h3 id="sitemap"&gt;Sitemap&lt;/h3&gt;
&lt;p&gt;Tampoco Pelican genera sitemaps en xml para los buscadores. Aunque tampoco es 
algo imprescindible y Drupal tampoco lo soporta por defecto, si no a través de 
un módulo. El autor lo tiene como tarea pendiente, y si tarda mucho, a lo mejor 
me animo y lo creo yo mismo.&lt;/p&gt;
&lt;h3 id="personalizaci+n_m+s_sencilla_para_non_geeks"&gt;Personalización más sencilla para non geeks&lt;/h3&gt;
&lt;p&gt;Esta es la parte que menos me afecta, pero es el gran inconveniente para la gran 
mayoría sin conocimientos avanzados. Aunque Pelican no es difícil de instalar y 
configurar, si queremos personalizarlo bastante, la cosa cambia. Los &lt;abbr title="Content Management System (en español, &amp;quot;Sistema de gestión de contenidos&amp;quot;)"&gt;CMS&lt;/abbr&gt; son 
mucho más sencillos en ese sentido, pero el coste a pagar por otro lado no me 
compensa. &lt;/p&gt;
&lt;h3 id="no_tiene_b+squeda_incorporada"&gt;No tiene búsqueda incorporada&lt;/h3&gt;
&lt;p&gt;Es otro pequeño inconveniente que puede suplirse empleando la de Google AdSense 
en el sitio, por ejemplo. Personalmente no me importa demasiado, teniendo 
disponibles en el sitio recursos como el archivo de todos los artículos 
publicados o la nube de etiquetas.&lt;/p&gt;
&lt;h3 id="no_puedes_personalizar_el_contenido_din+micamente"&gt;No puedes personalizar el contenido dinámicamente&lt;/h3&gt;
&lt;p&gt;Con un &lt;abbr title="Content Management System (en español, &amp;quot;Sistema de gestión de contenidos&amp;quot;)"&gt;CMS&lt;/abbr&gt; puedes hacer cosas como mostrar un contenido o un tema distinto según 
el perfil del usuario, o según la carga del servidor, etc. Con contenido 
estático lógicamente no puedes hacer esto. A mi me da igual, no lo necesito, es 
solo un blog.&lt;/p&gt;
&lt;br /&gt;

&lt;p&gt;Llevo varios años empleando Drupal en varios sitios y me sigue pareciendo un &lt;abbr title="Content Management System (en español, &amp;quot;Sistema de gestión de contenidos&amp;quot;)"&gt;CMS&lt;/abbr&gt; 
excelente y una buenísima opción para generar contenido dinámico para no 
desarrollladores (de otro modo prefiero un framework como Django). Pero 
actualmente, para crear blogs, si se tienen conocimientos suficientes, emplear 
un &lt;abbr title="Content Management System (en español, &amp;quot;Sistema de gestión de contenidos&amp;quot;)"&gt;CMS&lt;/abbr&gt; me parece una decisión poco acertada, es matar moscas a cañonazos. Hoy en 
día hay soluciones como Pelican y las mencionadas arriba (y otras alternativas 
en otros lenguajes) que te permiten crear blogs con facilidad, centrándote 
únicamente en crear los artículos y automatizar todo lo demás. ¿Acaso esa no es 
la razón principal del grandisimo éxito de &lt;a href="http://twitter.com/"&gt;twitter&lt;/a&gt; o &lt;a href="http://www.tumblr.com/"&gt;tumblr&lt;/a&gt;? 
La inmediatez de los resultados y la delegación de la gestión a terceros, tú 
solo escribes. Pelican te permite lo mismo, solo requiere la personalización 
inicial y listo, con la ventaja añadida de que puedes personalizarlo a tu gusto 
y hasta donde te dé la gana o seas capaz.&lt;/p&gt;</summary><category term="markdown"></category><category term="restructuredtext"></category><category term="pelican"></category><category term="drupal"></category><category term="cms"></category><category term="python"></category><category term="blog"></category><category term="html"></category></entry><entry><title>Notificaciones de escritorio en Ubuntu desde Python</title><link href="http://joedicastro.com/notificaciones-de-escritorio-en-ubuntu-desde-python.html" rel="alternate"></link><updated>2011-06-12T23:05:00+02:00</updated><author><name>joe di castro</name></author><id>http://joedicastro.com/notificaciones-de-escritorio-en-ubuntu-desde-python.html</id><summary type="html">&lt;p&gt;En &lt;strong&gt;Ubuntu&lt;/strong&gt;&lt;sup id="fnref:1"&gt;&lt;a href="#fn:1" rel="footnote"&gt;1&lt;/a&gt;&lt;/sup&gt; las notificaciones de escritorio son esos &lt;strong&gt;cuadros
emergentes&lt;/strong&gt; que nos proporcionan información sobre algún evento que se está
produciendo en nuestro equipo (llegada de un email, canción que se está
reproduciendo, cambio de volumen, estado de batería, etc). Sustituyen a las
ventanas de dialogo que normalmente requieren la intervención del usuario para
cerrarlas y son más molestas. Además frente a las ventanas de dialogo tienen la
gran ventaja de que son gestionadas a través de una cola. Es decir, que si hay
varias notificaciones para mostrar, no se muestran todas a la vez llenándote
todo el escritorio de pequeñas ventanas, se van mostrando una después de otra.
La notificaciones aparecen en una esquina del escritorio y se desvanecen por si
solas al cabo de unos segundos. Son muy útiles porque llaman nuestra atención y
nos informan sin interrumpir nuestro trabajo.&lt;/p&gt;
&lt;p style="text-align:center;"&gt;&lt;img src="pictures/notify_example.png" width="366"
 height="147" alt="notify_example.png" title="Ejemplo de notificación"/&gt;&lt;/p&gt;

&lt;p&gt;En Ubuntu esta operación la realiza el paquete &lt;a href="https://launchpad.net/notify-osd"&gt;Notify OSD&lt;/a&gt; que se basa en
las especificaciones del proyecto &lt;a href="http://www.galago-project.org/about.php"&gt;Galago&lt;/a&gt; (&lt;a href="http://www.galago-project.org/specs/notification/0.9/"&gt;freedesktop.org Desktop
Notifications Specification&lt;/a&gt;). Este emplea el demonio &lt;em&gt;notify-osd&lt;/em&gt; para
enviar las notificaciones a través de &lt;a href="http://www.freedesktop.org/wiki/Software/dbus"&gt;D-Bus&lt;/a&gt;. Sin embargo muchos paquetes
aún emplean la librería &lt;strong&gt;libnotify&lt;/strong&gt; del proyecto Galago para enviar sus
notificaciones. Además el proyecto Galago tiene un &lt;a href="http://es.wikipedia.org/wiki/Binding"&gt;binding&lt;/a&gt; para &lt;strong&gt;Python&lt;/strong&gt;
de está librería, que es lo que emplearé en este artículo. En Ubuntu, es el
paquete &lt;a href="http://packages.ubuntu.com/es/natty/i386/python-notify"&gt;python-notify&lt;/a&gt; y se importa desde Python como &lt;code&gt;pynotify&lt;/code&gt;. También
es posible emplear el paquete &lt;a href="http://packages.ubuntu.com/natty/libnotify-bin"&gt;libnotify-bin&lt;/a&gt; que nos proporciona el comando
&lt;code&gt;notify-send&lt;/code&gt; que nos permite enviar notificaciones directamente desde la línea
de comandos, algo que puede ser muy útil para scripts en &lt;a href="http://es.wikipedia.org/wiki/Bash"&gt;&lt;em&gt;bash&lt;/em&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Ejemplo de utilización de &lt;code&gt;notify-send&lt;/code&gt;:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;notify-send &lt;span class="s2"&gt;&amp;quot;Notificación&amp;quot;&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;Esto es un ejemplo de notificación&amp;quot;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;que nos mostraría una notificación como esta:&lt;/p&gt;
&lt;p style="text-align:center;"&gt;&lt;img src="pictures/notify_send.png" width="341"
height="84" alt="notify_send.png" /&gt;&lt;/p&gt;

&lt;h2 id="notificaciones_desde_python"&gt;Notificaciones desde Python&lt;/h2&gt;
&lt;p&gt;Empleando &lt;code&gt;pynotify&lt;/code&gt; podemos emplear estas notificaciones en nuestros programas
y scripts Python y se visualizaran igual que las propias del sistema. El
problema que tiene este modulo es que no tiene documentación &lt;abbr title="As Far As I Know (en español &amp;quot;Hasta donde yo sé&amp;quot; o &amp;quot;Que yo sepa&amp;quot;)"&gt;AFAIK&lt;/abbr&gt;, por lo que
tenemos que guiarnos por algunos ejemplos (los del &lt;a href="http://www.galago-project.org/downloads.php"&gt;propio paquete&lt;/a&gt;, por
ejemplo) o experimentar con él ayudándonos de las herramientas de introspección
de Python (&lt;code&gt;help()&lt;/code&gt;, &lt;code&gt;dir()&lt;/code&gt;, &lt;a href="http://docs.python.org/library/inspect.html"&gt;inspect&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;Para poder emplearlo en mis programas he creado una función que hace uso de las
opciones principales de las notificaciones:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;pynotify&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;gtk&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;notify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;icon&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;Send notification icon messages through libnotify.&lt;/span&gt;

&lt;span class="sd"&gt;    Parameters:&lt;/span&gt;

&lt;span class="sd"&gt;    (str) title -- The notification title&lt;/span&gt;
&lt;span class="sd"&gt;    (str) msg -- The message to display into notification&lt;/span&gt;
&lt;span class="sd"&gt;    (str / uri) icon -- Type of icon (ok|info|error|warm|ask|sync) or icon file&lt;/span&gt;

&lt;span class="sd"&gt;    &amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;pynotify&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;is_initted&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
        &lt;span class="n"&gt;pynotify&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;init&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;gtk_icon&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;ok&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;gtk&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;STOCK_YES&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;info&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;gtk&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;STOCK_DIALOG_INFO&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="s"&gt;&amp;#39;error&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;gtk&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;STOCK_DIALOG_ERROR&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;warm&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;gtk&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;STOCK_DIALOG_WARNING&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="s"&gt;&amp;#39;ask&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;gtk&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;STOCK_DIALOG_QUESTION&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;sync&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;gtk&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;STOCK_JUMP_TO&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;note&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pynotify&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Notification&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;helper&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;gtk&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Button&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="n"&gt;gtk_icon&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;helper&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;render_icon&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;gtk_icon&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;icon&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;gtk&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ICON_SIZE_BUTTON&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;note&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;set_icon_from_pixbuf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;gtk_icon&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="ne"&gt;KeyError&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;note&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pynotify&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Notification&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;icon&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;note&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;show&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Básicamente llamamos a la función y le decimos que titulo le queremos poner, el
mensaje y el icono que queremos mostrar. En cuanto al icono se puede emplear
ninguno, uno personalizado indicando la ruta al fichero o un icono estándar de
GTK para las opciones más comunes. Veamos lo sencillo de su funcionamiento con
ejemplos:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="c"&gt;# Notificación sin icono&lt;/span&gt;
&lt;span class="gp"&gt;... &lt;/span&gt;&lt;span class="n"&gt;notify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;Titulo&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;Esta notificación no lleva icono&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="go"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p style="text-align:center;"&gt;&lt;img src="pictures/notify_noicon.png" width="339"
height="87" alt="notify_noicon.png" /&gt;&lt;/p&gt;

&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="c"&gt;# Notificación con icono personalizado&lt;/span&gt;
&lt;span class="gp"&gt;... &lt;/span&gt;&lt;span class="n"&gt;notify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;Ubuntu&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;Esta lleva el icono personalizado&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="go"&gt;&amp;quot;/usr/share/icons/gnome/scalable/places/ubuntu-logo.svg&amp;quot;)&lt;/span&gt;
&lt;span class="go"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p style="text-align:center;"&gt;&lt;img src="pictures/notify_ubuntu.png" width="339"
height="87" alt="notify_ubuntu.png" /&gt;&lt;/p&gt;

&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="c"&gt;# Notificación de información (GTK)&lt;/span&gt;
&lt;span class="gp"&gt;... &lt;/span&gt;&lt;span class="n"&gt;notify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;Información&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;Esta emplea el icono de información estándar de GTK&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;info&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="go"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p style="text-align:center;"&gt;&lt;img src="pictures/notify_info.png" width="339"
height="87" alt="notify_info.png" /&gt;&lt;/p&gt;

&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="c"&gt;# Notificación de error (GTK)&lt;/span&gt;
&lt;span class="gp"&gt;... &lt;/span&gt;&lt;span class="n"&gt;notify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;Error!&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;Ejemplo de reporte de error (GTK)&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;error&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="go"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p style="text-align:center;"&gt;&lt;img src="pictures/notify_error.png" width="338"
height="87" alt="notify_error.png" /&gt;&lt;/p&gt;

&lt;p&gt;Como se puede ver el proceso es realmente sencillo y hacer esto puede aportar a
nuestros programas/scripts ese toque que le faltaba y que tanto contribuye a la
usabilidad de los mismos. Yo por ejemplo, lo empleo en scripts programados para
saber cuando se activan y cuando terminan, sobre todo cuando son procesos
largos. Un ejemplo puede verse en el código de &lt;a href="/sincronizar-una-carpeta-local-y-una-remota-a-traves-de-ftp-lftp-mirror.html"&gt;Sincronizar una carpeta local y
una remota a través de FTP: lftp-mirror&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;El fichero con este código, &lt;a href="https://bitbucket.org/joedicastro/python-recipes/src/tip/src/notify.py"&gt;notify.py&lt;/a&gt; se encuentra en
&lt;a href="https://bitbucket.org/joedicastro/python-recipes"&gt;mi repositorio&lt;/a&gt;.&lt;/p&gt;
&lt;div class="footnote"&gt;
&lt;hr /&gt;
&lt;ol&gt;
&lt;li id="fn:1"&gt;
&lt;p&gt;Se puede emplear también en otras distribuciones Linux
&amp;#160;&lt;a href="#fnref:1" rev="footnote" title="Jump back to footnote 1 in the text"&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;</summary><category term="linux"></category><category term="ubuntu"></category><category term="python"></category></entry><entry><title>Algoritmos Shuffle</title><link href="http://joedicastro.com/algoritmos-shuffle.html" rel="alternate"></link><updated>2011-06-09T23:35:00+02:00</updated><author><name>joe di castro</name></author><id>http://joedicastro.com/algoritmos-shuffle.html</id><summary type="html">&lt;p&gt;Los algoritmos &lt;strong&gt;shuffle&lt;/strong&gt; se emplean para barajar o desordenar los elementos de
 una lista (como si fueran las cartas de una baraja) al azar. Se intenta 
 reproducir lo que ocurre en la realidad cuando barajamos un mazo de cartas o 
 extraemos bolas de un bombo para formar una combinación. El comportamiento 
 ideal de estos algoritmos debe de ser imparcial, es decir que todas las 
 posibles &lt;a href="http://es.wikipedia.org/wiki/Permutaci%C3%B3n"&gt;permutaciones&lt;/a&gt; de esta lista tengan las mismas oportunidades
 de aparecer como resultado. Este reparto uniforme es clave para el buen 
 funcionamiento del mismo, pues de no ser así se crearían tendencias y el 
 resultado seria predecible, lo que acabaría con la aleatoriedad que se 
 pretende conseguir.&lt;/p&gt;
&lt;p&gt;Imaginemos tres distintas cartas de una baraja inglesa: As, Rey y Reina (Ace, 
King &amp;amp; Queen) como una lista en &lt;strong&gt;Python&lt;/strong&gt;, y ahora veamos todas las posibles 
permutaciones de la misma (&lt;em&gt;n!&lt;/em&gt;, es decir, &lt;em&gt;3! = 6&lt;/em&gt;):&lt;/p&gt;
&lt;p style="text-align:center;"&gt;&lt;img src="pictures/permutations.png" width="500" 
height="249" alt="permutations.png" /&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Representación gráfica de las distintas permutaciones.&lt;sup id="fnref:diseño"&gt;&lt;a href="#fn:diseño" rel="footnote"&gt;1&lt;/a&gt;&lt;/sup&gt;&lt;/em&gt;&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="n"&gt;lista&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;A&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;K&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;Q&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;itertools&lt;/span&gt;
&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;itertools&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;permutations&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;lista&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;span class="gp"&gt;... &lt;/span&gt;    &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="gp"&gt;...&lt;/span&gt;
&lt;span class="go"&gt;(&amp;#39;A&amp;#39;, &amp;#39;K&amp;#39;, &amp;#39;Q&amp;#39;)&lt;/span&gt;
&lt;span class="go"&gt;(&amp;#39;A&amp;#39;, &amp;#39;Q&amp;#39;, &amp;#39;K&amp;#39;)&lt;/span&gt;
&lt;span class="go"&gt;(&amp;#39;K&amp;#39;, &amp;#39;A&amp;#39;, &amp;#39;Q&amp;#39;)&lt;/span&gt;
&lt;span class="go"&gt;(&amp;#39;K&amp;#39;, &amp;#39;Q&amp;#39;, &amp;#39;A&amp;#39;)&lt;/span&gt;
&lt;span class="go"&gt;(&amp;#39;Q&amp;#39;, &amp;#39;A&amp;#39;, &amp;#39;K&amp;#39;)&lt;/span&gt;
&lt;span class="go"&gt;(&amp;#39;Q&amp;#39;, &amp;#39;K&amp;#39;, &amp;#39;A&amp;#39;)&lt;/span&gt;
&lt;span class="go"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Vemos entonces que existen 6 permutaciones posibles que pueden darse como 
resultado de barajar las cartas. Ahora imaginemos que escogemos una estas 
permutaciones, y jugamos a barajar las cartas una y otra vez partiendo siempre 
desde esta permutación. Como ya adelantábamos, si el algoritmo shuffle fuese 
perfecto, el resultado de estas operaciones debería repartirse de forma 
imparcial (equiprobable) entre las distintas permutaciones posibles. Es decir, 
que al ejecutar un fragmento de código como el que sigue, donde empleamos el 
algoritmo shuffle que por defecto trae Python (en el modulo &lt;strong&gt;random&lt;/strong&gt; de la 
librería estándar), &lt;code&gt;ramdom.suffle()&lt;/code&gt;&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;random&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;shuffle&lt;/span&gt;
&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;span class="gp"&gt;... &lt;/span&gt;    &lt;span class="n"&gt;lista&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;A&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;K&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;Q&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="gp"&gt;... &lt;/span&gt;    &lt;span class="n"&gt;shuffle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;lista&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="gp"&gt;... &lt;/span&gt;    &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;lista&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="gp"&gt;...&lt;/span&gt;
&lt;span class="go"&gt;[&amp;#39;A&amp;#39;, &amp;#39;K&amp;#39;, &amp;#39;Q&amp;#39;]&lt;/span&gt;
&lt;span class="go"&gt;[&amp;#39;K&amp;#39;, &amp;#39;Q&amp;#39;, &amp;#39;A&amp;#39;]&lt;/span&gt;
&lt;span class="go"&gt;[&amp;#39;A&amp;#39;, &amp;#39;K&amp;#39;, &amp;#39;Q&amp;#39;]&lt;/span&gt;
&lt;span class="go"&gt;[&amp;#39;Q&amp;#39;, &amp;#39;A&amp;#39;, &amp;#39;K&amp;#39;]&lt;/span&gt;
&lt;span class="go"&gt;[&amp;#39;K&amp;#39;, &amp;#39;Q&amp;#39;, &amp;#39;A&amp;#39;]&lt;/span&gt;
&lt;span class="go"&gt;[&amp;#39;Q&amp;#39;, &amp;#39;K&amp;#39;, &amp;#39;A&amp;#39;]&lt;/span&gt;
&lt;span class="go"&gt;[&amp;#39;A&amp;#39;, &amp;#39;K&amp;#39;, &amp;#39;Q&amp;#39;]&lt;/span&gt;
&lt;span class="go"&gt;[&amp;#39;Q&amp;#39;, &amp;#39;K&amp;#39;, &amp;#39;A&amp;#39;]&lt;/span&gt;
&lt;span class="go"&gt;[&amp;#39;K&amp;#39;, &amp;#39;Q&amp;#39;, &amp;#39;A&amp;#39;]&lt;/span&gt;
&lt;span class="go"&gt;[&amp;#39;K&amp;#39;, &amp;#39;A&amp;#39;, &amp;#39;Q&amp;#39;]&lt;/span&gt;
&lt;span class="go"&gt;[&amp;#39;Q&amp;#39;, &amp;#39;K&amp;#39;, &amp;#39;A&amp;#39;]&lt;/span&gt;
&lt;span class="go"&gt;[&amp;#39;Q&amp;#39;, &amp;#39;A&amp;#39;, &amp;#39;K&amp;#39;]&lt;/span&gt;
&lt;span class="go"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;los resultados en pantalla deberían irse repartiendo a partes iguales entre las 
distintas permutaciones. Del mismo modo, si ejecutáramos este algoritmo 
&lt;em&gt;600.000&lt;/em&gt; veces sobre la misma permutación, los resultados deberían repartirse 
de esta forma:&lt;/p&gt;
&lt;p style="text-align:center;"&gt;&lt;img src="pictures/ideal_shuffle.png" width="373" 
height="248" alt="ideal_shuffle.png" /&gt;&lt;/p&gt;

&lt;p&gt;Donde las apariciones de cada permutación posible son idénticas, es decir, 
&lt;em&gt;600.000/6 = 100.000&lt;/em&gt;. Pero veremos que en la realidad ocurre algo distinto, 
como ya podíamos adivinar al ver los resultados del código anterior. Veamos con 
detalle que  es lo que ocurre con el algoritmo &lt;code&gt;shuffle&lt;/code&gt; para ese mismo número 
de ejecuciones.&lt;/p&gt;
&lt;p style="text-align:center;"&gt;&lt;img src="pictures/realvsideal_radar.png" 
width="680" height="248" alt="realvsideal_radar.png" /&gt;&lt;/p&gt;

&lt;p&gt;Como podemos ver, el comportamiento real de un algoritmo shuffle no se ajusta 
al comportamiento ideal. Unas combinaciones están sobre-representadas como QAK 
y KQA y el resto sub-representadas, siendo la combinación AQK la más cercana a 
los valores ideales. Pero estos valores no debemos verlos como una tendencia, 
puesto que si volvemos a ejecutar otras &lt;em&gt;100.000&lt;/em&gt; operaciones de barajado, aunque 
seguirá sin ser un reparto equitativo, si es imparcial en cuanto que no se 
repiten las mismas desviaciones por permutación. Es decir, unas veces unas serán 
unas permutaciones las más sobre-representadas (y viceversa) y otras lo serán 
otras distintas, no hay una tendencia clara. Las variaciones en este caso oscilan 
entre &lt;em&gt;99.678 (-322)&lt;/em&gt; y &lt;em&gt;100.446 (+446)&lt;/em&gt; apariciones sobre un valor ideal de 
&lt;em&gt;100.000&lt;/em&gt; con una &lt;a href="http://es.wikipedia.org/wiki/Desviaci%C3%B3n_media"&gt;desviación media&lt;/a&gt; de &lt;em&gt;±0.039%&lt;/em&gt;, unos valores no ideales 
pero si muy razonables y más que suficientes para la mayoría de necesidades.&lt;/p&gt;
&lt;p&gt;Es importante volver a incidir en que un algoritmo de barajado bien diseñado 
siempre ha de ser imparcial, de hecho &lt;code&gt;random.shuffle&lt;/code&gt; lo es, si no será erróneo 
y nos creara tendencias que pueden pasar inadvertidas a no ser que se comprueben 
correctamente (en este caso de forma estadística). Tendencias que pueden suponer 
un verdadero problema y echar al traste todos los cálculos y procesos que empleen 
este algoritmo como base. El emplear un algoritmo de barajado erróneo le ha 
causado &lt;a href="http://www.cigital.com/news/index.php?pg=art&amp;amp;artid=20"&gt;más de un&lt;/a&gt; &lt;a href="http://www.cigital.com/papers/download/developer_gambling.php"&gt;disgusto a algún casino online&lt;/a&gt;, ya que las 
tendencias permiten predecir el resultado de el barajado y usarlo para hacer 
trampas en el juego.&lt;/p&gt;
&lt;p&gt;Pero si &lt;code&gt;random.suffle&lt;/code&gt; es imparcial, ¿por qué no se reparten los resultados de 
manera uniforme? El problema es aquí entra en juego un segundo actor, que 
independientemente de lo bien diseñado que este nuestro algoritmo shuffle, nos 
condicionará que el reparto sea uniforme o no. Este segundo actor es la 
&lt;a href="http://es.wikipedia.org/wiki/Aleatoriedad"&gt;aleatoriedad&lt;/a&gt;, lo que precisamente intentamos conseguir. Para que nuestro 
algoritmo funcione necesitamos que se elijan las cartas al azar para ir 
desordenándolas también al azar. Y es aquí donde está el problema, el que 
impide que &lt;code&gt;random.shuffle&lt;/code&gt; tenga un comportamiento ideal, que los ordenadores y 
la aleatoriedad, no son buenos "amigos". No existe algo así como un generador de 
números aleatorios perfecto basado en software (si los hay casi perfectos 
basados en hardware), debido a que son deterministas al estar basados en formulas 
matemáticas. Por lo que empleamos lo que se denomina &lt;a href="http://es.wikipedia.org/wiki/Generador_de_n%C3%BAmeros_pseudoaleatorios"&gt;generadores de números 
pseudoaleatorios&lt;/a&gt;. Estos generadores pueden ser más o menos "perfectos", pero 
al no generar verdadera aleatoriedad, nos impiden que nuestro algoritmo consiga 
efectuar un reparto uniforme. Matemáticamente se ha demostrado que algunos 
algoritmos, como el que emplea &lt;code&gt;ramdon.shuffle&lt;/code&gt; (luego aclararemos cual es) 
tienen un funcionamiento ideal, y de hecho podemos acercarnos muchísimo al 
modelo ideal cuando empleamos un &lt;a href="http://en.wikipedia.org/wiki/Hardware_random_number_generator"&gt;generador de aleatoriedad por hardware&lt;/a&gt; 
(que se acerca mucho a la verdadera aleatoriedad). Al final, aunque sean 
matemáticamente correctos, tu algoritmo de barajado será tan bueno como lo sea 
tu generador de números aleatorios.&lt;/p&gt;
&lt;p&gt;Python, en su modulo &lt;code&gt;random&lt;/code&gt;, emplea por defecto uno de los mejores generadores 
de números pseudoaleatorios existentes actualmente, el &lt;a href="http://en.wikipedia.org/wiki/Mersenne_twister"&gt;Mersenne twister&lt;/a&gt; 
(hasta la versión 2.4 se empleaba el &lt;a href="http://www2.imperial.ac.uk/~hohs/S9/2007-2008/wichmannhill.pdf"&gt;Wichmann-Hill&lt;/a&gt;, que sigue disponible 
como clase) y está implementado en C. Los resultados entregados por él son muy 
fiables (excepto para su empleo en &lt;a href="http://es.wikipedia.org/wiki/Criptograf%C3%ADa"&gt;criptografía&lt;/a&gt;), generando números float 
con una precisión de 53 bits y con un elevado periodo de 2¹⁹⁹³⁷-1. Otra buena 
opción que nos ofrece el modulo &lt;code&gt;random&lt;/code&gt; para entornos Unix/Linux es la clase 
&lt;code&gt;random.SystemRandom()&lt;/code&gt; que nos ofrece la entropía generada por &lt;code&gt;os.random()&lt;/code&gt; 
que a su vez la toma de la generada en &lt;code&gt;/dev/random&lt;/code&gt;. Esta es una fuente 
bastante decente de aleatoriedad que en las pruebas me ha ofrecido resultados 
similares a el generador por defecto en Python.&lt;/p&gt;
&lt;h2 id="los_algoritmos"&gt;Los algoritmos&lt;/h2&gt;
&lt;p&gt;Utilizar la función &lt;code&gt;random.shuffle()&lt;/code&gt; es la forma más cómoda de barajar una 
lista en Python, y desde luego la más recomendable. Pero si quisiéramos 
desarrollar nuestro propio algoritmo, esta es una de esas ocasiones en las que o 
tienes unos solidos conocimientos matemáticos (o estadísticos) o mejor no 
intentes reinventar la rueda, recurre a lo conocido y solido, y como mínimo 
prueba siempre que los resultados que obtienes son lo que tu esperas. Esto no es 
una afirmación gratuita, esta es una de las ocasiones en la que la diferencia es 
tan sutil, que es facilísimo equivocarse sin siquiera darse cuenta. Veamos porqué.&lt;/p&gt;
&lt;h3 id="la_intuici+n_a_veces_nos_enga+a_no_seas_ingenuo"&gt;La intuición a veces nos engaña, no seas ingenuo!&lt;/h3&gt;
&lt;p&gt;Aparentemente barajar una lista es de lo más sencillo, después de razonar un 
poco, podríamos acabar con un código parecido a este:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;faulty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;lst&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="s"&gt;&amp;quot;An example of a intuitive but very bad algorithm.&amp;quot;&lt;/span&gt;
    &lt;span class="n"&gt;lst_length&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;lst&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;lst_length&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;j&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;random&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;randrange&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;lst_length&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;lst&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;lst&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;j&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;lst&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;j&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;lst&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Que aparentemente debería ser correcto, algo que han pensado muchos 
programadores ingenuamente hasta caer en la cuenta "a la fuerza" de que no es 
correcto. Es más, si por ejemplo nos limitáramos a ejecutarlo unas &lt;em&gt;360&lt;/em&gt; veces 
para probarlo (las cuales aparentemente no son pocas), veríamos que tiene un 
comportamiento muy parecido al que nos da la función &lt;code&gt;random.shuffle&lt;/code&gt;:&lt;/p&gt;
&lt;p style="text-align:center;"&gt;&lt;img src="pictures/shuffle_faulty.png" width="467" 
height="350" alt="shuffle_faulty.png" /&gt;&lt;/p&gt;

&lt;p&gt;Como podemos ver las desviaciones del valor ideal son muy similares y con una 
desviación media muy parecida, &lt;em&gt;±2.130%&lt;/em&gt; para &lt;code&gt;shuffle&lt;/code&gt; y &lt;em&gt;±2.222%&lt;/em&gt; para nuestro 
algoritmo, &lt;code&gt;faulty&lt;/code&gt;. Y creeríamos erróneamente que nuestro algoritmo funcionaría 
correctamente. Pero es que para comprobar que no haya tendencia alguna es 
necesario ejecutarlo, muchas, pero muchas veces (por encima de los cientos de 
miles), ya que a mayor número de repeticiones, más se manifiestan esas 
tendencias. Al mismo tiempo, cuantos más elementos tenga la lista, mayores son 
las desviaciones y la tendencia. Además, con este número de ejecuciones aún no 
observamos las tendencias reales que se manifestarán luego en lo sucesivo. 
Veamos unos ejemplos para &lt;em&gt;2.400.000&lt;/em&gt; ejecuciones para nuestra lista de tres 
cartas y para otra a la que hemos añadido la Sota (Jack).&lt;/p&gt;
&lt;p style="text-align:center;"&gt;&lt;a href="pictures/shuffle_faulty_2M4.png"&gt;&lt;img 
src="pictures/shuffle_faulty_2m4_small.png" width="700" height="245" 
alt="shuffle_faulty_2m4_small.png" title="Pulsar para ver a tamaño completo"/&gt;
&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;En el gráfico de la izquierda ya se muestran claramente las diferencias, mientras 
la función &lt;code&gt;shuffle&lt;/code&gt; varía entre &lt;em&gt;-741&lt;/em&gt; y &lt;em&gt;+668&lt;/em&gt; (sobre &lt;em&gt;400.000&lt;/em&gt;) con una 
desviación media de &lt;em&gt;±0.017%&lt;/em&gt;, nuestra función tiene una desviación media de 
&lt;em&gt;±1.869%&lt;/em&gt; (&lt;em&gt;~110&lt;/em&gt; veces superior) y varia entre &lt;em&gt;-46.062&lt;/em&gt; y &lt;em&gt;+46.090&lt;/em&gt;. Como 
podemos ver es una autentica barbaridad. El gráfico de la derecha, nos confirma 
que a mayor número de elementos en la lista, mayores aún son las tendencias. 
Mientras que &lt;code&gt;shuffle&lt;/code&gt; se sigue comportando correctamente con una desviación 
media de &lt;em&gt;±0.009%&lt;/em&gt; y varía entre &lt;em&gt;-486&lt;/em&gt; y &lt;em&gt;+427&lt;/em&gt; (sobre &lt;em&gt;100.000&lt;/em&gt;), nuestro 
pobre algoritmo se desmanda completamente, variando entre &lt;em&gt;+31.326&lt;/em&gt; y &lt;em&gt;-25.254&lt;/em&gt;, 
una desviación media de &lt;em&gt;±0.541%&lt;/em&gt; (&lt;em&gt;~61&lt;/em&gt; veces más). Hay que tener en cuenta, 
que al ser mayor número de cartas, son &lt;em&gt;24&lt;/em&gt; permutaciones, lo que nos da unas 
&lt;em&gt;4&lt;/em&gt; veces menos repeticiones por permutación, es decir, que para tener el mismo 
número tendríamos que hacer &lt;em&gt;9.600.00&lt;/em&gt; ejecuciones. Y como podemos ver, los 
resultados no son cuatro veces mejores, ni mucho menos, además sabemos que la 
tendencia se acentúa con el número de ejecuciones. Viendo estos resultados vemos 
claramente que determinadas permutaciones tienen muy pocas probabilidades de 
salir, cuando otras son altamente predecibles.&lt;/p&gt;
&lt;p&gt;¿Entiendes ahora porqué no debes fiarte únicamente de tu instinto?&lt;/p&gt;
&lt;p&gt;Vamos a conocer ahora los algoritmos que funcionan, están matemáticamente 
comprobados y son sobradamente conocidos (aunque a la vista está que no lo 
suficiente).&lt;/p&gt;
&lt;h3 id="fisher-yates"&gt;Fisher-Yates&lt;/h3&gt;
&lt;p&gt;Este algoritmo fue desarrollado por &lt;a href="http://en.wikipedia.org/wiki/Ronald_Fisher"&gt;Ronald Fisher&lt;/a&gt; y &lt;a href="http://en.wikipedia.org/wiki/Frank_Yates"&gt;Frank Yates&lt;/a&gt; en 
1938&lt;sup id="fnref:FYa"&gt;&lt;a href="#fn:FYa" rel="footnote"&gt;2&lt;/a&gt;&lt;/sup&gt; como un método a realizar con lápiz y papel. Para ello empleaban una 
tabla de números aleatorios calculada anteriormente para generar la aleatoriedad 
que necesitaban para el algoritmo. Este algoritmo esta comprobado 
matemáticamente y funciona correctamente siempre que la fuente de aleatoriedad 
sea completamente aleatoria. No deja de ser curioso, que un algoritmo que fue 
creado hace más de 70 años y que sigue actualmente vigente y presente en 
multitud de aplicaciones, sea al mismo tiempo desconocido por muchos 
desarrolladores, lo que les lleva a cometer los errores que comentaba 
anteriormente al implementar los suyos propios. Por eso como programadores 
deberíamos ser humildes y procurar no inventar siempre lo ya inventado y 
buscar siempre una solución previa para nuestros propósitos. Y probar, probar y 
volver a probar nuestro código...&lt;/p&gt;
&lt;p&gt;La típica implementación del algoritmo en Python sería así:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;fisher_yates&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;lst&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="s"&gt;&amp;quot;Python implementation of the original Fisher-Yates algorithm.&amp;quot;&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;lst&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;idx&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;lst&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
        &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="n"&gt;idx&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;sel&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;random&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;randint&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;idx&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;lst&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;idx&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;lst&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;sel&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;lst&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;sel&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;lst&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;idx&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
            &lt;span class="n"&gt;idx&lt;/span&gt; &lt;span class="o"&gt;-=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;h3 id="knuth-durstenfeld"&gt;Knuth-Durstenfeld&lt;/h3&gt;
&lt;p&gt;Este algoritmo es en realidad una variante del Fisher-Yates y fue publicado 
originalmente por Richard Durstenfeld en 1964&lt;sup id="fnref:RDa"&gt;&lt;a href="#fn:RDa" rel="footnote"&gt;3&lt;/a&gt;&lt;/sup&gt;, pero fue popularizado por 
el genial &lt;a href="http://en.wikipedia.org/wiki/Donald_E._Knuth"&gt;Donald Knuth&lt;/a&gt; en el volumen 2 de su gran obra, 
&lt;a href="http://en.wikipedia.org/wiki/The_Art_of_Computer_Programming"&gt;The Art of Computer Programming&lt;/a&gt;, como el &lt;strong&gt;algoritmo P&lt;/strong&gt;&lt;sup id="fnref:DKa"&gt;&lt;a href="#fn:DKa" rel="footnote"&gt;4&lt;/a&gt;&lt;/sup&gt;. De ahí 
que sea generalmente conocido como Knuth-Durstenfeld o simplemente como Knuth 
Shuffle. Aunque curiosamente ninguno de los dos conocía anteriormente el trabajo 
previo de Fisher y Yates. Este algoritmo se ejecuta en tiempo polinómico lineal 
&lt;em&gt;O(n)&lt;/em&gt;, por lo que es muy eficiente.&lt;/p&gt;
&lt;p&gt;Implementado en Python, sería algo así:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;knuth_durstenfeld&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;lst&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="s"&gt;&amp;quot;Python implementation of the Durstenfeld algorithm popularized by Knuth.&amp;quot;&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;idx&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;reversed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;lst&lt;/span&gt;&lt;span class="p"&gt;))):&lt;/span&gt;
        &lt;span class="c"&gt;# pick an element in lst[:idx+1] with which to exchange lst[idx]&lt;/span&gt;
        &lt;span class="n"&gt;sel&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;random&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;randrange&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;idx&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;lst&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;idx&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;lst&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;sel&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;lst&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;sel&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;lst&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;idx&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Este algoritmo es el que está detrás de la función &lt;code&gt;random.shuffle&lt;/code&gt; que hemos 
venido usando desde el principio del articulo. Lógicamente si está empleando el 
mejor algoritmo hasta la fecha, es absurdo implementar el nuestro propio cuando 
podemos usar esta función estándar de Python. De hecho el código de esta función 
es el siguiente:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;shuffle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;random&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;x, random=random.random -&amp;gt; shuffle list x in place; return None.&lt;/span&gt;

&lt;span class="sd"&gt;    Optional arg random is a 0-argument function returning a random&lt;/span&gt;
&lt;span class="sd"&gt;    float in [0.0, 1.0); by default, the standard random.random.&lt;/span&gt;
&lt;span class="sd"&gt;    &amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;random&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;random&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;random&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;reversed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;xrange&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;))):&lt;/span&gt;
        &lt;span class="c"&gt;# pick an element in x[:i+1] with which to exchange x[i]&lt;/span&gt;
        &lt;span class="n"&gt;j&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;random&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;j&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;j&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Que como podemos ver, el algoritmo es el Knuth Shuffle.&lt;/p&gt;
&lt;h3 id="a_tener_en_cuenta"&gt;A tener en cuenta&lt;/h3&gt;
&lt;p&gt;Conviene tener en cuenta una cosa, todos estos algoritmos modifican la propia 
lista &lt;em&gt;in-place&lt;/em&gt;, es decir que no crean una nueva, y por lo tanto no devuelven 
ningún valor. Por lo tanto, no puedes asignar el resultado de una de estas 
operaciones a una variable, porque por defecto devuelven &lt;code&gt;None&lt;/code&gt;. Es un error 
muy común, de hecho &lt;a href="http://learning-python.com/formalbio.html"&gt;Mark Lutz&lt;/a&gt; lo define en su libro "Learning Python" 
como uno de los &lt;em&gt;Common Coding Gotchas&lt;/em&gt;, es decir, uno de los errores más 
comunes en Python. Y dice claramente (pág. 388, cap. 15)&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Don’t expect results from functions that change objects in-place&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;No esperes resultados desde funciones que cambien objetos en el sitio (sin crear
 nuevos objetos modificados). Y pone como ejemplos los métodos &lt;code&gt;append&lt;/code&gt;, &lt;code&gt;sort&lt;/code&gt; 
y &lt;code&gt;reverse&lt;/code&gt; en listas.&lt;/p&gt;
&lt;p&gt;Para verlo aún más claro, nada mejor que verlo con un ejemplo:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;random&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;shuffle&lt;/span&gt;
&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="n"&gt;lista&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;lista&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c"&gt;# Obtenemos el id del objeto (todo en Python es un objeto)&lt;/span&gt;
&lt;span class="go"&gt;3077959916L&lt;/span&gt;
&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="n"&gt;shuffle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;lista&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="n"&gt;lista&lt;/span&gt;      &lt;span class="c"&gt;# La lista ya aparece barajada&lt;/span&gt;
&lt;span class="go"&gt;[2, 3, 1]&lt;/span&gt;
&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;lista&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c"&gt;# Sin embargo vemos que el identificador es el mismo&lt;/span&gt;
&lt;span class="go"&gt;3077959916L&lt;/span&gt;
&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="n"&gt;variable&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;shuffle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;lista&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c"&gt;# Esto no tiene sentido, porque no se devuelve nada&lt;/span&gt;
&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="n"&gt;variable&lt;/span&gt;
&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;shuffle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;lista&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;  &lt;span class="c"&gt;# Aquí vemos que la función devuelve None&lt;/span&gt;
&lt;span class="go"&gt;None&lt;/span&gt;
&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;lista&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="go"&gt;3077959916L&lt;/span&gt;
&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="n"&gt;lista&lt;/span&gt;
&lt;span class="go"&gt;[1, 3, 2]&lt;/span&gt;
&lt;span class="go"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;En esta gráfica vemos los tres algoritmos comparados, los he ejecutado 
&lt;em&gt;solamente 48.000&lt;/em&gt; veces, para apreciar mejor las diferencias entre ellos.&lt;/p&gt;
&lt;p style="text-align:center;"&gt;&lt;img src="pictures/shuffle_algorithms.png" 
width="600" height="500" alt="suffle_algorithms.png" /&gt;&lt;/p&gt;

&lt;p&gt;Las desviaciones medias son de &lt;em&gt;±0.068%&lt;/em&gt; para Fisher-Yates, &lt;em&gt;±0.069%&lt;/em&gt; para 
Knuth-Durstenfeld y de ±0.554% para Faulty.&lt;/p&gt;
&lt;h3 id="sattolo-cycle"&gt;Sattolo-Cycle&lt;/h3&gt;
&lt;p&gt;Este algoritmo es muy parecido a los anteriores, pero con una significativa 
diferencia, este algoritmo solo genera ciclos, de ahí su nombre. Es decir, 
reparte uniformemente los resultados solo entre algunas permutaciones que se van 
rotando (aunque comparte el mismo problema con los anteriores al depender de una 
aleatoriedad no perfecta). Para resumir su funcionamiento, lo que hace es que 
después de ejecutarlo, ningún elemento de la lista repite la posición anterior 
que tenía en la misma. Ese algoritmo fue publicado por Sandra Sattolo en 1986&lt;/p&gt;
&lt;p&gt;El algoritmo en Python:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;sattolo_cycle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;lst&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="s"&gt;&amp;quot;Python implementation of the original Sattolo Cycle algorithm.&amp;quot;&lt;/span&gt;
    &lt;span class="n"&gt;idx&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;lst&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="n"&gt;idx&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;idx&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;idx&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
        &lt;span class="n"&gt;sel&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;random&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;randrange&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;idx&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c"&gt;# 0 &amp;lt;= sel &amp;lt;= idx-1&lt;/span&gt;
        &lt;span class="n"&gt;lst&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;sel&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;lst&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;idx&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;lst&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;idx&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;lst&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;sel&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Podemos ver aquí un ejemplo de su funcionamiento al ejecutarlo sucesivamente 
sobre la misma lista, sin partir siempre de la misma permutación, para comprobar 
como ningún elemento conserva su posición anterior.&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="go"&gt;lista = [&amp;#39;A&amp;#39;, &amp;#39;K&amp;#39;, &amp;#39;Q&amp;#39;]&lt;/span&gt;
&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;span class="gp"&gt;... &lt;/span&gt;    &lt;span class="n"&gt;sattolo_cycle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;lista&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="gp"&gt;... &lt;/span&gt;    &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;lista&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="gp"&gt;...&lt;/span&gt;
&lt;span class="go"&gt;[&amp;#39;K&amp;#39;, &amp;#39;Q&amp;#39;, &amp;#39;A&amp;#39;]&lt;/span&gt;
&lt;span class="go"&gt;[&amp;#39;A&amp;#39;, &amp;#39;K&amp;#39;, &amp;#39;Q&amp;#39;]&lt;/span&gt;
&lt;span class="go"&gt;[&amp;#39;K&amp;#39;, &amp;#39;Q&amp;#39;, &amp;#39;A&amp;#39;]&lt;/span&gt;
&lt;span class="go"&gt;[&amp;#39;A&amp;#39;, &amp;#39;K&amp;#39;, &amp;#39;Q&amp;#39;]&lt;/span&gt;
&lt;span class="go"&gt;[&amp;#39;Q&amp;#39;, &amp;#39;A&amp;#39;, &amp;#39;K&amp;#39;]&lt;/span&gt;
&lt;span class="go"&gt;[&amp;#39;A&amp;#39;, &amp;#39;K&amp;#39;, &amp;#39;Q&amp;#39;]&lt;/span&gt;
&lt;span class="go"&gt;[&amp;#39;K&amp;#39;, &amp;#39;Q&amp;#39;, &amp;#39;A&amp;#39;]&lt;/span&gt;
&lt;span class="go"&gt;[&amp;#39;Q&amp;#39;, &amp;#39;A&amp;#39;, &amp;#39;K&amp;#39;]&lt;/span&gt;
&lt;span class="go"&gt;[&amp;#39;K&amp;#39;, &amp;#39;Q&amp;#39;, &amp;#39;A&amp;#39;]&lt;/span&gt;
&lt;span class="go"&gt;[&amp;#39;Q&amp;#39;, &amp;#39;A&amp;#39;, &amp;#39;K&amp;#39;]&lt;/span&gt;
&lt;span class="go"&gt;[&amp;#39;K&amp;#39;, &amp;#39;Q&amp;#39;, &amp;#39;A&amp;#39;]&lt;/span&gt;
&lt;span class="go"&gt;[&amp;#39;Q&amp;#39;, &amp;#39;A&amp;#39;, &amp;#39;K&amp;#39;]&lt;/span&gt;
&lt;span class="go"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;El código de estos algoritmos y los test de rendimiento se encuentran en el 
fichero &lt;a href="https://bitbucket.org/joedicastro/python-recipes/src/tip/src/shuffle.py"&gt;shuffle.py&lt;/a&gt; de &lt;a href="https://bitbucket.org/joedicastro/python-recipes"&gt;mi repositorio&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Aunque estos algoritmos no tienen un funcionamiento perfecto, debido al 
generador de números pseudoaleatorios, son validos para la mayoría de las 
aplicaciones para las que son necesarios, digamos que son lo suficientemente 
buenos para las aplicaciones prácticas de los mismos.&lt;/p&gt;
&lt;h4 id="otras_fuentes_para_saber_m+s"&gt;Otras fuentes para saber más&lt;/h4&gt;
&lt;p&gt;&lt;a href="http://www.codinghorror.com/blog/2007/12/the-danger-of-naivete.html"&gt;The Danger of Naïveté&lt;/a&gt;, Jeff Atwood&lt;br /&gt;
&lt;a href="http://www.codinghorror.com/blog/2007/12/shuffling.html"&gt;Shuffling&lt;/a&gt;, Jeff Atwood&lt;br /&gt;
&lt;a href="http://www.codinghorror.com/blog/2006/11/computers-are-lousy-random-number-generators.html"&gt;Computers are lousy random numbers generators&lt;/a&gt;, Jeff Atwood&lt;br /&gt;
&lt;a href="http://eli.thegreenplace.net/2010/05/28/the-intuition-behind-fisher-yates-shuffling/"&gt;The intuition behind Fisher-Yates shuffling&lt;/a&gt;, Eli Bendersky&lt;br /&gt;
&lt;a href="http://www.random.org/"&gt;Ramdom.org&lt;/a&gt;&lt;/p&gt;
&lt;div class="footnote"&gt;
&lt;hr /&gt;
&lt;ol&gt;
&lt;li id="fn:diseño"&gt;
&lt;p&gt;Diseño de las cartas por &lt;a href="http://svg-cards.sourceforge.net/"&gt;David Bellot&lt;/a&gt;.
&amp;#160;&lt;a href="#fnref:diseño" rev="footnote" title="Jump back to footnote 1 in the text"&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:FYa"&gt;
&lt;p&gt;Fisher R., Yates F. [1938] (1943) &lt;em&gt;Statistical Tables for Biological, 
 Agricultural and Medical Research&lt;/em&gt;, 2nd edition, London, Oliver and 
 Boyd, p. 23-24&amp;#160;&lt;a href="#fnref:FYa" rev="footnote" title="Jump back to footnote 2 in the text"&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:RDa"&gt;
&lt;p&gt;Durstenfeld R.[1964] &lt;em&gt;Communications of the ACM&lt;/em&gt;, vol. 7, issue 7(July)
&amp;#160;&lt;a href="#fnref:RDa" rev="footnote" title="Jump back to footnote 3 in the text"&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:DKa"&gt;
&lt;p&gt;Knuth D.[1969] (1981) &lt;em&gt;The Art of Computer Programming&lt;/em&gt;, 2nd edition,
  Addison-Wesley, pp. 139-140&amp;#160;&lt;a href="#fnref:DKa" rev="footnote" title="Jump back to footnote 4 in the text"&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;</summary><category term="python"></category><category term="algoritmos"></category><category term="knuth"></category><category term="fisher-yates"></category><category term="shuffle"></category><category term="barajar"></category><category term="aleatoriedad"></category><category term="random"></category><category term="sattolo"></category></entry><entry><title>Aplanar listas en Python</title><link href="http://joedicastro.com/aplanar-listas-en-python.html" rel="alternate"></link><updated>2011-06-02T00:35:00+02:00</updated><author><name>joe di castro</name></author><id>http://joedicastro.com/aplanar-listas-en-python.html</id><summary type="html">&lt;p&gt;Una lista multinivel o anidada es aquella que a su vez contiene otra(s) listas o
 tuplas (o generadores, en general cualquier iterable). Estas listas pueden a su
 vez contener otras, que a su vez puede contener otras, etc. Cada una de estas
listas dentro de otra constituye un nivel de anidamiento. Un ejemplo sencillo
puede ser el siguiente:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="n"&gt;lista_anidada&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;a&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;b&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;c&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;A&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;aA&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;bB&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;cC&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;B&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Esta es una lista con dos niveles de anidamiento, porque la segunda lista dentro
 de la primera incluye a su vez dentro una tupla.&lt;/p&gt;
&lt;p&gt;Dada una lista de estas características, en ocasiones necesitamos aplanarla, es
decir tener todos los elementos dentro de una única lista sin anidamiento, una
lista plana. Siguiendo con el ejemplo anterior, esta lista una vez aplanada
quedaría del siguiente modo:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="n"&gt;lista_aplanada&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;a&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;b&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;c&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;A&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;aA&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;bB&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;cC&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;B&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Bien, ¿y como se consigue esto? Eso es precisamente lo que voy a tratar aquí,
diversos métodos para aplanar una lista.&lt;/p&gt;
&lt;h2 id="listas_anidadas_a_un_solo_nivel"&gt;Listas anidadas a un solo nivel.&lt;/h2&gt;
&lt;p&gt;Un primer modo de hacer esto podría ser emplear una función similar a esta:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;flattener_sum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;lst&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;lst&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[])&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Que parece muy simple y eficaz:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="n"&gt;lista_anidada&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;a&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;b&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;c&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;A&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;aA&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;bB&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;cC&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;B&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="n"&gt;flattener_sum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;lista_anidada&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="gt"&gt;Traceback (most recent call last):&lt;/span&gt;
  File &lt;span class="nb"&gt;&amp;quot;&amp;lt;input&amp;gt;&amp;quot;&lt;/span&gt;, line &lt;span class="m"&gt;1&lt;/span&gt;, in &lt;span class="n"&gt;&amp;lt;module&amp;gt;&lt;/span&gt;
  File &lt;span class="nb"&gt;&amp;quot;&amp;lt;input&amp;gt;&amp;quot;&lt;/span&gt;, line &lt;span class="m"&gt;2&lt;/span&gt;, in &lt;span class="n"&gt;flattener_sum&lt;/span&gt;
&lt;span class="gr"&gt;TypeError&lt;/span&gt;: &lt;span class="n"&gt;can only concatenate list (not &amp;quot;int&amp;quot;) to list&lt;/span&gt;
&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="n"&gt;lista_anidada&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[[&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;a&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;b&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;c&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;A&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;aA&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;bB&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;cC&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;B&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]]&lt;/span&gt;
&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="n"&gt;flattener_sum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;lista_anidada&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="go"&gt;[&amp;#39;a&amp;#39;, &amp;#39;b&amp;#39;, &amp;#39;c&amp;#39;, &amp;#39;A&amp;#39;, (&amp;#39;aA&amp;#39;, &amp;#39;bB&amp;#39;, &amp;#39;cC&amp;#39;), &amp;#39;B&amp;#39;]&lt;/span&gt;
&lt;span class="go"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Pero que como vemos tiene un problema, solo funciona con lista de un solo nivel
de anidamiento y además solo funciona únicamente con listas que solo contienen
otras listas.&lt;/p&gt;
&lt;p&gt;Otro método que solo funciona un nivel de anidamiento, y solo con listas de
iterables (soporta tuplas, generadores...) es el siguiente:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;flattener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;lst&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;item&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;sublist&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;lst&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;sublist&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;lista_anidada&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[[&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;a&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;b&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;c&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="p"&gt;))]&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;flattener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;lista_anidada&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;a&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;b&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;c&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;11&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Pero las listas anidadas compuestas únicamente de otros iterables son un caso
demasiado concreto y probablemente poco frecuente. Vamos a ver ahora cuatro
métodos que funcionan en todos los casos, en todo tipo de listas anidadas y a
cualquier nivel de anidamiento.&lt;/p&gt;
&lt;h2 id="aplanar_cualquier_tipo_de_lista_anidada"&gt;Aplanar cualquier tipo de lista anidada.&lt;/h2&gt;
&lt;p&gt;El primer ejemplo que pongo aquí se deriva del primero en que empleábamos la
función &lt;code&gt;sum()&lt;/code&gt; y que realizó &lt;a href="http://Ch3m4.org"&gt;Chema Cortés&lt;/a&gt; en &lt;a href="http://python.majibu.org/preguntas/547/serendipia-aplanando-una-lista"&gt;esta pregunta&lt;/a&gt; de
&lt;a href="http://python.majibu.org"&gt;majibu&lt;/a&gt;.&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;flat_sum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;lst&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;flat_sum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;elem&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt;
                &lt;span class="nb"&gt;hasattr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;elem&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;__iter__&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="nb"&gt;isinstance&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;elem&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;basestring&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;elem&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;elem&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;lst&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;[])&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Y que como podemos ver, ya puede aplanar sin problemas el primer ejemplo que
poníamos:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="n"&gt;lista_anidada&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;a&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;b&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;c&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;A&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;aA&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;bB&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;cC&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;B&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="n"&gt;flat_sum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;lista_anidada&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="go"&gt;[0, &amp;#39;a&amp;#39;, &amp;#39;b&amp;#39;, &amp;#39;c&amp;#39;, 1, &amp;#39;A&amp;#39;, &amp;#39;aA&amp;#39;, &amp;#39;bB&amp;#39;, &amp;#39;cC&amp;#39;, &amp;#39;B&amp;#39;, 2]&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Este método es relativamente fácil de recordar, pero tiene un rendimiento muy
pobre, como ya veremos más adelante.&lt;/p&gt;
&lt;p&gt;El siguiente método lo encontré en &lt;a href="http://stackoverflow.com/questions/2158395#2158532"&gt;esta pregunta de StackOverflow&lt;/a&gt;, y
obtiene la mejor puntuación de todas las soluciones propuestas. Seguramente se
debe a lo elegante de la solución y a que los generadores tienen fama de
eficaces y de consumir poco espacio en RAM. Aunque como luego veremos, es quizá
la peor de las soluciones que aquí comento.&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;flat_yield&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;lst&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;flatten&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;lst&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;elm&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;lst&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nb"&gt;hasattr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;elm&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;__iter__&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="nb"&gt;isinstance&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;elm&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;basestring&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
                &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;sub&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;flatten&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;elm&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
                    &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="n"&gt;sub&lt;/span&gt;
            &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="n"&gt;elm&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;flatten&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;lst&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Aunque en la función original esta línea:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nb"&gt;hasattr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;elm&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;__iter__&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="nb"&gt;isinstance&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;elm&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;basestring&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;es sustuida por esta otra:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;isinstance&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;elem&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;collections&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Iterable&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="nb"&gt;isinstance&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;elem&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;basestring&lt;/span&gt;&lt;span class="p"&gt;)):&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;el resultado es el mismo. Aunque la solución que yo propongo es ligeramente más
rápida.&lt;/p&gt;
&lt;p&gt;Esta otra solución la hallé &lt;a href="http://kogs-www.informatik.uni-hamburg.de/~meine/python_tricks"&gt;aquí&lt;/a&gt; y es un método que ya ofrece un
rendimiento aceptable a la par de ser muy elegante y &lt;em&gt;pythonica&lt;/em&gt;. Esta emplea 
los métodos propios de la listas &lt;code&gt;append()&lt;/code&gt; y &lt;code&gt;expand()&lt;/code&gt; para conseguir su 
objetivo. Es una función recursiva al igual que la anterior.&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;flat_list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;lst&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;elem&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;lst&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nb"&gt;hasattr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;elem&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;__iter__&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="nb"&gt;isinstance&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;elem&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;basestring&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
            &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;extend&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;flat_list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;elem&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;elem&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;h3 id="el_problema_de_la_recursividad"&gt;El problema de la recursividad.&lt;/h3&gt;
&lt;p&gt;El problema de los dos métodos anteriores es que emplean recursividad, y no
digo que la recursividad sea un problema, el problema son los limites por
defecto en Python. Por defecto Python solo admite 1000 niveles de recursividad
(e.g. una lista con 1000 niveles de anidamiento), y a nada que nuestra función
supere estos niveles, nos encontraremos con un bonito error como este:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="ne"&gt;RuntimeError&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;maximum&lt;/span&gt; &lt;span class="n"&gt;recursion&lt;/span&gt; &lt;span class="n"&gt;depth&lt;/span&gt; &lt;span class="n"&gt;exceeded&lt;/span&gt; &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="n"&gt;calling&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="n"&gt;Python&lt;/span&gt; &lt;span class="nb"&gt;object&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Aunque esto es algo que puede sortearse y podemos ampliar el limite de recursión
de esta forma:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="go"&gt;&amp;gt;&amp;gt;&amp;gt;import sys&lt;/span&gt;
&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;setrecursionlimit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;200000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="c"&gt;# ahora comprobamos el nuevo limite de recursividad&lt;/span&gt;
&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;getrecursionlimit&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="go"&gt;200000&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Ahora ya podríamos emplear el método hasta que alcanzáramos los 20.000 niveles
de recursión. Aunque no me parece la manera idónea de hacer las cosas, desde
luego. Si bien es cierto que será bastante difícil que nos encontremos listas
anidadas hasta estos niveles en el mundo real.&lt;/p&gt;
&lt;h2 id="la_mejor_soluci+n"&gt;La mejor solución.&lt;/h2&gt;
&lt;p&gt;Sin embargo tenemos una última solución que funciona en todos los casos y que no
 es recursiva y que además tiene el mejor rendimiento, con diferencia, de todos
los aquí comentados. Este se basa en la sustitución en línea de los elementos de
 la lista empleando la operación &lt;em&gt;slice&lt;/em&gt; Esta solución fue aportada por Chema
Cortés en &lt;a href="http://python.majibu.org/preguntas/547/serendipia-aplanando-una-lista"&gt;la misma pregunta&lt;/a&gt; de majibu.&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;flat_slice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;lst&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;lst&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;lst&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;enumerate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;lst&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;hasattr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;lst&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;__iter__&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="nb"&gt;isinstance&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;lst&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="nb"&gt;basestring&lt;/span&gt;&lt;span class="p"&gt;)):&lt;/span&gt;
            &lt;span class="n"&gt;lst&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;lst&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;lst&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;h2 id="pruebas_de_rendimiento"&gt;Pruebas de rendimiento&lt;/h2&gt;
&lt;p&gt;Para poder comprobar las diferencias de rendimiento entre unos métodos y otros,
he preparado unos tests que ejecutan cada una de las funciones analizadas 10
veces por cada caso planteado. Las funciones que he analizado son las cuatro que
 soportan cualquier tipo de lista. Al final se generan unas gráficas y unos
archivos en formato csv con los resultados de las pruebas. Para ello se generan
un par de valores (caso) en función del número de elementos de la lista y los
niveles de anidamiento de la misma. A partir de ahí planteo tres supuestos:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Número de elementos en aumento, número de niveles constante.&lt;/li&gt;
&lt;li&gt;Número de elementos constante, número de niveles en aumento.&lt;/li&gt;
&lt;li&gt;Número de elementos en aumento, número de niveles en aumento.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Para poder realizar estas pruebas, tenía que generar una lista que cumpliera las
 condiciones planteadas para cada caso. Para ello he creado una función que
genera una lista anidada con el número de elementos y niveles que nosotros le
indiquemos. Los elementos que la componen son números y cadenas (o solo números)
organizados aleatoriamente aunque con una estructura que es constante a
cualquier nivel de anidamiento o número de elementos. La razón de esto es que
aunque las listas se generen de forma aleatoria, si la estructura de las mismas
fuera dispar, estaríamos introduciendo un tercer factor, cuando lo que queremos
medir es el rendimiento en función de dos: nº elementos y nº niveles. De esta
manera podemos medir de una manera bastante fiable el rendimiento de los
distintos métodos en función de estos dos factores. Aunque aún existe otro
factor que podemos controlar solo hasta cierto punto, que son los procesos
corriendo simultáneamente y en segundo plano en nuestra maquina. Aunque los
reduzcamos al mínimo, los pocos que queden pueden generar pequeños "artefactos"
que desvirtúan un poco nuestra medida, pero que para el objetivo que perseguimos
 podemos considerar como despreciables. La estructura de estas listas generadas
es el de una lista de listas anidadas en la que se agrupan todos los elementos
posibles hasta el último nivel de anidamiento solicitado. Los elementos que
sobran se añaden al nivel base de la lista. Es algo así como si en el mundo real
 tuviéramos un campo lleno de zigurats (pirámides escalonadas de piedra) con las
 piedras sobrantes desperdigadas por el mismo. De ahí el nombre que le he puesto
 a la función.&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;ziggurat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;stones&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;steps&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;with_iters&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;only_numbers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;Make a list of nested lists, like a field of ziggurats.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
    &lt;span class="c"&gt;# First, generate the list of the stones (numbers and &amp;quot;strings&amp;quot;)&lt;/span&gt;
    &lt;span class="n"&gt;as_str&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;only_numbers&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="n"&gt;random&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sample&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;stones&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;stones&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;stones_list&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;stn&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;stn&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;as_str&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="n"&gt;stn&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;stn&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;stones&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
    &lt;span class="c"&gt;# Find the number of step pyramids (aka ziggurats)&lt;/span&gt;
    &lt;span class="n"&gt;num_zggts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;stones&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;steps&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;steps&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="n"&gt;ziggurats&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;zggt&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;num_zggts&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;zggt_step&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
        &lt;span class="c"&gt;# Build a step pyramid, step by step, until the chosen level&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;step&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;steps&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
            &lt;span class="c"&gt;# Get a choice of stones from the list to make a step &amp;amp; remove them&lt;/span&gt;
            &lt;span class="n"&gt;choice&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;random&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sample&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;stones_list&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;step&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;choosen&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;choice&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="n"&gt;stones_list&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;remove&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;choosen&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="c"&gt;# Build a step&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;step&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="n"&gt;zggt_step&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;choice&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
            &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="n"&gt;choice&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;insert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;iter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;zggt_step&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;with_iters&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="n"&gt;zggt_step&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="n"&gt;zggt_step&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;choice&lt;/span&gt;
        &lt;span class="n"&gt;ziggurats&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;zggt_step&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="c"&gt;# IF don&amp;#39;t have stones enough to make even a ziggurat, then will make&lt;/span&gt;
    &lt;span class="c"&gt;# multiple one-stone-many-airsteps ziggurats&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;num_zggts&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;step&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;steps&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
            &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;stn&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;stones&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
                &lt;span class="n"&gt;stones_list&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;stn&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;stones_list&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;stn&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;stn&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="c"&gt;# Finally, mix the remaining stones and the ziggurats, et Voila!!!&lt;/span&gt;
    &lt;span class="n"&gt;stones_list&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;ziggurats&lt;/span&gt;
    &lt;span class="n"&gt;random&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;shuffle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;stones_list&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;stones_list&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Ejemplos de listas generadas por esta función:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="n"&gt;final&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ziggurat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;15&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="go"&gt;[[&amp;#39;7&amp;#39;, [&amp;#39;14&amp;#39;, [12, [&amp;#39;13&amp;#39;], &amp;#39;10&amp;#39;], 5], &amp;#39;11&amp;#39;], 1, [&amp;#39;2&amp;#39;, [&amp;#39;0&amp;#39;, [4, [8], 3], 9], 6]]&lt;/span&gt;
&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="n"&gt;final&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ziggurat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="go"&gt;[5, [[[[[&amp;#39;2&amp;#39;]]]]], &amp;#39;1&amp;#39;, [[[[[4]]]]], [[[[[&amp;#39;6&amp;#39;]]]]], 3, [[[[[&amp;#39;0&amp;#39;]]]]], 7]&lt;/span&gt;
&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="n"&gt;final&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ziggurat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="go"&gt;[[&amp;#39;2&amp;#39;, [&amp;#39;7&amp;#39;, [&amp;lt;listiterator object at 0x9fe47ac&amp;gt;], &amp;#39;5&amp;#39;], 1], 0, 6, 4]&lt;/span&gt;
&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="n"&gt;final&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ziggurat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;only_numbers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="go"&gt;[[7, [3], 1], [2, [5], 8], [4, [0], 6]]&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Una vez ejecutados estos test, podemos ver en estas gráficas los resultados de
los mismos:&lt;/p&gt;
&lt;p style="text-align: center;"&gt;&lt;img src="pictures/test_results.png" width="650" 
height="880" alt="test_results.png" title="all functions, no iterables"/&gt;&lt;/p&gt;

&lt;p&gt;Analizemos gráfica a gráfica:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Aquí podemos ver que tal y como adelantaba anteriormente, la función 
&lt;code&gt;flat_slice()&lt;/code&gt; es la más eficaz y la función &lt;code&gt;flat_sum()&lt;/code&gt; la peor de todas. 
Además como podemos ver el incremento es constante, pero de magnitudes muy 
diferentes. Bueno, la verdad es que una diferencia de unos 2,25 ms por ejecución
 cuando la lista consta de 1400 elementos es insignificante, el hecho es que 
puede ser importante si se busca el rendimiento y se aplanan listas de forma 
masiva. En este caso no se ven afectado por el limite de recursión, todos los 
metodos han seguido funcionando sin problemas incluso cuando he aumentado a 
10.000 elementos por lista.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;En esta progresión podemos ver que las diferencias de rendimiento entre el 
 peor método y el mejor son aún más acusadas. Curiosamente el método empleando
generadores (&lt;code&gt;flat_yield()&lt;/code&gt;) y el metodo que emplea los métodos de las listas
(&lt;code&gt;flat_list()&lt;/code&gt;) tienen un rendimiento identico. Aquí aparece por vez primer el
limite de recursividad por defecto de python. Evidentemente la primera en caer
es la función &lt;code&gt;flat_sum()&lt;/code&gt;, ya que emplea doble recursividad y deja de funcionar
 a los 500 niveles de anidamiento por lista. Despues se caen sin remedio las 
funciones &lt;code&gt;flat_yield()&lt;/code&gt; y &lt;code&gt;flat_list()&lt;/code&gt; al alcanzar los 1000 niveles. La 
función &lt;code&gt;flat_slice()&lt;/code&gt; al no ser recursiva no se ve afectada por este limite.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Finalmente en esta gŕafica despejamos todas las dudas. Al aumentar
simultaneamente y en la misma proporción el número de elementos y el número de
niveles, podemos ver como la función &lt;code&gt;flat_yield()&lt;/code&gt; aumenta de manera drástica 
el tiempo empleado en cada ejecución. Antes de caer debido al limite de la
recursividad, tenemos una diferencia de 119,38 ms por cada ejecución para
1980 elementos y 990 niveles con la función &lt;code&gt;flat_slice()&lt;/code&gt;. Una diferencia de 
más de un segundo para diez ejecuciones. Y aunque esta gran diferencia desvirtua
 un poco la comparación entre el resto de funciones, básicamente siguen un 
 comportamiento similar a la de la anterior gráfica, destacando otra vez el 
metodo flat_slice como el más eficaz de todos ellos.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Como conclusión podriamos decir que moviendonos en los valores que nos movemos,
en ms, y con listas anidadas "normales" (sin llegar a cifras tan elevadas en
elementos/niveles), que cualquier metodo a priori nos sirve. Aunque yo
descartaría directamente el método de la función &lt;code&gt;flat_sum()&lt;/code&gt; y apostaría por 
emplear siempre &lt;code&gt;flat_slice()&lt;/code&gt;, sobre todo si va a ser ejecutado numerosas veces.&lt;/p&gt;
&lt;p&gt;Estas funciones, alguna más, ejemplos de uso y los test de rendimento se 
encuentran en el fichero &lt;a href="https://bitbucket.org/joedicastro/python-recipes/src/tip/src/flatten_nested_lists.py"&gt;flatten_nested_lists.py&lt;/a&gt; de &lt;a href="https://code.joedicastro.com/python-recipes/"&gt;mi repositorio&lt;/a&gt;.&lt;/p&gt;
&lt;br /&gt;

&lt;hr /&gt;
&lt;h2 id="comentarios_realizados_anteriormente_en_drupal"&gt;Comentarios realizados anteriormente en Drupal&lt;/h2&gt;
&lt;div style="float:right; padding:2px; border: 1px solid #ccc; height:28px;"&gt;
&lt;img src="pictures/avtr_kiko.png" height=28 width=28
alt="avatar" title="avatar de kikocorreoso"/&gt;&lt;/div&gt;

&lt;h3 id="interesante_art+culo"&gt;Interesante artículo.&lt;/h3&gt;
&lt;p&gt;por kikocorreoso el Jue, 02/06/2011 - 10:53&lt;/p&gt;
&lt;p&gt;Interesante artículo.&lt;/p&gt;
&lt;p&gt;Por cierto, ¡Qué gráficas más chulas! ;-D&lt;/p&gt;
&lt;hr /&gt;
&lt;div style="float:right; padding:2px; border: 1px solid #ccc; height:28px;"&gt;
&lt;a href="pages/sobre-mi.html"&gt;&lt;img src="pictures/avtr_joedicastro.png" height=28 
width=28 alt="avatar" title="avatar de joedicastro"/&gt;&lt;/a&gt;&lt;/div&gt;

&lt;h3 id="si_la_librer+a_matplotlib_da"&gt;Si, la librería matplotlib da&lt;/h3&gt;
&lt;p&gt;por &lt;a href="http://joedicastro.com"&gt;joe di castro&lt;/a&gt; el Jue, 02/06/2011 - 11:03&lt;/p&gt;
&lt;p&gt;Si, la librería &lt;a href="http://matplotlib.sourceforge.net/"&gt;matplotlib&lt;/a&gt; da unos 
resultados muy buenos :)&lt;/p&gt;
&lt;p&gt;Gracias por la &lt;a href="http://python.majibu.org/preguntas/713/que-me-aconsejais-para-realizar-graficos-desde-tablas"&gt;recomendación&lt;/a&gt;.&lt;/p&gt;
&lt;hr /&gt;
&lt;div style="float:right; padding:2px; border: 1px solid #ccc; height:28px;"&gt;
&lt;a href="http://ch3m4.org/blog"&gt;&lt;img src="pictures/avtr_ch3m4.png" height=28 
width=28 alt="avatar" title="avatar de Chema Cortés"/&gt;&lt;/a&gt;&lt;/div&gt;

&lt;h3 id="+buen_trabajo"&gt;¡Buen trabajo!&lt;/h3&gt;
&lt;p&gt;por &lt;a href="http://ch3m4.org/blog"&gt;Chema Cortés&lt;/a&gt; el Vie, 03/06/2011 - 21:28 &lt;/p&gt;
&lt;p&gt;¡Buen trabajo!&lt;/p&gt;
&lt;p&gt;Se me hace raro que las gráficas salgan tan &lt;em&gt;lineales&lt;/em&gt;. Esperaba que los 
rendimientos se degradaran al aumentar niveles y elementos.&lt;/p&gt;
&lt;p&gt;Que la soluciones &lt;code&gt;flat_list&lt;/code&gt; y &lt;code&gt;flat_yield&lt;/code&gt; vayan tan parejas me da que es 
porque esta última está utilizando al final el constructor &lt;code&gt;list()&lt;/code&gt; para generar 
la lista; pero que vaya tan mal cuando se incrementa el número de elementos y 
de niveles, tiene que ser debido a la sobrecarga de &lt;em&gt;clausuras&lt;/em&gt; que hace falta 
mantener.&lt;/p&gt;
&lt;p&gt;Creo que habría una opción más eficiente para reducir las clausuras. ¿Podrías 
hacer la prueba con esta versión de &lt;code&gt;flat_yield&lt;/code&gt;? Gasta más memoria, pero tiene 
que acercarse más a &lt;code&gt;flat_list&lt;/code&gt;. Sólo he cambiado que la recursividad se haga 
sobre el nivel superior en lugar de hacerlo dentro de la clausura. Ésto, en la 
práctica, va creando listas intermedias que sustituyen las clausuras, lo que 
debería simplificar el número de "contextos" a mantener:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;flat_yield&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;lst&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;flatten&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;lst&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;elm&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;lst&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nb"&gt;hasattr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;elm&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;__iter__&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="nb"&gt;isinstance&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;elm&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;basestring&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
                &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;sub&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;flat_yield&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;elm&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
                    &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="n"&gt;sub&lt;/span&gt;
            &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="n"&gt;elm&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;flatten&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;lst&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;hr /&gt;
&lt;div style="float:right; padding:2px; border: 1px solid #ccc; height:28px;"&gt;
&lt;a href="pages/sobre-mi.html"&gt;&lt;img src="pictures/avtr_joedicastro.png" height=28 
width=28 alt="avatar" title="avatar de joedicastro"/&gt;&lt;/a&gt;&lt;/div&gt;

&lt;h3 id="gracias_si_la_verdad_es_que_yo"&gt;Gracias. Si, la verdad es que yo&lt;/h3&gt;
&lt;p&gt;por &lt;a href="http://joedicastro.com"&gt;joe di castro&lt;/a&gt; el Vie, 03/06/2011 - 23:16&lt;/p&gt;
&lt;p&gt;Gracias. Si, la verdad es que yo tampoco esperaba una respuesta tan lineal en 
los resultados, pero engañan algo, como ya comentaba, la espectacular 
degradación en el rendimiento de la función &lt;code&gt;flat_yield&lt;/code&gt;, enmascara el 
comportamiento del resto de funciones en el último supuesto. He realizado una 
comparativa con todas las funciones sin emplear &lt;code&gt;flat_yield&lt;/code&gt;, y como ves la cosa 
cambia ligeramente y ya se parece algo más a lo esperado, aunque siguen siendo 
bastante &lt;em&gt;lineales&lt;/em&gt;.&lt;/p&gt;
&lt;p style="text-align: center;"&gt;&lt;img src="pictures/test_results_wy.png" 
width="650" height="980" alt="test_results_wy.png" 
title="no flat_yield, no iterables"/&gt;&lt;/p&gt;

&lt;p&gt;Si, el utilizar el constructor list, cambia bastante las cosas, de hecho el 
método de la función &lt;code&gt;flat_yield&lt;/code&gt; es el más rápido de todos si dejamos que 
devuelva solo el generador, es el crear la lista lo que lo ralentiza. Si 
fuéramos a emplear el resultado de la función como un iterable (en un bucle 
&lt;code&gt;for&lt;/code&gt; por ejemplo), desde luego creo que sería una de las mejores soluciones 
(en el primer supuesto, en el resto, la recursividad sigue pesando demasiado), 
porque nos ahorraríamos el proceso de creación de la lista. &lt;/p&gt;
&lt;p&gt;He realizado la prueba que me comentas con la función que me propones, la he 
llamado &lt;code&gt;flat_yield_cc&lt;/code&gt;, cc por Chema Cortés :)&lt;/p&gt;
&lt;p style="text-align: center;"&gt;&lt;img src="pictures/test_results_cc.png" 
width="650" height="980" alt="test_results_cc.png" 
title="all functions plus flat_yield_cc, no iterables"/&gt;&lt;/p&gt;

&lt;p&gt;Creo que los resultados te chocarán un poco, no solo tiene peor rendimiento, 
si no que se aumenta el nivel de recursividad y se cae a los 340 niveles de 
anidamiento. Aunque en el tercer supuesto el rendimiento es idéntico a la 
función anterior, en los demás supuestos tiene el peor comportamiento de las 
cinco. Creo que se debe a que de esta manera, generamos aún más recursividad, 
una función recursiva que llama a otra que también es recursiva (después de 
la primera llamada a &lt;code&gt;flat_yield&lt;/code&gt; desde &lt;code&gt;flatten&lt;/code&gt;), lo que hace que se 
multipliquen los niveles de anidamiento como se ve en la gráfica.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;He hecho algunas rectificaciones, las horas intempestivas a las que realice 
los comentarios, parece que me pesaban demasiado :-)&lt;/em&gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;div style="float:right; padding:2px; border: 1px solid #ccc; height:28px;"&gt;
&lt;a href="pages/sobre-mi.html"&gt;&lt;img src="pictures/avtr_joedicastro.png" height=28 
width=28 alt="avatar" title="avatar de joedicastro"/&gt;&lt;/a&gt;&lt;/div&gt;

&lt;h3 id="f+jate_en_lo_que_ocurre"&gt;Fíjate en lo que ocurre&lt;/h3&gt;
&lt;p&gt;por &lt;a href="http://joedicastro.com"&gt;joe di castro&lt;/a&gt; el Sáb, 04/06/2011 - 00:56&lt;/p&gt;
&lt;p&gt;Fíjate en lo que ocurre cuando empleo iterables dentro de las listas anidadas 
generadas:&lt;/p&gt;
&lt;p style="text-align: center;"&gt;&lt;img src="pictures/test_results_ga.png" 
width="650" height="880" alt="test_results_ga.png" 
title="all functions, with iterables"/&gt;&lt;/p&gt;

&lt;p&gt;Se pueden observar dos cosas:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Lo primero que se puede ver es que el aumentar el número de elementos no 
supone una penalización tan grande en el tiempo empleado, gracias al empleo de 
iterables. De hecho, vemos que en el tercer supuesto, los tiempos son diez veces 
menores, lo que confirma una vez más la eficacia de los iterables al iterarlos 
(valga la redundancia).&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Lo segundo es que la función &lt;code&gt;flat_slice&lt;/code&gt; (aunque apenas se distingue) tiene 
idéntico rendimiento que &lt;code&gt;flat_yield&lt;/code&gt;, aunque sigue siendo la más eficaz en los 
otros supuestos. Yo diría que esto confirma lo que comentábamos, de que es 
precisamente la creación de la lista en &lt;code&gt;flat_yield&lt;/code&gt; lo que penaliza su 
rendimiento, ya que la función &lt;code&gt;flat_slice&lt;/code&gt; también la crea en el primer paso, 
y ahora al incluir iterables en la lista anidada, su rendimiento se ve 
perjudicado, aunque no en la misma medida que &lt;code&gt;flat_yield&lt;/code&gt; al no ser recursiva 
(razón por la cual en el incremento en los niveles de anidamiento este hecho no 
la perjudica tanto, es decir la recursividad tiene una penalización varias 
magnitudes mayor que la creación de la lista).&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;</summary><category term="python"></category><category term="listas"></category><category term="recursividad"></category><category term="rendimiento"></category></entry><entry><title>Monitorizar los cambios de tamaño en un directorio</title><link href="http://joedicastro.com/monitorizar-los-cambios-de-tamano-en-un-directorio.html" rel="alternate"></link><updated>2011-05-17T21:52:00+02:00</updated><author><name>joe di castro</name></author><id>http://joedicastro.com/monitorizar-los-cambios-de-tamano-en-un-directorio.html</id><summary type="html">&lt;p&gt;Cuando administramos varias maquinas &lt;abbr title="Linux, Unix, Solaris, BSD, etc"&gt;UN*X&lt;/abbr&gt; nos puede interesar el tener 
controlados los cambios de tamaño en algunos directorios determinados, para 
poder observar pautas de comportamiento o ver cambios inesperados, para 
solucionar los problemas cuando o antes de que se produzcan. Y aunque hay varias
 formas de realizar esto, incluso con demonios que monitorizan los cambios en 
tiempo real (incrond, inotify, dnotify, gamin, watch, ...), puede que una 
solución más sencilla nos sea suficiente para directorios no críticos. Para 
instalaciones no complejas nos puede servir, por ejemplo, para no tener que 
lidiar con las &lt;a href="http://en.wikipedia.org/wiki/Disk_quota"&gt;quotas de disco&lt;/a&gt; (no siempre es una buena opción). O para 
evitar que por ejemplo una mala configuración en la rotación de los logs de una 
maquina acaben agotando el espacio disponible para la partición de sistema (caso
 real que me he encontrado más de una vez). &lt;/p&gt;
&lt;p&gt;Para poder monitorizar los cambios de tamaño de un directorio (y subdirectorios)
 he creado un sencillo script &lt;strong&gt;Python&lt;/strong&gt; que registra los cambios en la ruta que
  le proporcionemos y luego envía un informe por correo al buzón del usuario 
local. Los datos de los directorios los registra en un fichero oculto (su 
nombre empieza por un &lt;code&gt;.&lt;/code&gt;) binario de tipo &lt;a href="http://docs.python.org/library/pickle.html#module-pickle"&gt;pickle&lt;/a&gt; y el informe se guarda 
a su vez en un archivo de texto con el mismo nombre que el script, pero 
terminado en &lt;code&gt;.log&lt;/code&gt;. &lt;/p&gt;
&lt;p&gt;El informe que genera nos muestra por un lado tantos los nuevos directorios, 
como los directorios que se han eliminado con la cifra del espacio en disco que 
ocupan (o liberan). Por otro lado también nos informa de los directorios que han 
cambiado de tamaño, mostrándonos en que porcentaje se han 
incrementado/decrementado y la cantidad de espacio que ha variado. Aquí podemos 
ver un ejemplo de uno de estos informes.&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;SCRIPT =========================================================================
dir_size_monitor (ver. 0.2)
http://code.joedicastro.com/python-recipes

Changes in size of directories for .. on yourmachine
================================================================================

START TIME =====================================================================
                                                      Tuesday 05/17/11, 21:10:48
================================================================================

NEW DIRECTORIES ________________________________________________________________

   799.72 KiB   ./src/test/bibendum
     1.14 MiB   ./src/test/condimentum
     2.31 MiB   ./src/test/laoreet
   204.28 KiB   ./src/test/risus
     2.90 MiB   ./src/test/torquent

DELETED DIRECTORIES ____________________________________________________________

   383.79 KiB   ./src/test/adipiscing
     5.38 MiB   ./src/test/consequat
   847.72 KiB   ./src/test/etiam
   938.93 KiB   ./src/test/maecenas
     3.55 MiB   ./src/test/tincidunt
     2.33 MiB   ./src/test/viverra

CHANGED DIRECTORIES ____________________________________________________________

    34.79 %     55.5 MiB   ./src
    34.82 %     55.5 MiB   ./src/test
   -99.97 %     15.3 MiB   ./src/test/odio
   -99.97 %     15.6 MiB   ./src/test/tellus

THRESHOLD VALUES _______________________________________________________________

The directories whose size differences are less than any of these values are ignored:

Percentage:     10 %
Size:        10.00 MiB

.. STATISTICS __________________________________________________________________

      78 directories
  215.04 MiB

END TIME =======================================================================
                                                      Tuesday 05/17/11, 21:10:48
================================================================================
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Opcionalmente podemos establecer dos valores de umbral para que se ignoren todos 
los cambios que estén por debajo de estas dos cifras. Para desactivarlos 
simplemente hay que dejarlos a cero. Estas cifras se refieren por un lado al 
porcentaje de diferencia mínimo que deseamos establecer para que se nos informe 
y por otro a la cantidad mínima (expresada en bytes) de espacio en disco que se 
ha incrementado/decrementado en ese directorio. Pueden funcionar los dos a la 
vez, por lo que se han de cumplir las dos condiciones, o solamente uno dejando 
el otro a cero.&lt;/p&gt;
&lt;p&gt;Si programamos este script para que se ejecute cada cierto tiempo, podemos tener 
una idea aproximada de los cambios producidos en el. Y digo aproximada porque 
este nos muestra únicamente los cambios registrados entre dos &lt;em&gt;instantáneas&lt;/em&gt; 
tomadas, una en la ejecución anterior y otra en la ejecución actual. Y por lo 
tanto no esperemos obtener la relación de todos los cambios producidas entre 
ellas. Para conocer algo a ese nivel de detalle es mejor emplear uno de los 
servicios en tiempo real que mencionaba al principio. Dicho esto, es evidente 
que en la primera ejecución no tiene sentido informar de nada y de hecho hasta 
la segunda ejecución no empezara a generar informes.&lt;/p&gt;
&lt;p&gt;Este es el contenido de &lt;a href="https://bitbucket.org/joedicastro/python-recipes/src/tip/src/dir_size_monitor.py"&gt;dir_size_monitor.py&lt;/a&gt; es el siguiente:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="c"&gt;#!/usr/bin/env python&lt;/span&gt;
&lt;span class="c"&gt;# -*- coding: utf8 -*-&lt;/span&gt;

&lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="sd"&gt;    dir_size_monitor.py: Monitors changes in the size of dirs for a given path&lt;/span&gt;
&lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;

&lt;span class="c"&gt;#===============================================================================&lt;/span&gt;
&lt;span class="c"&gt;# This Script monitors the changes in disk size for the directories included in&lt;/span&gt;
&lt;span class="c"&gt;# a given path. It reports what directories are new or deleted. Also reports the&lt;/span&gt;
&lt;span class="c"&gt;# directories in which their size increases or decreases above threshold values.&lt;/span&gt;
&lt;span class="c"&gt;# These threshold values refer to the amount in difference of size of the &lt;/span&gt;
&lt;span class="c"&gt;# directory or/and the percentage difference. These values can be overrided by &lt;/span&gt;
&lt;span class="c"&gt;# setting them to zero.&lt;/span&gt;
&lt;span class="c"&gt;#&lt;/span&gt;
&lt;span class="c"&gt;# The final report is sended via email to the local user. This script is &lt;/span&gt;
&lt;span class="c"&gt;# intended to run periodically (e.g. via cron) &lt;/span&gt;
&lt;span class="c"&gt;#===============================================================================&lt;/span&gt;

&lt;span class="c"&gt;#===============================================================================&lt;/span&gt;
&lt;span class="c"&gt;#    Copyright 2011 joe di castro &amp;lt;joe@joedicastro.com&amp;gt;&lt;/span&gt;
&lt;span class="c"&gt;#&lt;/span&gt;
&lt;span class="c"&gt;#    This program is free software: you can redistribute it and/or modify&lt;/span&gt;
&lt;span class="c"&gt;#    it under the terms of the GNU General Public License as published by&lt;/span&gt;
&lt;span class="c"&gt;#    the Free Software Foundation, either version 3 of the License, or&lt;/span&gt;
&lt;span class="c"&gt;#    (at your option) any later version.&lt;/span&gt;
&lt;span class="c"&gt;#&lt;/span&gt;
&lt;span class="c"&gt;#    This program is distributed in the hope that it will be useful,&lt;/span&gt;
&lt;span class="c"&gt;#    but WITHOUT ANY WARRANTY; without even the implied warranty of&lt;/span&gt;
&lt;span class="c"&gt;#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the&lt;/span&gt;
&lt;span class="c"&gt;#    GNU General Public License for more details.&lt;/span&gt;
&lt;span class="c"&gt;#&lt;/span&gt;
&lt;span class="c"&gt;#    You should have received a copy of the GNU General Public License&lt;/span&gt;
&lt;span class="c"&gt;#    along with this program.  If not, see &amp;lt;http://www.gnu.org/licenses/&amp;gt;.&lt;/span&gt;
&lt;span class="c"&gt;#===============================================================================&lt;/span&gt;

&lt;span class="n"&gt;__author__&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;joe di castro &amp;lt;joe@joedicastro.com&amp;gt;&amp;quot;&lt;/span&gt;
&lt;span class="n"&gt;__license__&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;GNU General Public License version 3&amp;quot;&lt;/span&gt;
&lt;span class="n"&gt;__date__&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;17/05/2011&amp;quot;&lt;/span&gt;
&lt;span class="n"&gt;__version__&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;0.2&amp;quot;&lt;/span&gt;

&lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;sys&lt;/span&gt;
    &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;os&lt;/span&gt;
    &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;platform&lt;/span&gt;
    &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;pickle&lt;/span&gt;
    &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;logger&lt;/span&gt;
    &lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;get_size&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;best_unit_size&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;get_size_fast&lt;/span&gt;
&lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="ne"&gt;ImportError&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="c"&gt;# Checks the installation of the necessary python modules &lt;/span&gt;
    &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;linesep&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;join&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;An error found importing one module:&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;exc_info&lt;/span&gt;&lt;span class="p"&gt;()[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]),&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;You need to install it&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;Stopping...&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;]))&lt;/span&gt;
    &lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;exit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;list4log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dirs_size_dict&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;wpath&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;dirs&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;Create a list of new or deleted directories for the log.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
    &lt;span class="n"&gt;llst&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;ldir&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;sorted&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dirs&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;dsz&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;best_unit_size&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dirs_size_dict&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;ldir&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
        &lt;span class="n"&gt;llst&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot; {0:8.2f} {1}   ./{2}&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;
                    &lt;span class="n"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dsz&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;s&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;dsz&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;u&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;relpath&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ldir&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;wpath&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;llst&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;diff4log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;before&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;current&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;wpath&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;dirs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;threshold_pct&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;threshold_sz&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;Create a list of the directories that had size changes for the log.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
    &lt;span class="n"&gt;llst&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;ddir&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;sorted&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dirs&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;pct&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(((&lt;/span&gt;&lt;span class="n"&gt;current&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;ddir&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nb"&gt;float&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;before&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;ddir&lt;/span&gt;&lt;span class="p"&gt;]))&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="n"&gt;before&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;ddir&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mf"&gt;100.0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;diff&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;current&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;ddir&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;before&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;ddir&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nb"&gt;abs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pct&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="n"&gt;threshold_pct&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="nb"&gt;abs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;diff&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;threshold_sz&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;dsz&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;best_unit_size&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;diff&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;llst&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot; {0:8.2f} % {1:8.1f} {2}   ./{3}&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;
                        &lt;span class="n"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pct&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;dsz&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;s&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;dsz&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;u&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;relpath&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ddir&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                                                                        &lt;span class="n"&gt;wpath&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;llst&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;first_exec&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;Main section&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
    &lt;span class="c"&gt;# The path to monitor changes in directories dir_size&lt;/span&gt;
    &lt;span class="n"&gt;mon_pth&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;/your/path/to/monitor&amp;quot;&lt;/span&gt;

    &lt;span class="c"&gt;# Ignore all directories that are below these percentage or absolute value &lt;/span&gt;
    &lt;span class="c"&gt;# of size difference. There are optional, set to zero to override them.&lt;/span&gt;
    &lt;span class="n"&gt;thld_pct&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;      &lt;span class="c"&gt;# In percentage of difference in size for a directory&lt;/span&gt;
    &lt;span class="n"&gt;thld_sz&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;10.486E6&lt;/span&gt; &lt;span class="c"&gt;# In bytes of absolute value of directory size difference&lt;/span&gt;

    &lt;span class="c"&gt;# Prepare the log&lt;/span&gt;
    &lt;span class="n"&gt;log&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Logger&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;http://code.joedicastro.com/python-recipes&amp;quot;&lt;/span&gt;
    &lt;span class="n"&gt;head&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;Changes in size of directories for {0} on {1}&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;
            &lt;span class="n"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mon_pth&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;platform&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="p"&gt;()))&lt;/span&gt;
    &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;header&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;head&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;START TIME&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c"&gt;# Load the last dictionary of directories/sizes if exists&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nb"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;.dir_sizes.pkl&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;rb&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;input_file&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;bfr_dir&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pickle&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;load&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;input_file&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ne"&gt;EOFError&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ne"&gt;IOError&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;pickle&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PickleError&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;bfr_dir&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
        &lt;span class="n"&gt;first_exec&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;

    &lt;span class="c"&gt;# Get the current dictionary of directories/sizes&lt;/span&gt;
    &lt;span class="n"&gt;crr_dir&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;dirs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;files&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;walk&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mon_pth&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;directory&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;dirs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;dir_path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;directory&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;dir_size&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;get_size_fast&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dir_path&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;crr_dir&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;dir_path&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;dir_size&lt;/span&gt;

    &lt;span class="c"&gt;# First, Save the current dirs/sizes&lt;/span&gt;
    &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nb"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;.dir_sizes.pkl&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;wb&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;output_file&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;pickle&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dump&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;crr_dir&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;output_file&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c"&gt;# Create the list depending the status of directories&lt;/span&gt;
    &lt;span class="n"&gt;deleted&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;d&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;d&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;bfr_dir&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;d&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;crr_dir&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="n"&gt;added&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;d&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;d&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;crr_dir&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;d&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;bfr_dir&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="n"&gt;changed&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;d&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;d&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;crr_dir&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;d&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;bfr_dir&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;crr_dir&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="n"&gt;bfr_dir&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="p"&gt;]]&lt;/span&gt;

    &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;Deleted directories&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;list4log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bfr_dir&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;mon_pth&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;deleted&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;New directories&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;list4log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;crr_dir&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;mon_pth&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;added&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;Changed directories&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;diff4log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bfr_dir&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;crr_dir&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;mon_pth&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;changed&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                                             &lt;span class="n"&gt;thld_pct&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;thld_sz&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

    &lt;span class="c"&gt;# If thresholds are nonzero, then report the values &lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;thld_pct&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="n"&gt;thld_sz&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;tsz&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;best_unit_size&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;thld_sz&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;Threshold Values&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                 &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;The directories whose size differences are less than any of &amp;quot;&lt;/span&gt;
                  &lt;span class="s"&gt;&amp;quot;these values are ignored:&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                  &lt;span class="s"&gt;&amp;quot;Percentage: {0:6} %&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;thld_pct&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
                  &lt;span class="s"&gt;&amp;quot;Size:       {0:6.2f} {1}&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tsz&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;s&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;tsz&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;u&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;])])&lt;/span&gt;

    &lt;span class="c"&gt;# Show some statistics for the analyzed path&lt;/span&gt;
    &lt;span class="n"&gt;mon_pth_sz&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;best_unit_size&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;get_size_fast&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mon_pth&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;{0} Statistics&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mon_pth&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
             &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;{0:8} directories&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;crr_dir&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
              &lt;span class="s"&gt;&amp;quot;{0:8.2f} {1}&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mon_pth_sz&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;s&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;mon_pth_sz&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;u&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;])])&lt;/span&gt;
    &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;END TIME&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;first_exec&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;Changes in size of directories&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;write&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;__name__&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;__main__&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Este script necesita los módulos &lt;a href="http://joedicastro.com/conocer_el_tamano_de_un_directorio_con_python"&gt;get_size&lt;/a&gt; y &lt;a href="http://joedicastro.com/logger_informes_legibles_para_tus_scripts_python"&gt;logger&lt;/a&gt; para poder 
funcionar. Solo es necesario descargar los archivos y guardarlos en el mismo 
directorio donde se aloje este script. La versión más actualizada de este script 
se puede encontrar en &lt;a href="http://code.joedicastro.com/python-recipes"&gt;mi repositorio&lt;/a&gt;&lt;/p&gt;</summary><category term="python"></category><category term="script"></category><category term="tamaño"></category><category term="directorio"></category><category term="monitor"></category><category term="linux"></category></entry><entry><title>Conocer el tamaño de un directorio con Python</title><link href="http://joedicastro.com/conocer-el-tamano-de-un-directorio-con-python.html" rel="alternate"></link><updated>2011-05-16T21:05:00+02:00</updated><author><name>joe di castro</name></author><id>http://joedicastro.com/conocer-el-tamano-de-un-directorio-con-python.html</id><summary type="html">&lt;p&gt;Aunque conocer el tamaño de un directorio en sistemas como Linux es algo 
trivial, solo es necesario emplear el comando &lt;code&gt;du&lt;/code&gt;, si queremos hacer lo mismo 
con &lt;strong&gt;Python&lt;/strong&gt; -sin hacer uso de este comando- la cosa ya no es tan sencilla. 
Sobre todo si lo que queremos es una solución que nos devuelva tanto el tamaño 
de un fichero como el de un directorio. Cuando me encontré con esta necesidad lo 
primero que hice fue buscar en Internet para conocer alguna solución previa 
(reinventar la rueda no siempre es lo mejor) y me encontré con esto:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_dir_size&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;the_path&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;Get size of a directory tree in bytes.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
    &lt;span class="n"&gt;path_size&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;dirs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;files&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;walk&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;the_path&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;fil&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;files&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;filename&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;fil&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;path_size&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;getsize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;path_size&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Esta solución nos daría el tamaño en bytes de un directorio. Pero esta solución,
 que encontré en varios sitios, presentaba dos problemas:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;No da un tamaño exacto&lt;/strong&gt;. Esto se debe a que no tiene en cuenta las carpetas 
y ficheros ocultos (los que empiezan con un &lt;code&gt;.&lt;/code&gt; en Linux) y los ficheros 
especiales &lt;code&gt;..&lt;/code&gt; (que apuntan al directorio superior). Además tampoco tiene en 
cuenta los enlaces simbólicos. Por está razón la salida de esta función no 
coincide con el espacio que nos reporta el comando &lt;abbr title="Linux, Unix, Solaris, BSD, etc"&gt;UN*X&lt;/abbr&gt; &lt;code&gt;du -bs&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;No funciona para un solo fichero&lt;/strong&gt;. Solo trabaja cuando lo ejecutamos sobre 
un directorio, al hacerlo sobre un solo fichero nos dará como resultado siempre 0.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Teniendo en cuenta este punto de partida, elaboré una función que solucionara 
estos dos problemas y que devolviera el tamaño exacto de un directorio o 
fichero. Esta es la &lt;strong&gt;función que nos da el resultado correcto&lt;/strong&gt;:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_size&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;the_path&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;Get size of a directory tree or a file in bytes.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
    &lt;span class="n"&gt;path_size&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;directories&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;files&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;walk&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;the_path&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;filename&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;files&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;path_size&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;lstat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;st_size&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;directory&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;directories&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;path_size&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;lstat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;directory&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;st_size&lt;/span&gt;
    &lt;span class="n"&gt;path_size&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;getsize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;the_path&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;path_size&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;El resultado de esta función es el mismo que el que nos devuelve el comando 
Linux &lt;code&gt;du -bs&lt;/code&gt;. Además tiene en cuenta los enlaces simbólicos y no los sigue. 
Luego buscando una &lt;strong&gt;solución ligeramente más rápida&lt;/strong&gt; (aunque menos elegante y 
&lt;em&gt;pythonica&lt;/em&gt;) y que siguiera dando resultados precisos, cree una variante basada 
en el empleo de generadores. &lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_size_fast&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;the_path&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;Get size of a directory tree or a file in bytes.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_sizes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;the_path&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;Make a generator of individual file &amp;amp; directory sizes.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;islink&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;the_path&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;isdir&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;the_path&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
                &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;file_or_dir&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;listdir&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;the_path&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
                    &lt;span class="n"&gt;path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;the_path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;file_or_dir&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;isfile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
                        &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;lstat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;st_size&lt;/span&gt;
                    &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;size&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;get_sizes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
                            &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="n"&gt;size&lt;/span&gt;
            &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;lstat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;the_path&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;st_size&lt;/span&gt;
        &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;lstat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;the_path&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;st_size&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;get_sizes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;the_path&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;h2 id="obtener_el_tama+o_del_directorio_en_la_mejor_unidad_posible"&gt;Obtener el tamaño del directorio en la mejor unidad posible&lt;/h2&gt;
&lt;p&gt;Estas funciones proporcionan el resultado que deseamos, pero lo entregan en una 
unidad difícilmente legible, en bytes. ¿Que ocurre si queremos verlo en 
&lt;a href="http://es.wikipedia.org/wiki/Prefijo_binario"&gt;Mebibytes, GibiBytes&lt;/a&gt;, ... y que además sea siempre la más adecuada para una 
mejor visualización? Para responder a esta pregunta desarrolle una función que 
nos hace precisamente esto, tomar un tamaño en bytes y devolvernos el valor 
correcto en la &lt;a href="http://physics.nist.gov/cuu/Units/binary.html"&gt;unidad binaria IEC&lt;/a&gt; más adecuada:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;best_unit_size&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bytes_size&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;Get a size in bytes &amp;amp; convert it to the best IEC prefix for readability.&lt;/span&gt;

&lt;span class="sd"&gt;    Return a dictionary with three pair of keys/values:&lt;/span&gt;

&lt;span class="sd"&gt;    &amp;quot;s&amp;quot; -- (float) Size of path converted to the best unit for easy read&lt;/span&gt;
&lt;span class="sd"&gt;    &amp;quot;u&amp;quot; -- (str) The prefix (IEC) for s (from bytes(2^0) to YiB(2^80))&lt;/span&gt;
&lt;span class="sd"&gt;    &amp;quot;b&amp;quot; -- (int / long) The original size in bytes&lt;/span&gt;

&lt;span class="sd"&gt;    &amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;exp&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;90&lt;/span&gt; &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;bu_size&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;abs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bytes_size&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="nb"&gt;pow&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;2.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;exp&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bu_size&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;unit&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;bytes&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;KiB&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;MiB&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;GiB&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;40&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;TiB&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;PiB&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="mi"&gt;60&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;EiB&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;70&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;ZiB&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;80&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;YiB&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;}[&lt;/span&gt;&lt;span class="n"&gt;exp&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
            &lt;span class="k"&gt;break&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;s&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;bu_size&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;u&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;unit&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;b&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;bytes_size&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Esta función nos devuelve un diccionario con tres claves:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;'s'&lt;/code&gt;: Es el tamaño convertido a la mejor unidad IEC posible en términos de 
legibilidad.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;'u'&lt;/code&gt;: Es el prefijo IEC para el tamaño anterior.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;'b'&lt;/code&gt;: Es el tamaño original en bytes.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Para entenderla, lo mejor es mostrar algunos ejemplos:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;get_size&lt;/span&gt;
&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="n"&gt;size&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;get_size&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;best_unit_size&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;38467206502&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;{0:.2f} {1}&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;s&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;u&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="go"&gt;&amp;#39;35.83 GiB&amp;#39;&lt;/span&gt;
&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="n"&gt;size&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;get_size&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;best_unit_size&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;45332&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;{0:.2f} {1}&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;s&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;u&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="go"&gt;&amp;#39;44.27 KiB&amp;#39;&lt;/span&gt;
&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="n"&gt;size&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;get_size&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;best_unit_size&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;9878323&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;{0:.2f} {1} es igual a {2} bytes&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;s&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;u&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;b&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="go"&gt;&amp;#39;9.42 MiB es igual a 9878323 bytes&amp;#39;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Y evidentemente, combinar las dos funciones en una, nos evita tener que pasar 
las dos a un mismo directorio/fichero. &lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_unit_size&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;the_path&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;Calculate size of a directory/file &amp;amp; convert it for the best IEC prefix.&lt;/span&gt;

&lt;span class="sd"&gt;    Return a dictionary with three pair of keys/values:&lt;/span&gt;

&lt;span class="sd"&gt;    &amp;quot;s&amp;quot; -- (float) Size of path converted to the best unit for easy read&lt;/span&gt;
&lt;span class="sd"&gt;    &amp;quot;u&amp;quot; -- (str) The prefix (IEC) for s (from bytes(2^0) to YiB(2^80))&lt;/span&gt;
&lt;span class="sd"&gt;    &amp;quot;b&amp;quot; -- (int / long) The original size in bytes&lt;/span&gt;

&lt;span class="sd"&gt;    &amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;

    &lt;span class="n"&gt;bytes_size&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;directories&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;files&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;walk&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;the_path&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;filename&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;files&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;bytes_size&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;lstat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;st_size&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;directory&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;directories&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;bytes_size&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;lstat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;directory&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;st_size&lt;/span&gt;
    &lt;span class="n"&gt;bytes_size&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;getsize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;the_path&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;exp&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;90&lt;/span&gt; &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;bu_size&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;abs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bytes_size&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="nb"&gt;pow&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;2.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;exp&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bu_size&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;unit&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;bytes&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;KiB&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;MiB&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;GiB&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;40&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;TiB&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;PiB&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="mi"&gt;60&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;EiB&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;70&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;ZiB&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;80&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;YiB&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;}[&lt;/span&gt;&lt;span class="n"&gt;exp&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
            &lt;span class="k"&gt;break&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;s&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;bu_size&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;u&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;unit&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;b&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;bytes_size&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Que nos devuelve un diccionario similar al anterior, lo que nos proporciona la 
posibilidad de disponer tanto del tamaño en bytes como en la mejor unidad IEC 
posible con una única función. &lt;/p&gt;
&lt;p&gt;Todas estas funciones con ejemplos (y además una clase que hace uso de ellas), 
se pueden encontrar en el fichero &lt;a href="https://bitbucket.org/joedicastro/python-recipes/src/tip/src/get_size.py"&gt;get_size.py&lt;/a&gt; de &lt;a href="http://code.joedicastro.com"&gt;mi repositorio&lt;/a&gt;. Si 
se ejecuta el fichero como un script puede verse una comparativa de las diversas 
funciones en rendimiento y precisión con respecto al comando &lt;code&gt;du -bs&lt;/code&gt;&lt;/p&gt;</summary><category term="python"></category><category term="script"></category><category term="linux"></category><category term="iec"></category><category term="tamaño"></category><category term="directorio"></category><category term="fichero"></category></entry><entry><title>Enviar un correo electrónico con Python</title><link href="http://joedicastro.com/enviar-un-correo-electronico-con-python.html" rel="alternate"></link><updated>2011-05-08T13:29:00+02:00</updated><author><name>joe di castro</name></author><id>http://joedicastro.com/enviar-un-correo-electronico-con-python.html</id><summary type="html">&lt;p&gt;Existen varias maneras de enviar un correo electrónico a través de &lt;strong&gt;Python&lt;/strong&gt;, 
por ejemplo empleando el modulo &lt;code&gt;smtplib&lt;/code&gt; de la librería estándar de Python. Si 
estamos en &lt;strong&gt;Linux&lt;/strong&gt; y contamos con un servidor de correo funcionando, enviar un
 correo puede ser algo tan sencillo como este script:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="c"&gt;#!/usr/bin/env python&lt;/span&gt;
&lt;span class="c"&gt;# -*- coding: utf8 -*-&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;os&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;socket&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;smtplib&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;send_mail_local&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;subject&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;Send a mail to the user&amp;#39;s local mailbox.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
    &lt;span class="c"&gt;# Set the local mail address for the script&amp;#39; user&lt;/span&gt;
    &lt;span class="n"&gt;email&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;@&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;join&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;LOGNAME&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;socket&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;gethostname&lt;/span&gt;&lt;span class="p"&gt;()])&lt;/span&gt;
    &lt;span class="n"&gt;msg&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;From: {0}&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;To: {0}&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;Subject: {1}&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;{2}&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;subject&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="n"&gt;server&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;smtplib&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SMTP&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;localhost&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;server&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sendmail&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;server&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;quit&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;Main section&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;

    &lt;span class="n"&gt;send_mail_local&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;Comprobando el envío de correo localmente&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="s"&gt;&amp;quot;Si puedes leer esto, tu servidor local SMTP está OK&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;Comprueba el correo en tu buzón local {0}&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;Este normalmente se &amp;quot;&lt;/span&gt;
          &lt;span class="s"&gt;&amp;quot;encuentra situado en /var/mail/{1}&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;
          &lt;span class="n"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;@&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;join&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;LOGNAME&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;socket&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;gethostname&lt;/span&gt;&lt;span class="p"&gt;()]),&lt;/span&gt;
                 &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;LOGNAME&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;__name__&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;__main__&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Que si lo ejecutamos, nos generará una salida por consola como esta:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;Comprueba el correo en tu buzón local tuusuario@tumaquina
Este normalmente se encuentra situado en /var/mail/tuusuario
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Y tendrás en la bandeja de correo de tu usuario en la maquina algo como esto:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;De:     tuusuario@tumaquina
Para:   tuusuario@tumaquina
Asunto:     Comprobando el envío de correo localmente
Fecha:  Sun, 08 May 2011 12:57:30 +0200

Si puedes leer esto, tu servidor local SMTP está OK
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Es algo realmente sencillo, y la mayoría de las lineas sirven para construir el 
ejemplo, lo que realmente hace el trabajo, y solo necesita el modulo &lt;code&gt;smtplib&lt;/code&gt; 
para funcionar es esto:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="n"&gt;server&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;smtplib&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SMTP&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;localhost&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;server&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sendmail&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;server&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;quit&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Pero si lo que queremos es enviar correos más complejos, con adjuntos, empleando 
servidores SMTP externos y enviar con copia (CC) ó copia oculta (CCO) a varios 
usuarios, entonces ya necesitamos emplear las opciones del modulo &lt;code&gt;email&lt;/code&gt; que 
nos permite hacer prácticamente cualquier tarea relacionada con los correos 
electronicos. Para estas situaciones desarrolle en su día una función que ha 
cubierto todas las situaciones que se me han dado hasta ahora. Además devuelve 
mensajes de error allí donde algo puede ir mal.&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="c"&gt;# The more complete solution. This adds the Cc: (Carbon Copy) and Bcc: (Blind &lt;/span&gt;
&lt;span class="c"&gt;# Carbon Copy) fields and the ability to add attachments. &lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;send_email&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;subject&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;send_from&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;dest_to&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;attachments&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
               &lt;span class="n"&gt;send_cc&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;send_bcc&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;server&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;localhost&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;port&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;25&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
               &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;passwd&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;Send a email with(out) attachment(s) enabling CC and BCC fields.&lt;/span&gt;

&lt;span class="sd"&gt;    Arguments:&lt;/span&gt;
&lt;span class="sd"&gt;        (str) subject -- the mail&amp;#39;s subject&lt;/span&gt;
&lt;span class="sd"&gt;        (str) text -- the message&amp;#39;s text&lt;/span&gt;
&lt;span class="sd"&gt;        (str) send_from -- a sender&amp;#39;s email address (default &amp;quot;&amp;quot;)&lt;/span&gt;
&lt;span class="sd"&gt;        (list) dest_to -- a list of receivers&amp;#39; email addresses (&amp;quot;&amp;quot;)&lt;/span&gt;
&lt;span class="sd"&gt;        (list) attachments -- a list of attachments files (default None)&lt;/span&gt;
&lt;span class="sd"&gt;        (list) send_cc -- a list of carbon copy&amp;#39;s email addresses (def. None)&lt;/span&gt;
&lt;span class="sd"&gt;        (list) send_bcc -- a list of blind carbon copy&amp;#39;s email addresses (None)&lt;/span&gt;
&lt;span class="sd"&gt;        (str) server -- the smtp server (default &amp;quot;localhost&amp;quot;)&lt;/span&gt;
&lt;span class="sd"&gt;        (int) port -- the smtp server port (default 25)&lt;/span&gt;
&lt;span class="sd"&gt;        (str) user -- the smtp server user (default &amp;quot;&amp;quot;)&lt;/span&gt;
&lt;span class="sd"&gt;        (str) passwd --the smtp server password (default &amp;quot;&amp;quot;)&lt;/span&gt;

&lt;span class="sd"&gt;    If &amp;quot;send_from&amp;quot; or &amp;quot;dest_to&amp;quot; are empty or None, then script user&amp;#39;s mailbox &lt;/span&gt;
&lt;span class="sd"&gt;    is assumed instead. Useful for logging scripts&lt;/span&gt;

&lt;span class="sd"&gt;    &amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
    &lt;span class="n"&gt;local_email&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;@&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;join&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;LOGNAME&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;socket&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;gethostname&lt;/span&gt;&lt;span class="p"&gt;()])&lt;/span&gt;
    &lt;span class="n"&gt;send_from&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;send_from&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;send_from&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="n"&gt;local_email&lt;/span&gt;
    &lt;span class="n"&gt;dest_to&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;dest_to&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;dest_to&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;local_email&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

    &lt;span class="n"&gt;dest_to_addrs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;dest_to&lt;/span&gt; &lt;span class="c"&gt;# receivers mails including to, cc and bcc fields&lt;/span&gt;
    &lt;span class="n"&gt;message&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;MIMEMultipart&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;Subject&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;subject&lt;/span&gt;
    &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;From&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;send_from&lt;/span&gt;
    &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;To&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;COMMASPACE&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dest_to&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;send_cc&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;Cc&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;COMMASPACE&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;send_cc&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;dest_to_addrs&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;send_cc&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;send_bcc&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;dest_to_addrs&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;send_bcc&lt;/span&gt;
    &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;Date&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;formatdate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;localtime&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;preamble&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;You&amp;#39;ll not see this in a MIME-aware mail reader.&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;&lt;/span&gt;
    &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;attach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;MIMEText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

    &lt;span class="c"&gt;# For all type of attachments&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;attachments&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;att_file&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;attachments&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nb"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;att_file&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;rb&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;attmnt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="n"&gt;att&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;MIMEBase&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;application&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;octet-stream&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="n"&gt;att&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;set_payload&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;attmnt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;read&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
            &lt;span class="n"&gt;encoders&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;encode_base64&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;att&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;att&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;add_header&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;content-disposition&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;attachment&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                           &lt;span class="n"&gt;filename&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;basename&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;att_file&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
            &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;attach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;att&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c"&gt;# initialize the mail server&lt;/span&gt;
    &lt;span class="n"&gt;smtp_server&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;smtplib&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SMTP&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="c"&gt;# Connect to mail server&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;smtp_server&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;server&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;port&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="n"&gt;socket&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;gaierror&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;mail error&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;Wrong server, are you sure is correct?&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="n"&gt;socket&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;mail error&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;Server unavailable or connection refused&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="c"&gt;# Login in mail server&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;server&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;localhost&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;smtp_server&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;login&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;passwd&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="n"&gt;smtplib&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SMTPAuthenticationError&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;mail error&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;Authentication error&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="n"&gt;smtplib&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SMTPException&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;mail error&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;No suitable authentication method&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="c"&gt;# Send mail&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;smtp_server&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sendmail&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;send_from&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;dest_to_addrs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;as_string&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
    &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="n"&gt;smtplib&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SMTPRecipientsRefused&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;mail error&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;All recipients were refused.&amp;quot;&lt;/span&gt;
              &lt;span class="s"&gt;&amp;quot;Nobody got the mail.&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="n"&gt;smtplib&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SMTPSenderRefused&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;mail error&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;The server didn’t accept the from_addr&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="n"&gt;smtplib&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SMTPDataError&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;mail error&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;An unexpected error code, Data refused&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="c"&gt;# Disconnect from server&lt;/span&gt;
    &lt;span class="n"&gt;smtp_server&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;quit&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Para las situaciones en las que no disponemos de un servidor de correo SMTP 
funcionando, podemos montar uno temporalmente para realizar pruebas. La tarea 
puede tan sencilla como recurrir a uno de los &lt;strong&gt;python one-liners&lt;/strong&gt; que 
mencionaba en &lt;a href="http://joedicastro.com/python_one_liners_potencia_en_una_sola_linea"&gt;Python one-liners. Potencia en una sola línea&lt;/a&gt;.&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="n"&gt;python&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;m&lt;/span&gt; &lt;span class="n"&gt;smtpd&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="n"&gt;DebuggingServer&lt;/span&gt; &lt;span class="n"&gt;localhost&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;8025&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;que es  exactamente lo mismo que hace este script que empleo habitualmente para 
las mismas funciones (me permite montar un segundo servidor SMTP en un puerto 
distinto sin necesitar permisos de administrador, lo que me permite llamarlo 
desde otros scripts). Emplear una u otra forma, pues ya depende de uno.&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="c"&gt;#!/usr/bin/env python&lt;/span&gt;
&lt;span class="c"&gt;# -*- coding: utf8 -*-&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;smtpd&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;asyncore&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;smtp_server&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;port&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;Starts a smtp server for test purposes.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
    &lt;span class="n"&gt;smtpd&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DebuggingServer&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;localhost&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;port&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;smtp_server&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;8025&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;asyncore&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;loop&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="ne"&gt;KeyboardInterrupt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;pass&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;El código completo con ejemplos de estas funciones, se puede encontrar en mi 
&lt;a href="https://code.joedicastro.com/python-recipes"&gt;repositorio&lt;/a&gt;.&lt;/p&gt;</summary><category term="python"></category><category term="email"></category><category term="smtp"></category><category term="linux"></category><category term="one-liner"></category></entry><entry><title>Logger, informes legibles para tus scripts Python</title><link href="http://joedicastro.com/logger-informes-legibles-para-tus-scripts-python.html" rel="alternate"></link><updated>2011-05-07T23:02:00+02:00</updated><author><name>joe di castro</name></author><id>http://joedicastro.com/logger-informes-legibles-para-tus-scripts-python.html</id><summary type="html">&lt;p&gt;Normalmente los scripts que se crean para ser ejecutados periódicamente, como 
los de administración de sistemas, realizan tareas que generan cambios que 
queremos conocer. Esta información se suele guardar en logs o se envía por 
correo a una dirección de email. Hay muchas formas de generar estos logs, pero 
normalmente es una simple salida de texto plano en consola, que no suele tener 
una presentación muy "amigable" o legible. Cuando tienes una cantidad generosa 
de estos scripts, los recibes por correo electrónico y con una frecuencia diaria 
o mayor, lo que menos deseas es andar buscando la información entre lineas de 
texto plano. Lo que yo quiero es poder identificar la información rápidamente de 
un vistazo, además tenía una idea rondando por la cabeza, que seria aún más 
cómodo si todos emplearan un formato similar. Con estas premisas cree un módulo 
&lt;strong&gt;Python&lt;/strong&gt;, &lt;code&gt;logger.py&lt;/code&gt;, que empleo en muchos de mis scripts en Python y que me 
permite analizar adecuadamente la información que me interesa.&lt;/p&gt;
&lt;p&gt;Un ejemplo de informe generado por este modulo Python sería el siguiente (el 
mismo que se generaría si ejecutáramos el modulo como script):&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;SCRIPT =========================================================================
logger (ver. 0.3)
http://code.joedicastro.com/python-recipes

This is a test of class Logger
================================================================================

START TIME =====================================================================
                                                     Saturday 05/07/11, 21:51:27
================================================================================

BLOCK ==========================================================================
This
is
a
sample
of
Logger.block()
================================================================================

LIST ___________________________________________________________________________

This
is
a
sample
of
Logger.list()

This a sample of logger.free() text.

&amp;quot;Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque sed
tortor eget justo vehicula consequat vel eu quam. Suspendisse non lectus eget
orci varius adipiscing.&amp;quot;

END TIME =======================================================================
                                                     Saturday 05/07/11, 21:51:27
================================================================================
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Este modulo contiene una clase, &lt;code&gt;Logger()&lt;/code&gt;, que dispone de una serie de métodos 
que nos permiten diversas tareas, supongamos que tenemos un objeto &lt;code&gt;log&lt;/code&gt; de la 
clase &lt;code&gt;Logger()&lt;/code&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;log.header(url, msg)&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Nos crea una cabecera para el script que nos aporta cierta información que 
 nos sirve para  identificar el mismo y el contexto en el que se ejecuta. &lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;SCRIPT =========================================================================
nombre del script (versión)
http://miweb.com/script

mensaje que nos informa de la finalidad del mismo o de 
información dependiente del contexto. Totalmente 
personalizable.
================================================================================
&lt;/pre&gt;&lt;/div&gt;


&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;log.time(title)&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Nos permite registrar el tiempo de un evento. Normalmente lo empleo para 
   registrar el comienzo y el final de la ejecución del script. &lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;TITULO =========================================================================
                                                       Sábado 07/05/11, 22:18:27
================================================================================
&lt;/pre&gt;&lt;/div&gt;


&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;log.block(title, content)&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Crea un bloque de texto con titulo y enmarcado por líneas compuestas por el 
   carácter &lt;code&gt;=&lt;/code&gt;. Entre esas lineas y el contenido no hay ninguna linea en 
   blanco. Es útil para destacar cierto contenido del resto de forma muy 
   notable, suelo utilizarlo para ubicar el script en su contexto. &lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;TITULO =========================================================================
contenido
sigue el contenido
...
================================================================================
&lt;/pre&gt;&lt;/div&gt;


&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;log.list(title, content)&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Como su nombre bien indica, es una lista de líneas con un simple encabezado 
  que lo distinga del resto. Suelo emplearlo para volcar la información generada 
  por el script. Entre el encabezado y la primera línea de texto existe una 
  línea en blanco.&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;TITULO __________________________________________________

Primera línea del contenido
Segunda línea del contenido
...
&lt;/pre&gt;&lt;/div&gt;


&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;log.free(content)&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Texto libre, párrafos que se mostrarán tal y como son, sin formato ni 
  cabecera alguna. Raramente lo empleo, pero es útil por ejemplo para 
  introducir comentarios, licencias, etc.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;log.send(subject, send_from='', dest_to='', mail_server='localhost',
             server_user='', server_pass='')&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;El meollo del script. Se emplea para mandar el resultado por 
   correo electrónico. Si solo especificamos el asunto, empleará nuestro 
   servidor de correo local para mandar el informe al buzón local del usuario 
   que programó/ejecuto el script. Pero se pueden especificar tanto el empleo 
   de un servidor de correo (SMTP) distinto como otro (o varios) 
   destinatario(s) en particular. &lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;log.write(append=False)&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Escribe el resultado del log en un fichero de texto. El nombre de este 
   fichero estará compuesto por el nombre del script sin extensión, más la 
   extensión &lt;em&gt;.log&lt;/em&gt;. Si &lt;code&gt;append&lt;/code&gt; es &lt;code&gt;True&lt;/code&gt; entonces añadirá el resultado al 
   final de texto, si no, lo reescribirá en cada ejecución guardando 
   únicamente el último informe. &lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;log.get()&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Nos devuelve el contenido del log. Es útil cuando estamos con tareas de 
  depurado, con un &lt;code&gt;print&lt;/code&gt; llamando a este método podemos volcar en la 
  consola la información registrada en el log.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;El código del script es el siguiente: &lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="c"&gt;#!/usr/bin/env python&lt;/span&gt;
&lt;span class="c"&gt;# -*- coding: utf8 -*-&lt;/span&gt;

&lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="sd"&gt;    logger.py: Create a log object to log script messages in a elegant way&lt;/span&gt;
&lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;

&lt;span class="c"&gt;#===============================================================================&lt;/span&gt;
&lt;span class="c"&gt;# This module create a log object to log script messages in a elegant way&lt;/span&gt;
&lt;span class="c"&gt;#===============================================================================&lt;/span&gt;

&lt;span class="c"&gt;#===============================================================================&lt;/span&gt;
&lt;span class="c"&gt;#    Copyright 2010 joe di castro &amp;lt;joe@joedicastro.com&amp;gt;&lt;/span&gt;
&lt;span class="c"&gt;#&lt;/span&gt;
&lt;span class="c"&gt;#    This program is free software: you can redistribute it and/or modify&lt;/span&gt;
&lt;span class="c"&gt;#    it under the terms of the GNU General Public License as published by&lt;/span&gt;
&lt;span class="c"&gt;#    the Free Software Foundation, either version 3 of the License, or&lt;/span&gt;
&lt;span class="c"&gt;#    (at your option) any later version.&lt;/span&gt;
&lt;span class="c"&gt;#&lt;/span&gt;
&lt;span class="c"&gt;#    This program is distributed in the hope that it will be useful,&lt;/span&gt;
&lt;span class="c"&gt;#    but WITHOUT ANY WARRANTY; without even the implied warranty of&lt;/span&gt;
&lt;span class="c"&gt;#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the&lt;/span&gt;
&lt;span class="c"&gt;#    GNU General Public License for more details.&lt;/span&gt;
&lt;span class="c"&gt;#&lt;/span&gt;
&lt;span class="c"&gt;#    You should have received a copy of the GNU General Public License&lt;/span&gt;
&lt;span class="c"&gt;#    along with this program.  If not, see &amp;lt;http://www.gnu.org/licenses/&amp;gt;.&lt;/span&gt;
&lt;span class="c"&gt;#===============================================================================&lt;/span&gt;

&lt;span class="n"&gt;__author__&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;joe di castro &amp;lt;joe@joedicastro.com&amp;gt;&amp;quot;&lt;/span&gt;
&lt;span class="n"&gt;__license__&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;GNU General Public License version 3&amp;quot;&lt;/span&gt;
&lt;span class="n"&gt;__date__&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;10/09/2010&amp;quot;&lt;/span&gt;
&lt;span class="n"&gt;__version__&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;0.3&amp;quot;&lt;/span&gt;

&lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;sys&lt;/span&gt;
    &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;os&lt;/span&gt;
    &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;time&lt;/span&gt;
    &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;smtplib&lt;/span&gt;
    &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;socket&lt;/span&gt;
    &lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;email.mime.text&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;MIMEText&lt;/span&gt;
    &lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;email.mime.multipart&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;MIMEMultipart&lt;/span&gt;
    &lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;email.utils&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;COMMASPACE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;formatdate&lt;/span&gt;
&lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="ne"&gt;ImportError&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="c"&gt;# Checks the installation of the necessary python modules &lt;/span&gt;
    &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;linesep&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;join&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;An error found importing one module:&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;exc_info&lt;/span&gt;&lt;span class="p"&gt;()[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]),&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;You need to install it&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;Stopping...&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;]))&lt;/span&gt;
    &lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;exit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Logger&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;

&lt;span class="sd"&gt;    Create a log object to log script messages.&lt;/span&gt;

&lt;span class="sd"&gt;    These messages can be sended via email or writed in a log file&lt;/span&gt;

&lt;span class="sd"&gt;    &amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;Create the object Logger itself and set various attributes.&lt;/span&gt;

&lt;span class="sd"&gt;        These attributes are about the python file wich invokes this module:&lt;/span&gt;

&lt;span class="sd"&gt;        __script_vers = The version of python file which invokes this module&lt;/span&gt;
&lt;span class="sd"&gt;        __script_name = The name of the python file which invokes this module&lt;/span&gt;
&lt;span class="sd"&gt;        filename = the log file&amp;#39;s name&lt;/span&gt;

&lt;span class="sd"&gt;        &amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
        &lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;__main__&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;__dict__&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;__dict&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;__log&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;&amp;#39;&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;__version__&amp;#39;&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;__dict&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
            &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;__script_vers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;__dict&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;__version__&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;__script_vers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;Unknown&amp;#39;&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;__script_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;basename&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;__dict&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;__file__&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;.&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;filename&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;{0}.log&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;__script_name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__len__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;__log&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__format__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tit&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cont&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;decor&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;Format a block or a list of lines to enhance comprehension.&lt;/span&gt;

&lt;span class="sd"&gt;        (str) tit -- title for the block or list&lt;/span&gt;
&lt;span class="sd"&gt;        (str or iterable) cont -- line/s for the list/block content&lt;/span&gt;
&lt;span class="sd"&gt;        (&amp;#39;=&amp;#39; or &amp;#39;_&amp;#39;) decor - define if it&amp;#39;s list or block and decorate it&lt;/span&gt;

&lt;span class="sd"&gt;        make the looks of self.block() and self.list()&lt;/span&gt;

&lt;span class="sd"&gt;        &amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
        &lt;span class="n"&gt;ending&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;=&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;_&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;linesep&lt;/span&gt;&lt;span class="p"&gt;}[&lt;/span&gt;&lt;span class="n"&gt;decor&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="n"&gt;end&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;=&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;=&amp;#39;&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;80&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;_&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;}[&lt;/span&gt;&lt;span class="n"&gt;decor&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="n"&gt;begin&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;#39; &amp;#39;&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;join&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="n"&gt;tit&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;upper&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;80&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tit&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;decor&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;ending&lt;/span&gt;
        &lt;span class="n"&gt;cont&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;cont&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nb"&gt;isinstance&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cont&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="n"&gt;cont&lt;/span&gt;
        &lt;span class="n"&gt;sep&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;linesep&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;__log&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;sep&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;join&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="n"&gt;begin&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sep&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cont&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;end&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sep&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;block&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;A block of text lines headed and followed by a line full of &amp;#39;=&amp;#39;.&lt;/span&gt;

&lt;span class="sd"&gt;        (str) title -- The title that start the first line of &amp;#39;=&amp;#39;&lt;/span&gt;
&lt;span class="sd"&gt;        (str or iterable) content -- The line/s between the &amp;#39;=&amp;#39; lines&lt;/span&gt;

&lt;span class="sd"&gt;        There&amp;#39;s not any empty line between the &amp;#39;=&amp;#39; lines and content, e.g.:&lt;/span&gt;

&lt;span class="sd"&gt;        TITLE ==================================================&lt;/span&gt;
&lt;span class="sd"&gt;        content&lt;/span&gt;
&lt;span class="sd"&gt;        ========================================================&lt;/span&gt;

&lt;span class="sd"&gt;        &amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;__format__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;=&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;A list of text lines headed by a line full of &amp;#39;_&amp;#39;.&lt;/span&gt;

&lt;span class="sd"&gt;        (str) title -- The title that start the line of &amp;#39;_&amp;#39;&lt;/span&gt;
&lt;span class="sd"&gt;        (str or iterable) content -- The line/s after the &amp;#39;_&amp;#39; line&lt;/span&gt;

&lt;span class="sd"&gt;        After the &amp;#39;_&amp;#39; line is a empty line between it and the content, e.g.:&lt;/span&gt;

&lt;span class="sd"&gt;        TITLE __________________________________________________&lt;/span&gt;

&lt;span class="sd"&gt;        content&lt;/span&gt;

&lt;span class="sd"&gt;        &amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;__format__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;_&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;free&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;Free text unformatted.&lt;/span&gt;

&lt;span class="sd"&gt;        (str) content -- Text free formated&lt;/span&gt;

&lt;span class="sd"&gt;        &amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nb"&gt;isinstance&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
            &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;__log&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;content&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;linesep&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;time&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;A self.block() formated line with current time and date.&lt;/span&gt;

&lt;span class="sd"&gt;        (str) title -- Title for self.block()&lt;/span&gt;

&lt;span class="sd"&gt;        Looks like this, the data and time are right-justified:&lt;/span&gt;

&lt;span class="sd"&gt;        TITLE ==================================================&lt;/span&gt;
&lt;span class="sd"&gt;                                       Friday 09/10/10, 20:01:39&lt;/span&gt;
&lt;span class="sd"&gt;        ========================================================&lt;/span&gt;

&lt;span class="sd"&gt;        &amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;block&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;{0:&amp;gt;80}&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;strftime&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;%A &lt;/span&gt;&lt;span class="si"&gt;%x&lt;/span&gt;&lt;span class="s"&gt;, &lt;/span&gt;&lt;span class="si"&gt;%X&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;header&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;A self.block() formated header for the log info.&lt;/span&gt;

&lt;span class="sd"&gt;        (str) url -- The url of the script&lt;/span&gt;
&lt;span class="sd"&gt;        (str) msg -- Message to show into the header. To Provide any useful info&lt;/span&gt;

&lt;span class="sd"&gt;        It looks like this:&lt;/span&gt;

&lt;span class="sd"&gt;        SCRIPT =================================================&lt;/span&gt;
&lt;span class="sd"&gt;        script name and version&lt;/span&gt;
&lt;span class="sd"&gt;        url&lt;/span&gt;

&lt;span class="sd"&gt;        msg&lt;/span&gt;
&lt;span class="sd"&gt;        ========================================================&lt;/span&gt;

&lt;span class="sd"&gt;        &amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
        &lt;span class="n"&gt;script&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;{0} (ver. {1})&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;__script_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;__script_vers&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;block&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;Script&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;script&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;Get the log content.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;__log&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;subject&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;send_from&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;dest_to&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;mail_server&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;localhost&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
             &lt;span class="n"&gt;server_user&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;server_pass&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;Send a email with the log.&lt;/span&gt;

&lt;span class="sd"&gt;        Arguments:&lt;/span&gt;
&lt;span class="sd"&gt;            (str) send_from -- a sender&amp;#39;s email address (default &amp;#39;&amp;#39;)&lt;/span&gt;
&lt;span class="sd"&gt;            (str or list) dest_to -- a list of receivers&amp;#39; email addresses (&amp;#39;&amp;#39;)&lt;/span&gt;
&lt;span class="sd"&gt;            (str) subject -- the mail&amp;#39;s subject&lt;/span&gt;
&lt;span class="sd"&gt;            (str) mail_server -- the smtp server (default &amp;#39;localhost&amp;#39;)&lt;/span&gt;
&lt;span class="sd"&gt;            (str) server_user -- the smtp server user (default &amp;#39;&amp;#39;)&lt;/span&gt;
&lt;span class="sd"&gt;            (str) server_pass --the smtp server password (default &amp;#39;&amp;#39;)&lt;/span&gt;

&lt;span class="sd"&gt;        If &amp;#39;send_from&amp;#39; or &amp;#39;dest_to&amp;#39; are empty or None, then script user&amp;#39;s&lt;/span&gt;
&lt;span class="sd"&gt;        mailbox is assumed instead. Useful for loggin scripts&lt;/span&gt;

&lt;span class="sd"&gt;        &amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
        &lt;span class="n"&gt;local_email&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;@&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;join&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;LOGNAME&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;socket&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;gethostname&lt;/span&gt;&lt;span class="p"&gt;()])&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;send_from&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;send_from&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;local_email&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;dest_to&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;dest_to&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;local_email&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

        &lt;span class="n"&gt;dest_to_addrs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;COMMASPACE&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dest_to&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c"&gt;# receivers mails&lt;/span&gt;
        &lt;span class="n"&gt;message&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;MIMEMultipart&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;Subject&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;{0} - {1}&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;subject&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                                                &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;strftime&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;%A &lt;/span&gt;&lt;span class="si"&gt;%x&lt;/span&gt;&lt;span class="s"&gt;, &lt;/span&gt;&lt;span class="si"&gt;%X&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;From&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;send_from&lt;/span&gt;
        &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;To&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;dest_to_addrs&lt;/span&gt;
        &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;Date&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;formatdate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;localtime&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;preamble&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;You&amp;#39;ll not see this in a MIME-aware mail reader.&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;&lt;/span&gt;
        &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;attach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;MIMEText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;__log&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

        &lt;span class="c"&gt;# initialize the mail server&lt;/span&gt;
        &lt;span class="n"&gt;server&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;smtplib&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SMTP&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="c"&gt;# Connect to mail server&lt;/span&gt;
        &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;server&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mail_server&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="n"&gt;socket&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;gaierror&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;mail error&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;Wrong server, are you sure is correct?&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="n"&gt;socket&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;mail error&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;Server unavailable or connection refused&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="c"&gt;# Login in mail server&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;mail_server&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;localhost&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="n"&gt;server&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;login&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;server_user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;server_pass&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="n"&gt;smtplib&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SMTPAuthenticationError&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;mail error&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;Authentication error&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="n"&gt;smtplib&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SMTPException&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;mail error&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;No suitable authentication method&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="c"&gt;# Send mail&lt;/span&gt;
        &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;server&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sendmail&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;send_from&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;dest_to_addrs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;as_string&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
        &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="n"&gt;smtplib&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SMTPRecipientsRefused&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;mail error&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;All recipients were refused.&amp;#39;&lt;/span&gt;
                      &lt;span class="s"&gt;&amp;#39;Nobody got the mail.&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="n"&gt;smtplib&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SMTPSenderRefused&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;mail error&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;The server didn’t accept the from_addr&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="n"&gt;smtplib&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SMTPDataError&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;mail error&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;An unexpected error code, Data refused&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="c"&gt;# Disconnect from server&lt;/span&gt;
        &lt;span class="n"&gt;server&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;quit&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;append&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;Write the log to a file.&lt;/span&gt;

&lt;span class="sd"&gt;        The name of the file will be like this:&lt;/span&gt;

&lt;span class="sd"&gt;        script.log&lt;/span&gt;

&lt;span class="sd"&gt;        where &amp;#39;script&amp;#39; is the name of the script file without extension (.py)&lt;/span&gt;

&lt;span class="sd"&gt;        (boolean) append -- If true appends log to file, else writes a new one&lt;/span&gt;

&lt;span class="sd"&gt;        &amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
        &lt;span class="n"&gt;mode&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;ab&amp;#39;&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;append&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;wb&amp;#39;&lt;/span&gt;
        &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nb"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;mode&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;log_file&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;log_file&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;__log&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;Main section&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
    &lt;span class="n"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;http://code.joedicastro.com/python-recipes&amp;#39;&lt;/span&gt;
    &lt;span class="n"&gt;head&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;This is a test of class Logger&amp;#39;&lt;/span&gt;

    &lt;span class="n"&gt;log&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Logger&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;header&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;head&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;Start time&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;block&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;Block&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;This is a sample of Logger.block()&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;split&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
    &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;List&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;This is a sample of Logger.list()&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;split&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
    &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;free&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;&amp;#39;&amp;#39;This a sample of logger.free() text.&lt;/span&gt;

&lt;span class="s"&gt;&amp;quot;Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque sed&lt;/span&gt;
&lt;span class="s"&gt;tortor eget justo vehicula consequat vel eu quam. Suspendisse non lectus eget&lt;/span&gt;
&lt;span class="s"&gt;orci varius adipiscing.&amp;quot;&amp;#39;&amp;#39;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;End time&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;This is mail test&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;free&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;All of this had been recorded in {0}&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;__name__&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;__main__&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Para acceder a la versión más reciente del mismo, acudir a mi &lt;a href="http://code.joedicastro.com/python-recipes"&gt;repositorio&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Ejemplos de utilización de este modulo pueden ser 
&lt;a href="http://joedicastro.com/sincronizar_una_carpeta_local_y_una_remota_a_traves_de_ftp_lftp_mirror"&gt;lftp-mirror&lt;/a&gt; (incluido dentro del mismo), &lt;a href="http://joedicastro.com/ted_talks_descargar_videos_y_subtitulos_de_las_charlas"&gt;TEDTalks&lt;/a&gt;, &lt;a href="http://joedicastro.com/generar_informes_de_cambios_en_paquetes_instalados_en_debian_y_ubuntu"&gt;dpkg_diff&lt;/a&gt; y &lt;a href="http://joedicastro.com/combatir_el_spam_en_drupal"&gt;ban_drupal_spammers&lt;/a&gt;&lt;/p&gt;</summary><category term="usabilidad"></category><category term="script"></category><category term="python"></category><category term="informes"></category><category term="logging"></category><category term="modulo"></category></entry><entry><title>Generar informes de cambios en paquetes instalados en Debian y Ubuntu.</title><link href="http://joedicastro.com/generar-informes-de-cambios-en-paquetes-instalados-en-debian-y-ubuntu.html" rel="alternate"></link><updated>2011-05-06T22:55:00+02:00</updated><author><name>joe di castro</name></author><id>http://joedicastro.com/generar-informes-de-cambios-en-paquetes-instalados-en-debian-y-ubuntu.html</id><summary type="html">&lt;p&gt;Una de las políticas de seguridad que tengo con mis sistemas &lt;strong&gt;Linux&lt;/strong&gt;, es 
además de efectuar respaldos periódicos (diarios) del contenido del directorio 
&lt;code&gt;/home&lt;/code&gt; (en mi caso siempre es una partición o disco independiente), es siempre 
guardar también una lista de los &lt;abbr title="En sistemas *NIX se denomina así a los programas"&gt;paquetes&lt;/abbr&gt; instalados en los mismos. 
Prácticamente nunca hago una copia completa de la partición o disco de de 
sistema, y aunque si hago una copia de los directorios más importantes, prefiero 
tener siempre una relación actualizada de los &lt;abbr title="En sistemas *NIX se denomina así a los programas"&gt;paquetes&lt;/abbr&gt; instalados en cada 
maquina.&lt;/p&gt;
&lt;p&gt;En sistemas Linux &lt;a href="http://debian.org"&gt;Debian&lt;/a&gt; y otras distribuciones derivadas (&lt;a href="http://ubuntu.com"&gt;Ubuntu&lt;/a&gt;, 
&lt;a href="http://www.linuxmint.com/"&gt;Mint&lt;/a&gt;, &lt;a href="http://www.mepis.org/"&gt;Mepis&lt;/a&gt;, &lt;a href="http://www.knoppix.org/"&gt;Knoppix&lt;/a&gt;, ...) obtener esta lista es realmente 
sencillo, pues solo es necesario ejecutar este comando:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="go"&gt;dpkg -l &amp;gt; lista_paquetes.txt&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Y con eso, se generaría una lista de los &lt;abbr title="En sistemas *NIX se denomina así a los programas"&gt;paquetes&lt;/abbr&gt; instalados&lt;sup id="fnref:nota"&gt;&lt;a href="#fn:nota" rel="footnote"&gt;1&lt;/a&gt;&lt;/sup&gt; en el 
sistema en el fichero &lt;em&gt;lista_paquetes.txt&lt;/em&gt;. Y mantener actualizado este fichero 
es tan sencillo como programar (via &lt;code&gt;crontab&lt;/code&gt; por ejemplo) la ejecución de este 
comando con un sencillo &lt;a href="http://es.wikipedia.org/wiki/"&gt;script&lt;/a&gt; &lt;a href="http://es.wikipedia.org/wiki/Shell_de_UNIX"&gt;shell&lt;/a&gt;. Y así lo he realizado durante 
años, hasta que quise tener aun más información.&lt;/p&gt;
&lt;p&gt;Entonces me interesó conocer también los cambios que se producen en los 
&lt;abbr title="En sistemas *NIX se denomina así a los programas"&gt;paquetes&lt;/abbr&gt;, es decir nuevas (des)instalaciones y actualizaciones. Es lógico pensar 
que si uno mismo es el que las realiza, pues ya sabe esta información de primera 
mano. Pero la memoria es frágil y no demasiado confiable, además ¿que ocurre 
cuando en una misma maquina tienen permisos para administrar los &lt;abbr title="En sistemas *NIX se denomina así a los programas"&gt;paquetes&lt;/abbr&gt; más de 
un usuario? &lt;/p&gt;
&lt;p&gt;Se pueden conocer estos cambios de los &lt;abbr title="En sistemas *NIX se denomina así a los programas"&gt;paquetes&lt;/abbr&gt; en el tiempo de varios modos, 
desde acudir a los logs de &lt;strong&gt;dpkg&lt;/strong&gt; en &lt;code&gt;/var/log/dpkg.log&lt;/code&gt; y examinarlos con 
algún analizador de logs (o el más sencillo e inmediato comando &lt;code&gt;less&lt;/code&gt;) hasta 
consultarlos de una manera más sencilla y gráfica con el gestor de &lt;abbr title="En sistemas *NIX se denomina así a los programas"&gt;paquetes&lt;/abbr&gt; 
&lt;strong&gt;Synaptic&lt;/strong&gt; (en el menú &lt;em&gt;Archivo -&amp;gt; Histórico&lt;/em&gt;). Pero me interesaba automatizar 
esto, para conocer esos cambios poco después de que se produjeran y lo lógico 
era emplear el mismo script shell que empleaba para generar la lista de &lt;abbr title="En sistemas *NIX se denomina así a los programas"&gt;paquetes&lt;/abbr&gt; 
instalados. Así lo hice entonces, combinando los comandos &lt;code&gt;awk&lt;/code&gt;, &lt;code&gt;grep&lt;/code&gt;, &lt;code&gt;diff&lt;/code&gt; 
y &lt;code&gt;mail&lt;/code&gt; con el comando del principio para obtener la lista, tenia los cambios 
en mi buzón de correo al poco tiempo de producirse. Y ha funcionado 
perfectamente hasta ahora.&lt;/p&gt;
&lt;p&gt;Pero desde que me introduje en el lenguaje &lt;strong&gt;Python&lt;/strong&gt;, he ido migrando poco a 
poco los scripts &lt;a href="http://es.wikipedia.org/wiki/Bash"&gt;bash&lt;/a&gt; que tengo para reescribirlos en este lenguaje. Y 
recientemente le ha tocado a este. Ha sido muy fácil y la verdad es que el 
resultado aunque funcionalmente es el mismo, me ha permitido entregar unos 
informes más elegantes y fáciles de interpretar de un golpe de vista. &lt;/p&gt;
&lt;p&gt;Ahora, cuando se realiza algún cambio en los &lt;abbr title="En sistemas *NIX se denomina así a los programas"&gt;paquetes&lt;/abbr&gt; del sistema, de forma 
automática, por mi o por otro usuario autorizado, yo recibo en mi correo un 
informe similar a este:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;SCRIPT =========================================================================
dpkg_diff (ver. 0.1)
http://code.joedicastro.com/python-recipes

Changes of packages installed on yourmachine
 ===============================================================================

START TIME =====================================================================
                                                     Thursday 05/05/11, 10:30:01
 ===============================================================================

INSTALLED PACKAGES LIST FILE ___________________________________________________

/your/path/to/package_list.txt

CHANGES DIFF ___________________________________________________________________

&lt;span class="gd"&gt;--- previous Wed May  4 22:59:51 2011&lt;/span&gt;
&lt;span class="gi"&gt;+++ current  Thu May  5 10:30:01 2011&lt;/span&gt;
&lt;span class="gu"&gt;@@ -34,1 +34,1 @@&lt;/span&gt;
&lt;span class="gd"&gt;-ii  apt                 0.7.25.3ubuntu9.3&lt;/span&gt;
&lt;span class="gi"&gt;+ii  apt                 0.7.25.3ubuntu9.4&lt;/span&gt;
&lt;span class="gu"&gt;@@ -36,2 +36,2 @@&lt;/span&gt;
&lt;span class="gd"&gt;-ii  apt-transport-https 0.7.25.3ubuntu9.3&lt;/span&gt;
&lt;span class="gd"&gt;-ii  apt-utils           0.7.25.3ubuntu9.3&lt;/span&gt;
&lt;span class="gi"&gt;+ii  apt-transport-https 0.7.25.3ubuntu9.4&lt;/span&gt;
&lt;span class="gi"&gt;+ii  apt-utils           0.7.25.3ubuntu9.4&lt;/span&gt;

END TIME =======================================================================
                                                     Thursday 05/05/11, 10:30:01
 ===============================================================================
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Donde se puede ver la información que nos indica el script que ha generado el 
correo, la maquina en la que se han realizado los cambios, la localización del 
fichero con la relación de todos los &lt;abbr title="En sistemas *NIX se denomina así a los programas"&gt;paquetes&lt;/abbr&gt; instalados en el sistema, la fecha 
y hora de la ejecución del script y el &lt;a href="http://es.wikipedia.org/wiki/Diff"&gt;diff&lt;/a&gt; con la relación de cambios en 
los &lt;abbr title="En sistemas *NIX se denomina así a los programas"&gt;paquetes&lt;/abbr&gt; en formato &lt;a href="http://en.wikipedia.org/wiki/Diff#Unified_format"&gt;Unified format&lt;/a&gt;. En este ejemplo podemos ver como se 
han actualizado tres &lt;abbr title="En sistemas *NIX se denomina así a los programas"&gt;paquetes&lt;/abbr&gt; en esa maquina. No nos dice la hora en que se 
efectuó la modificación (podemos verlo en el log de &lt;code&gt;dpkg&lt;/code&gt;) pero si podemos 
saber que se efectuó entre dos intervalos de ejecución del script, información 
que será más que suficiente la mayoría de las veces. Para datos concretos, ir al 
log y filtrarlo con &lt;code&gt;grep&lt;/code&gt; y en segundos sabremos la respuesta. Si además como 
es mi caso, ejecutamos el script cada 12 o 24 horas, pues será fácil saber 
cuando se han realizado los cambios.&lt;/p&gt;
&lt;p&gt;Un fragmento de código de la parte principal del script es el siguiente:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;pretty_diff&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;diff&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;Better format for package lines in diff.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
    &lt;span class="n"&gt;pkg&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt; &lt;span class="c"&gt;# diff&amp;#39;s packages lines&lt;/span&gt;

    &lt;span class="c"&gt;# Get columns info for diff package lines&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;idx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;enumerate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;diff&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;findall&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;^-{3}|^\+{3}|^@{2}&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
            &lt;span class="c"&gt;# split the line in columns and remove the description column&lt;/span&gt;
            &lt;span class="n"&gt;cols&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;\s{2,}&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;)[:&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
            &lt;span class="c"&gt;# A nested dict, for each line index we have a dict that contains &lt;/span&gt;
            &lt;span class="c"&gt;# the package line columns: &amp;#39;s&amp;#39; (status), &amp;#39;n&amp;#39; (name) &amp;amp; &amp;#39;v&amp;#39; (version)&lt;/span&gt;
            &lt;span class="c"&gt;# and the width of the name column: w(width)&lt;/span&gt;
            &lt;span class="n"&gt;pkg&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;idx&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;s&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;cols&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;n&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;cols&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;v&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;cols&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;w&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cols&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;])}&lt;/span&gt;

    &lt;span class="c"&gt;# maximum width in packages&amp;#39; name column for all lines&lt;/span&gt;
    &lt;span class="n"&gt;mxw&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;max&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;pkg&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;w&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;index&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;pkg&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

    &lt;span class="c"&gt;# Replace each package line for a prettier one (more legible) &lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;diff&lt;/span&gt;&lt;span class="p"&gt;)):&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;pkg&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;diff&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;{0} {1} {2}&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pkg&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;s&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;pkg&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;n&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s"&gt;&amp;quot; &amp;quot;&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;
                                            &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mxw&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;pkg&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;w&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]),&lt;/span&gt; &lt;span class="n"&gt;pkg&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;v&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]))&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;diff&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;old&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;Main section&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;

    &lt;span class="c"&gt;# The path to store the debian packages list file&lt;/span&gt;
    &lt;span class="n"&gt;pkg_lst_file&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;./package_list.txt&amp;quot;&lt;/span&gt;

    &lt;span class="c"&gt;# Start logging&lt;/span&gt;
    &lt;span class="n"&gt;log&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Logger&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;http://code.joedicastro.com/python-recipes&amp;quot;&lt;/span&gt;
    &lt;span class="n"&gt;head&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;Changes of packages installed on {0}&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;platform&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
    &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;header&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;head&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;Start time&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c"&gt;# Read the old file and clean the list&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;exists&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pkg_lst_file&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;old&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pkg_lst_file&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;r&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;readlines&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="n"&gt;old_date&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ctime&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;stat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pkg_lst_file&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;st_mtime&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c"&gt;# Get the current list of debian packages installed on system &lt;/span&gt;
    &lt;span class="n"&gt;current&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Popen&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;dpkg&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;-l&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;stdout&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;PIPE&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;stdout&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;readlines&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="c"&gt;# First, save the list file&lt;/span&gt;
    &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nb"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pkg_lst_file&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;w&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;out&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;out&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;writelines&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;current&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;curr_date&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ctime&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;stat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pkg_lst_file&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;st_mtime&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c"&gt;# Compare both lists&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;old&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;file_path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;realpath&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pkg_lst_file&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;diff&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;ln&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;ln&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;unified_diff&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;old&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;current&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;fromfile&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;previous&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                                          &lt;span class="n"&gt;tofile&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;current &amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                                          &lt;span class="n"&gt;fromfiledate&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;old_date&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                                          &lt;span class="n"&gt;tofiledate&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;curr_date&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                                          &lt;span class="n"&gt;lineterm&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;

        &lt;span class="c"&gt;# If there are differences write the log to disk and send mail&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;diff&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;Installed packages list file&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;file_path&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;Changes diff&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;pretty_diff&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;diff&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
            &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;End time&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="c"&gt;# Send mail to current system user. For other options, see logger &lt;/span&gt;
            &lt;span class="c"&gt;# module info&lt;/span&gt;
            &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;Debian packages changes&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Este script hace uso del modulo logger, que comento en este &lt;a href="http://joedicastro.com/logger_informes_legibles_para_tus_scripts_python"&gt;artículo&lt;/a&gt;. &lt;/p&gt;
&lt;p&gt;Para obtener la versión más reciente del script y del modulo logger, consultar 
mi &lt;a href="http://code.joedicastro.com/python-recipes"&gt;repositorio&lt;/a&gt;.&lt;/p&gt;
&lt;div class="footnote"&gt;
&lt;hr /&gt;
&lt;ol&gt;
&lt;li id="fn:nota"&gt;
&lt;p&gt;Es una relación de los programas instalados empleando el sistema de 
paqueteria de Debian, los programas instalados manualmente vía compilación 
u otros medios no aparecerán en ella. &amp;#160;&lt;a href="#fnref:nota" rev="footnote" title="Jump back to footnote 1 in the text"&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;</summary><category term="python"></category><category term="script"></category><category term="paquetes"></category><category term="linux"></category><category term="debian"></category><category term="ubuntu"></category><category term="dpkg"></category></entry><entry><title>Python one-liners. Potencia en una sola línea</title><link href="http://joedicastro.com/python-one-liners-potencia-en-una-sola-linea.html" rel="alternate"></link><updated>2011-05-02T22:18:00+02:00</updated><author><name>joe di castro</name></author><id>http://joedicastro.com/python-one-liners-potencia-en-una-sola-linea.html</id><summary type="html">&lt;p&gt;Un &lt;a href="http://en.wikipedia.org/wiki/One-liner_program"&gt;one-liner&lt;/a&gt; propiamente dicho
 es un comando de la consola que ejecuta una determinada función en "una sola 
 línea de texto". En Python esto se consigue con una única orden de línea de 
 comandos que llama al interprete y ejecuta los argumentos que le pasemos en 
 la misma. Aunque también se les denomina así a veces a funciones lambda y 
 funciones que han sido reducidas a una sola linea de código.&lt;/p&gt;
&lt;p&gt;Un ejemplo de python one-liner es uno de los más famosos:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="n"&gt;python&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;m&lt;/span&gt; &lt;span class="n"&gt;this&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Y que nos devuelve el popular &lt;a href="http://www.python.org/dev/peps/pep-0020/"&gt;Zen of Python&lt;/a&gt;:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;The Zen of Python, by Tim Peters

Beautiful is better than ugly.
Explicit is better than implicit.
Simple is better than complex.
Complex is better than complicated.
Flat is better than nested.
Sparse is better than dense.
Readability counts.
Special cases aren&amp;#39;t special enough to break the rules.
Although practicality beats purity.
Errors should never pass silently.
Unless explicitly silenced.
In the face of ambiguity, refuse the temptation to guess.
There should be one-- and preferably only one --obvious way to do it.
Although that way may not be obvious at first unless you&amp;#39;re Dutch.
Now is better than never.
Although never is often better than *right* now.
If the implementation is hard to explain, it&amp;#39;s a bad idea.
If the implementation is easy to explain, it may be a good idea.
Namespaces are one honking great idea -- let&amp;#39;s do more of those!
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Pero existen varios one-liners muy útiles que nos pueden hacer la vida más fácil 
para determinadas tareas. &lt;/p&gt;
&lt;h3 id="iniciar_un_servidor_web_y_compartir_un_directorio"&gt;Iniciar un servidor web y compartir un directorio&lt;/h3&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="n"&gt;python&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;m&lt;/span&gt; &lt;span class="n"&gt;SimpleHTTPServer&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Si ejecutamos este one-liner se iniciará un servidor web en el puerto &lt;code&gt;8000&lt;/code&gt; de 
nuestra dirección local (&lt;code&gt;localhost&lt;/code&gt; o &lt;code&gt;127.0.0.1&lt;/code&gt;) que nos servirá los archivos 
del directorio donde lo ejecutemos. Para ello solo tenemos que acceder desde 
nuestro navegador a la dirección &lt;code&gt;http://localhost:8000&lt;/code&gt; o &lt;code&gt;http://127.0.0.1&lt;/code&gt;. 
Para interrumpir el servicio basta con que interrumpamos el programa con 
&lt;code&gt;Ctrl+C&lt;/code&gt;. Si quisiéramos emplear un puerto distinto de &lt;em&gt;8000&lt;/em&gt;, por ejemplo 
&lt;em&gt;8080&lt;/em&gt;, solo tenemos que indicarselo:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="n"&gt;python&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;m&lt;/span&gt; &lt;span class="n"&gt;SimpleHTTPServer&lt;/span&gt; &lt;span class="mi"&gt;8080&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;h3 id="iniciar_un_servidor_smtp"&gt;Iniciar un servidor SMTP&lt;/h3&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="n"&gt;python&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;m&lt;/span&gt; &lt;span class="n"&gt;smtpd&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="n"&gt;DebuggingServer&lt;/span&gt; &lt;span class="n"&gt;localhost&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;25&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Con este comando iniciamos un servidor de correo SMTP. Es básico, pero puede ser 
muy útil para crear un entorno temporal de pruebas. Dependiendo de la 
configuración de nuestro sistema, es muy probable que necesitemos ejecutar este 
comando como administrador (&lt;em&gt;root&lt;/em&gt;). Si ya tenemos corriendo un servidor de 
correo en ese puerto, podemos cambiarlo por otro (eg: &lt;em&gt;2525&lt;/em&gt;)&lt;/p&gt;
&lt;h3 id="iniciar_un_servidor_ftp"&gt;Iniciar un servidor FTP&lt;/h3&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="n"&gt;python&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;m&lt;/span&gt; &lt;span class="n"&gt;pyftpdlib&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ftpserver&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Para poder ejecutar este comando es necesario tener instalada la librería 
&lt;a href="http://code.google.com/p/pyftpdlib/"&gt;pyftpdlib&lt;/a&gt;. Debemos además ser usuario con permisos de administrador de la 
maquina para poder iniciar este servidor. Una vez ejecutado tendremos un 
servidor FTP de solo lectura en el directorio en el que lo iniciemos.&lt;/p&gt;
&lt;h3 id="ejecutar_un_cliente_ftp"&gt;Ejecutar un cliente FTP&lt;/h3&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="n"&gt;python&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;m&lt;/span&gt; &lt;span class="n"&gt;ftplib&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Si ejecutamos este comando, nos devolverá la ayuda con los argumentos necesarios 
para poder conectarnos a un servidor FTP.&lt;/p&gt;
&lt;h3 id="abrir_una_p+gina_web_en_el_navegador"&gt;Abrir una página web en el navegador&lt;/h3&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="n"&gt;python&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;m&lt;/span&gt; &lt;span class="n"&gt;webbrowser&lt;/span&gt; &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="o"&gt;//&lt;/span&gt;&lt;span class="n"&gt;joedicastro&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;com&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Nos abrirá la dirección web que le indiquemos en el navegador web predeterminado 
de nuestro equipo.&lt;/p&gt;
&lt;h3 id="obtener_el_c+digo_fuente_de_una_p+gina_web"&gt;Obtener el código fuente de una página web&lt;/h3&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="n"&gt;python&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;m&lt;/span&gt; &lt;span class="n"&gt;urllib&lt;/span&gt; &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="o"&gt;//&lt;/span&gt;&lt;span class="n"&gt;example&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;com&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Esto nos imprime en pantalla el código fuente (HTML) de una dirección web.&lt;/p&gt;
&lt;h3 id="eliminar_las_etiquetas_html_de_un_fichero_html"&gt;Eliminar las etiquetas HTML de un fichero HTML&lt;/h3&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="n"&gt;python&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;m&lt;/span&gt; &lt;span class="n"&gt;htmllib&lt;/span&gt; &lt;span class="n"&gt;test&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;html&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Esto nos imprime el contenido del fichero &lt;code&gt;test-html&lt;/code&gt; pero sin mostrarnos las 
etiquetas HTML, solo texto plano. Algo parecido a lo que hace un navegador en 
modo texto como &lt;em&gt;links&lt;/em&gt;, pero de forma estática.&lt;/p&gt;
&lt;h3 id="comparar_dos_directorios"&gt;Comparar dos directorios&lt;/h3&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="n"&gt;python&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;m&lt;/span&gt; &lt;span class="n"&gt;filecmp&lt;/span&gt; &lt;span class="n"&gt;dir_1&lt;/span&gt; &lt;span class="n"&gt;dir_2&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;De esta manera podemos comparar dos directorios. El resultado será un resumen de 
que ficheros son iguales, cuales diferentes y lo mismo con los subdirectorios.&lt;/p&gt;
&lt;h3 id="identificar_la_plataforma"&gt;Identificar la plataforma&lt;/h3&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="n"&gt;python&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;from sys import platform; print platform&amp;#39;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Este nos sirve para identificar la plataforma sobre la que se ejecuta el comando 
(Windows, Linux, ...) No es de mucha utilidad, pero nos sirve como curiosidad. 
Otra opción más simple y que nos devuelve un resultado más completo sería:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="n"&gt;python&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;m&lt;/span&gt; &lt;span class="n"&gt;platform&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;h3 id="obtener_el_nombre_de_la_maquina_hostname"&gt;Obtener el nombre de la maquina (hostname)&lt;/h3&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="n"&gt;python&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;from socket import gethostname; print gethostname()&amp;#39;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;ó&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="n"&gt;python&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;import platform; print platform.node()&amp;#39;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Cualquiera de estos dos one-liners nos devolverán el nombre de la maquina en que 
los ejecutemos.&lt;/p&gt;
&lt;h3 id="imprimir_el_calendario_del_a+o_en_curso"&gt;Imprimir el calendario del año en curso&lt;/h3&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="n"&gt;python&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;m&lt;/span&gt; &lt;span class="n"&gt;calendar&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Imprime en pantalla el calendario de todo el año. Para conocer más opciones, 
como imprimir el calendario de otro año, mes, etc añadir la opción &lt;code&gt;-h&lt;/code&gt; al final 
de la línea.&lt;/p&gt;
&lt;h3 id="mostrar_los_m+dulos_empleados_en_un_script"&gt;Mostrar los módulos empleados en un script&lt;/h3&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="n"&gt;python&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;m&lt;/span&gt; &lt;span class="n"&gt;modulefinder&lt;/span&gt; &lt;span class="n"&gt;script&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;py&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;De esta manera podemos no solo saber que módulos son importados por el script, 
si no que además nos muestra su ruta en el sistema de archivos.&lt;/p&gt;
&lt;p&gt;Aquí he mencionado solo algunos que empleo con cierta frecuencia o que me han 
parecido reseñables, pero existen bastantes más. Una buena pista es que la 
mayoría hace uso de los módulos de la librería estándar de Python.&lt;/p&gt;
&lt;h2 id="como_funciona"&gt;Como funciona&lt;/h2&gt;
&lt;p&gt;Básicamente se trata de llamar al interprete Python utilizando uno de los 
siguientes argumentos:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;-c&lt;/code&gt;: Lo que seguiría a esta opción sería un programa Python encerrado entre 
 comillas. Por ejemplo, el primer ejemplo quedaría de este modo, 
 &lt;code&gt;python -c 'import this'&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;-m&lt;/code&gt;: Está opción llama a un modulo de python, que puede a su vez tener 
 distintas opciones y argumentos, y lo ejecuta como script. Por ejemplo: 
 &lt;code&gt;python -m locale&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="https_server"&gt;HTTPS Server&lt;/h2&gt;
&lt;p&gt;Este no es un one-liner en Python, pero sirve como complemento a los distintos 
servidores que hemos visto que se podían iniciar con mucha facilidad gracias a 
Python. Este código inicia un servidor HTTPS en Python en el puerto &lt;em&gt;4443&lt;/em&gt; de la 
dirección local. Lo suyo sería iniciarlo en el puerto &lt;em&gt;443&lt;/em&gt;, y se puede hacer 
simplemente cambiándolo, pero se necesitan entonces permisos de administrador 
para iniciarlo.&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;BaseHTTPServer&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nn"&gt;SimpleHTTPServer&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;ssl&lt;/span&gt;

&lt;span class="n"&gt;httpd&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;BaseHTTPServer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;HTTPServer&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;localhost&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4443&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
                                  &lt;span class="n"&gt;SimpleHTTPServer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SimpleHTTPRequestHandler&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;httpd&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;socket&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ssl&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;wrap_socket&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;httpd&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;socket&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
                                &lt;span class="n"&gt;certfile&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;path/to/localhost.pem&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
                                &lt;span class="n"&gt;server_side&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;httpd&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;serve_forever&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;El código es original de &lt;a href="http://www.piware.de/2011/01/creating-an-https-server-in-python/"&gt;Martin Pitt&lt;/a&gt; y me ha servido en más de una ocasión 
para probar un script.&lt;/p&gt;
&lt;br /&gt;

&lt;h3 id="editar_una_p+gina_web_con_javascript"&gt;Editar una página web con javascript&lt;/h3&gt;
&lt;p&gt;No es un one-liner escrito en Python, si no en javascript, pero lo menciono aquí 
a modo de curiosidad porque siempre me ha parecido curioso y aparentemente 
inútil, pero tiene sus aplicaciones (alguna broma ha caído gracias a él).&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="nx"&gt;javascript&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;contentEditable&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;true&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;designMode&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;on&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Para ejecutarlo es muy sencillo. Abrimos nuestro navegador web favorito, 
abrimos la página web que deseamos editar y luego simplemente sustituimos el 
texto de la barra de direcciones por el de este one-liner y pulsamos Enter. 
Ahora podemos editar la página web como si fuera un procesador de texto.&lt;/p&gt;</summary><category term="one-liner"></category><category term="linux"></category><category term="python"></category><category term="javascript"></category></entry><entry><title>Comprobar si un programa está instalado con Python</title><link href="http://joedicastro.com/comprobar-si-un-programa-esta-instalado-con-python.html" rel="alternate"></link><updated>2011-04-28T14:04:00+02:00</updated><author><name>joe di castro</name></author><id>http://joedicastro.com/comprobar-si-un-programa-esta-instalado-con-python.html</id><summary type="html">&lt;p&gt;Cuando creamos un script en &lt;strong&gt;Python&lt;/strong&gt;, sobre todo aquellos orientados a 
ejecutarse en la línea de comandos, a veces necesitamos echar mano de un 
programa externo que no siempre viene por defecto instalado en el sistema. Por 
ejemplo, en el script que empleo en 
&lt;a href="http://joedicastro.com/optimizar_imagenes_para_la_web"&gt;optimizar imágenes para la web&lt;/a&gt; 
empleo los programas externos &lt;em&gt;pngcrush&lt;/em&gt; y &lt;em&gt;jpegtran&lt;/em&gt;. ¿Como comprobamos 
entonces si el programa está instalado? Desde luego es siempre mejor 
comprobarlo y avisar al usuario, que dejar que arroje un feo error.&lt;/p&gt;
&lt;p&gt;Una forma de comprobar si el programa está instalado es capturando la excepción 
cuando se produzca con las sentencias &lt;code&gt;try&lt;/code&gt; y &lt;code&gt;except&lt;/code&gt;, incluyendo la llamada al 
ejecutable dentro de ellas y devolver un mensaje de error avisando de la 
necesidad de la presencia de este ejecutable. Pero al igual que en el ejemplo 
anterior, yo prefiero comprobar esto incluso antes de ejecutar cualquier otro 
código dentro del script. Para ello empleo el &lt;a href="http://ibiblio.org/g2swap/byteofpython/read/module-name.html"&gt;conocido truco&lt;/a&gt; que nos 
permite ejecutar cierto código solo cuando es ejecutado como script y no cuando 
es importado como módulo:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;__name__&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;__main__&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
   &lt;span class="n"&gt;chequeo_ejecutable&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
   &lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Donde &lt;code&gt;main()&lt;/code&gt; es la función principal donde albergaríamos el código fundamental 
del script. Es un conocido &lt;a href="http://python.net/~goodger/projects/pycon/2007/idiomatic/handout.html"&gt;Python Idiom&lt;/a&gt; que me sirve perfectamente para 
esta tarea. De hecho lo empleo habitualmente en todos mis scripts para separar 
el código principal de las funciones. &lt;/p&gt;
&lt;p&gt;Comprobar en Linux si existe el ejecutable es realmente sencillo (siempre y 
cuando conozcamos el nombre exacto del ejecutable) con este código:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="c"&gt;#!/usr/bin/env python&lt;/span&gt;
&lt;span class="c"&gt;# -*- coding: utf8 -*-&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;sys&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;subprocess&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Popen&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;PIPE&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;check_execs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;progs&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;Check if the programs are installed, if not exit and report.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;prog&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;progs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;Popen&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="n"&gt;prog&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;--help&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;stdout&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;PIPE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;stderr&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;PIPE&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="ne"&gt;OSError&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;msg&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;The {0} program is necessary to run this script&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;prog&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;exit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="c"&gt;# Incluir aquí el código fundamental del script&lt;/span&gt;
    &lt;span class="k"&gt;pass&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;__name__&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;__main__&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;check_execs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;python&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Si no existiera el ejecutable que le pasamos a la función &lt;code&gt;check_execs()&lt;/code&gt; 
entonces se detendrá el script y nos dirá que necesitamos instalarlo para 
ejecutar el script. Hacerlo así nos evita cualquier tipo de manipulación previa 
antes de darnos cuenta de que nos falta un elemento esencial para ejecutarlo al 
completo. Esta función funciona porque en Linux los ejecutables normalmente 
están colgando de una ruta del PATH. En Windows, por ejemplo, la cosa cambia.&lt;/p&gt;
&lt;p&gt;Si queremos que el script sea multiplataforma y que nos funcione tanto en 
sistemas *NIX como en Windows entonces necesitamos una función algo más 
compleja. En Windows un ejecutable no necesita colgar del PATH y es muy 
frecuente que no sea así. Al mismo tiempo en Windows podemos tener el sistema de 
ficheros repartido en varias unidades de disco, y necesitamos explorar todas 
ellas para buscar nuestro ejecutable. Aunque lo más frecuente es que se 
encuentre en la unidad C: y la función se detiene en cuanto encuentra el primer 
ejecutable, el hecho de realizar está búsqueda hace que el proceso sea más lento 
en Windows que en Linux. &lt;/p&gt;
&lt;p&gt;La función multiplataforma es la siguiente:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;check_execs_posix_win&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;progs&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;Check if the program is installed.&lt;/span&gt;

&lt;span class="sd"&gt;    Returns one  dictionary with 1+n pair of key/values:&lt;/span&gt;

&lt;span class="sd"&gt;    A fixed key/value:&lt;/span&gt;

&lt;span class="sd"&gt;    &amp;quot;WinOS&amp;quot; -- (boolean) True it&amp;#39;s a Windows OS, False it&amp;#39;s a *nix OS&lt;/span&gt;

&lt;span class="sd"&gt;    for each program in progs a key/value like this:&lt;/span&gt;

&lt;span class="sd"&gt;    &amp;quot;program&amp;quot;  -- (str or boolean) The Windows executable path if founded else &lt;/span&gt;
&lt;span class="sd"&gt;                                   &amp;#39;&amp;#39; if it&amp;#39;s Windows OS. If it&amp;#39;s a *NIX OS True&lt;/span&gt;
&lt;span class="sd"&gt;                                   if founded else False&lt;/span&gt;

&lt;span class="sd"&gt;    &amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
    &lt;span class="n"&gt;execs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;WinOS&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;platform&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;system&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;Windows&amp;#39;&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="c"&gt;# get all the drive unit letters if the OS is Windows&lt;/span&gt;
    &lt;span class="n"&gt;windows_drives&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;findall&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;r&amp;#39;(\w:)&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                             &lt;span class="n"&gt;Popen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;fsutil fsinfo drives&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;stdout&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;PIPE&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;
                             &lt;span class="n"&gt;communicate&lt;/span&gt;&lt;span class="p"&gt;()[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;execs&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;WinOS&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;

    &lt;span class="n"&gt;progs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;progs&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nb"&gt;isinstance&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;progs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="n"&gt;progs&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;prog&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;progs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;execs&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;WinOS&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
            &lt;span class="c"&gt;# Set all commands to search the executable in all drives&lt;/span&gt;
            &lt;span class="n"&gt;win_cmds&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;dir /B /S {0}\*{1}.exe&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;letter&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;prog&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt;
                        &lt;span class="n"&gt;letter&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;windows_drives&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
            &lt;span class="c"&gt;# Get the first location (usually in C:) where the executable exists&lt;/span&gt;
            &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;cmd&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;win_cmds&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="n"&gt;execs&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;prog&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Popen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cmd&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;stdout&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;PIPE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;stderr&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;PIPE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;shell&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;
                               &lt;span class="n"&gt;communicate&lt;/span&gt;&lt;span class="p"&gt;()[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;linesep&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
                &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;execs&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;prog&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
                    &lt;span class="k"&gt;break&lt;/span&gt;
        &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="n"&gt;Popen&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="n"&gt;prog&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;--help&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;stdout&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;PIPE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;stderr&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;PIPE&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="n"&gt;execs&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;prog&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;
            &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="ne"&gt;OSError&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="n"&gt;execs&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;prog&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;execs&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;En la parte de Windows (la de *NIX es básicamente igual) esta función lo que 
hace es obtener primero las letras de las unidades de disco disponibles en el 
sistema. Luego busca el ejecutable en cada una de ellas y en cuanto encuentra 
la primera ruta al ejecutable se detiene. La función en este caso devuelve un 
diccionario donde hay una clave fija que es &lt;code&gt;WinOS&lt;/code&gt; que será &lt;code&gt;True&lt;/code&gt; si estamos 
en Windows y &lt;code&gt;False&lt;/code&gt; en caso contrario. Luego nos devuelve una clave por cada 
uno de los programas que le mandemos comprobar. Esta clave sera &lt;abbr title="Verdadero o Falso. El nombre proviene de la Álgebra de Boole."&gt;booleana&lt;/abbr&gt; en el 
caso de *NIX y la ruta del programa (o una cadena vacía) en el caso de Windows.&lt;/p&gt;
&lt;p&gt;Las funciones y un ejemplo de su funcionamiento podéis encontrarlas en mi 
&lt;a href="https://bitbucket.org/joedicastro/python-recipes/src/tip/src/check_execs.py"&gt;repositorio&lt;/a&gt;&lt;/p&gt;</summary><category term="python idiom"></category><category term="script"></category><category term="python"></category></entry><entry><title>Crear un arbol de directorios falso para entorno de pruebas</title><link href="http://joedicastro.com/crear-un-arbol-de-directorios-falso-para-entorno-de-pruebas.html" rel="alternate"></link><updated>2011-04-27T20:52:00+02:00</updated><author><name>joe di castro</name></author><id>http://joedicastro.com/crear-un-arbol-de-directorios-falso-para-entorno-de-pruebas.html</id><summary type="html">&lt;p&gt;A veces, para realizar pruebas con nuestros propios programas o para, por 
ejemplo, hacer pruebas con programas de Backup o FTP, necesitamos crear un 
entorno de pruebas que nos permita hacer todo tipo de operaciones sin comprometer 
la seguridad de nuestros datos reales. Una practica habitual es hacer copias de 
directorios de nuestros propios archivos y trabajar sobre ellos. Es algo que 
creo se debería dejar únicamente para la última fase de las pruebas, cuando en 
teoría ya todo funciona. Confundir ubicaciones y malograr los datos reales es 
más fácil de lo que pueda parecer, sobre todo si se comparte el mismo sistema de 
archivos. Otra veces lo que necesitamos es generar una estructura de directorios 
totalmente aleatoria, con sus correspondientes archivos de forma temporal. &lt;/p&gt;
&lt;p&gt;He creado un script &lt;strong&gt;Python&lt;/strong&gt; que hace precisamente esto último, generar una 
jerarquía completa de directorios, de forma aleatoria, con sus correspondientes 
archivos de texto para simular una estructura de directorios real. Cuando le 
estaba dando vueltas a como generar de forma aleatoria los nombres de los 
archivos y que al mismo tiempo fuera multiplataforma (respetando los caracteres 
no permitidos según el sistema de archivos) me acorde de &lt;a href="http://es.wikipedia.org/wiki/Lorem_ipsum"&gt;Lorem Ipsum&lt;/a&gt;. El 
texto que se lleva empleando desde los tiempos de la imprenta para generar 
borradores y que aún se emplea en diseño gráfico para los contenidos de prueba 
(al igual que en la web). No solo hacía de este modo un guiño a esta veterana 
prueba de maquetación, si no que me solucionaba la papeleta de generar los 
nombres de directorios y archivos. Al estar compuesto de "palabras" en latín 
(solo caracteres del alfabeto) me olvidaba de las incompatibilidades entre 
plataformas y sistemas de archivo. Un homenaje y un problema resuelto en un solo 
paso.&lt;/p&gt;
&lt;p&gt;Cuando me disponía ya a crear un modo de extraer palabras aleatorias del texto 
original de Lorem Ipsum, pensé que seguro que no era el primero al que se le 
ocurría hacer esto con Python, así que después de una pequeña búsqueda en el 
&lt;a href="http://pypi.python.org/pypi"&gt;&lt;abbr title="Python Package Index"&gt;&lt;abbr title="Python Package Index"&gt;PyPI&lt;/abbr&gt;&lt;/abbr&gt;&lt;/a&gt;, di con el magnifico proyecto de James Hales, 
&lt;a href="http://code.google.com/p/lorem-ipsum-generator/"&gt;Lorem-Ipsum-Generator&lt;/a&gt;. Importando el modulo de este proyecto en mi script, 
tenía medio trabajo hecho.&lt;/p&gt;
&lt;p&gt;El script, al que llamo &lt;strong&gt;test_dir_tree&lt;/strong&gt;, genera una estructura de directorios 
de entre 7 y 39 directorios (estos parámetros pueden ser fácilmente cambiados en 
el script), con sus correspondientes archivos. El número de archivos se decide 
en función del número de directorios y se reparten también de forma aleatoria 
entre los mismos. Como he comentado, los nombres tanto de directorios como 
ficheros proceden de Lorem Ipsum. El tamaño de los archivos oscila entre 3 y 512 
Kbytes, para no ocupar demasiado espacio en disco. Son archivos de texto 
normales en los cuales el contenido está formado a su vez por párrafos 
aleatorios de Lorem Ipsum.&lt;/p&gt;
&lt;p&gt;No es necesario ningún parámetro para ejecutar el script:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="go"&gt;python test_dir_tree.py&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Una vez ejecutado el script, se genera una estructura de directorios como la de 
este ejemplo:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;test
├── blandit
│   ├── [234K]  bibendum.txt
│   ├── [319K]  congue.txt
│   ├── [382K]  consequat.txt
│   └── [298K]  ligula.txt
├── elit
├── est
│   ├── [ 22K]  condimentum.txt
│   ├── [401K]  hac.txt
│   ├── [139K]  nibh.txt
│   └── [ 12K]  tincidunt.txt
├── habitasse
│   ├── [359K]  etiam.txt
│   ├── [ 59K]  facilisi.txt
│   ├── [ 12K]  integer.txt
│   ├── [ 23K]  metus.txt
│   └── [ 31K]  non.txt
├── lacus
│   ├── [394K]  faucibus.txt
│   └── [ 29K]  torquent.txt
├── laoreet
│   └── [424K]  eros.txt
├── litora
│   └── [ 87K]  tellus.txt
└── nostra
    ├── [349K]  curabitur.txt
    ├── [241K]  neque.txt
    ├── [3.4K]  odio.txt
    └── [ 78K]  sapien.txt
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Donde el principio del contenido de uno de esos archivos de texto, sería algo así:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;Eget. Sem turpis tempus sagittis arcu, nullam ac rutrum turpis. Sem
fusce lacus, cum neque fermentum potenti. Est aliquam donec, leo
amet elit dapibus ipsum. Quam pellentesque eu fusce pellentesque
torquent netus vivamus velit at nisl. Senectus ligula, erat dictum.
Natoque metus urna quis vivamus in duis. Cras. Massa nam. Quisque.
Potenti, sit. Urna metus, et eu duis suspendisse per primis. Duis,
viverra massa enim hac.

Fames ut leo in a turpis proin gravida ac, auctor. Natoque
suspendisse nisl. Dictumst mus, amet pede, eni velit velit. Elit.
Cum. Congue pretium ad id porta in massa enim purus. Tempus, porta
donec molestie habitasse, urna urna nam. Tempor massa sed quam sit
nec dapibus at, duis. Leo pellentesque. Orci arcu iaculis ac, elit
netus conubia. Penatibus platea rutrum netus suspendisse non. Purus
consequat. Inceptos platea, tempus metus eu consectetuer feugiat,
urna at lorem pellentesque curae. Dapibus a, scelerisque cum, auctor
 tincidunt primis. Augue nisi id per nulla, sit enim mus id eleifend
 taciti, semper est.
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;El script tarda muy poco en generar esta estructura, por ejemplo en un Core 2 
Duo a 2 GHz, se tardan 1.72s en generar una estructura de 71 directorios y 114 
ficheros.&lt;/p&gt;
&lt;p&gt;Este es el núcleo central del script &lt;strong&gt;test_dir_tree.py&lt;/strong&gt;:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test_tree&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;min_dirs&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;max_dirs&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;79&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;make a fake directory hierarchy with files for test purposes.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;latin_words&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;generator&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;Generate a list of latin words&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
        &lt;span class="n"&gt;words&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;generator&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;generate_paragraphs_plain&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;lower&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;words&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;.&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;,&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;split&lt;/span&gt;&lt;span class="p"&gt;()))&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;check_path&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;If no exists a path, make it.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;exists&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
            &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mkdir&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;lorem&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;lipsum&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;MarkupGenerator&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;latins&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;latin_words&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;lorem&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;dirs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;latins&lt;/span&gt;&lt;span class="p"&gt;[:&lt;/span&gt;&lt;span class="n"&gt;randrange&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;min_dirs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;max_dirs&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
    &lt;span class="n"&gt;files&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;latins&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;dirs&lt;/span&gt;&lt;span class="p"&gt;][:&lt;/span&gt;&lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;dirs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

    &lt;span class="n"&gt;check_path&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;directory&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;dirs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;check_path&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;directory&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;fil&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;files&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;filename&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;choice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dirs&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;{0}.txt&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fil&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="n"&gt;text&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;&amp;#39;&lt;/span&gt;
        &lt;span class="n"&gt;size&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;randint&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;524288&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c"&gt;# Files not bigger than 512 Kbytes&lt;/span&gt;
        &lt;span class="n"&gt;sample&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;lorem&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;generate_paragraphs_plain&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;randrange&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;text&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;sample&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;linesep&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;
        &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nb"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;w&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;out&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;out&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;[:&lt;/span&gt;&lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;dirs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;files&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Para obtener el script completo, acudir a mi &lt;a href="https://bitbucket.org/joedicastro/python-recipes/src/tip/src/test_dir_tree.py"&gt;repositorio&lt;/a&gt;.&lt;/p&gt;</summary><category term="script"></category><category term="python"></category><category term="lorem ipsum"></category></entry><entry><title>Mover todos los archivos del mismo tipo de un arbol de directorios a la vez</title><link href="http://joedicastro.com/mover-todos-los-archivos-del-mismo-tipo-de-un-arbol-de-directorios-a-la-vez.html" rel="alternate"></link><updated>2011-04-26T23:17:00+02:00</updated><author><name>joe di castro</name></author><id>http://joedicastro.com/mover-todos-los-archivos-del-mismo-tipo-de-un-arbol-de-directorios-a-la-vez.html</id><summary type="html">&lt;p&gt;A veces tenemos la necesidad de &lt;strong&gt;mover (o copiar o borrar)&lt;/strong&gt; varios ficheros 
del mismo tipo a la vez, pero estos no "cuelgan" de un solo directorio, si no 
que se encuentran repartidos dentro una jerarquía de subdirectorios. En este 
caso los típicos comandos para mover/copiar/borrar archivos no nos sirven, ni 
siquiera los útiles comodines nos resuelven el problema.  ¿Como lo hacemos 
entonces?&lt;/p&gt;
&lt;p&gt;Por ejemplo, si tenemos una estructura de directorios como esta:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;raiz
├── dir_1
│   ├── imagen_1.jpg
│   ├── pdf_1.pdf
│   ├── texto_1.txt
│   └── texto_2.txt
├── dir_2
│   ├── imagen_2.jpg
│   ├── imagen_3.gif
│   └── texto_3.txt
├── dir_3
│   ├── doc_1.doc
│   ├── pdf_2.pdf
│   ├── pdf_3.pdf
│   ├── pdf_4.pdf
│   ├── texto_4.txt
│   └── texto_5.txt
├── dir_4
│   ├── subdir_1
│   │   └── imagen_4.png
│   ├── subdir_2
│   │   ├── pdf_5.pdf
│   │   └── texto_7.txt
│   ├── doc_2.doc
│   └── texto_6.txt
└── dir_5
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Y queremos mover todos los ficheros &lt;code&gt;.txt&lt;/code&gt; al &lt;code&gt;dir_5&lt;/code&gt; que tenemos vacío, ¿Como 
lo hacemos? Bueno, en el caso de &lt;strong&gt;Linux&lt;/strong&gt; (y UNIX) podemos hacerlo en una sola 
operación gracias al siempre útil y versátil &lt;code&gt;find&lt;/code&gt; con el siguiente comando 
(ejecutado desde el directorio raíz):&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;find . -type f -name *.txt -exec mv &lt;span class="o"&gt;{}&lt;/span&gt; ./dir_5 &lt;span class="se"&gt;\;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Este comando nos encontraría todos los archivos con la extensión &lt;code&gt;.txt&lt;/code&gt; dentro 
de todos los directorios y subdirectorios y los iría moviendo uno a uno al 
&lt;code&gt;dir_5&lt;/code&gt;. Es un comando rápido y efectivo. Genéricamente, el comando sería así:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;find directorio_origen -type f -name *.EXT -exec mv &lt;span class="o"&gt;{}&lt;/span&gt; ./directorio_destino &lt;span class="se"&gt;\;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;donde &lt;code&gt;EXT&lt;/code&gt; sería la extensión del tipo de archivo que queremos mover. Se puede 
emplear esta variante para copiar los archivos en vez de moverlos:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;find directorio_origen -type f -name *.EXT -exec cp &lt;span class="o"&gt;{}&lt;/span&gt; ./directorio_destino &lt;span class="se"&gt;\;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Y esta otra para eliminarlos (¡emplear con cuidado!):&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;find directorio_origen -type f -name *.EXT -exec rm -f &lt;span class="o"&gt;{}&lt;/span&gt; &lt;span class="se"&gt;\;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Partiendo de la idea de hacer algo equivalente en &lt;strong&gt;Python&lt;/strong&gt;, he creado un 
script que hace algo similar a esto, sin la necesidad de teclear todo el comando 
(y con el riesgo a equivocarse y liarla parda) y que también sirve para que los 
no trabajen habitualmente con la consola no necesiten recordar (o anotar) esos 
comandos.&lt;/p&gt;
&lt;p&gt;La parte principal del script &lt;strong&gt;move_by_ext.py&lt;/strong&gt; es la siguiente:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;find_and_process&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;count&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;Find the files by file extension and process (move/copy/remove) them.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;process&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;the_path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;the_file&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;Process each file.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
        &lt;span class="n"&gt;processed&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
        &lt;span class="n"&gt;src_file&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;the_path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;the_file&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;dst_file&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dst&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;the_file&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;rm&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;remove&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;src_file&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;processed&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
        &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;exists&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dst_file&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="c"&gt;# not replace if already exists &lt;/span&gt;
                &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cp&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                    &lt;span class="n"&gt;shutil&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;copy2&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;src_file&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;dst_file&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                    &lt;span class="n"&gt;shutil&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;move&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;src_file&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;dst_file&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="n"&gt;processed&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;processed&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;exists&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dst&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mkdir&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dst&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;directories&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;files&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;walk&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;src&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;fil&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;files&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="c"&gt;# ignore files without extension (can have the same name as the ext)&lt;/span&gt;
            &lt;span class="n"&gt;file_ext&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;fil&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;.&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fil&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;.&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;
            &lt;span class="c"&gt;# ignore dots in given extensions&lt;/span&gt;
            &lt;span class="n"&gt;extensions&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;ext&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;.&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;ext&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ext&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;file_ext&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;extensions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="n"&gt;count&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;process&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;fil&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;opt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;{0}{1}&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;rm&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cp&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;log&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;Files {0}: {1}&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;format&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;moved&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;copied&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;removed&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;}[&lt;/span&gt;&lt;span class="n"&gt;opt&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;count&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;log&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Este script nos permite realizar las mismas operaciones que los comandos 
anteriores y además tiene dos ventajas adicionales:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Permite emplear varias extensiones a la vez&lt;/strong&gt;, lo que nos permite
mover/copiar/borrar distintos tipos de archivo en una sola operación.
Por ejemplo si queremos mover todos los tipos de imagen de la estructura de 
directorios anterior, podríamos hacerlo indicándole que emplee las extensiones 
&lt;code&gt;.jpg&lt;/code&gt;, &lt;code&gt;.gif&lt;/code&gt; y &lt;code&gt;.png&lt;/code&gt; en la misma operación. El comando sería el siguiente 
(las extensiones pueden llevar o no el punto, funciona exactamente igual), 
ejecutado desde el directorio raíz:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;python move_by_ext.py jpg .gif png -d ./dir_5&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Funciona en Windows&lt;/strong&gt;. A diferencia del comando para Linux, este se puede 
emplear en Windows sin necesidad de instalar ningún entorno que nos simule un 
shell Linux (como &lt;a href="http://www.cygwin.com/"&gt;Cygwin&lt;/a&gt;) y solo es necesario tener 
instalado Python 2.7&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;La sintaxis del script es muy sencilla y se puede ver reflejada en la ayuda del 
mismo:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="gp"&gt;$&lt;/span&gt; python move_by_ext.py -h
&lt;span class="go"&gt;usage: move_by_ext.py ext [-s SRC] [-d DST] [-c | -r] [--help]&lt;/span&gt;

&lt;span class="go"&gt;Move (or copy/remove) all files selected by extension into a directory tree to&lt;/span&gt;
&lt;span class="go"&gt;a destination directory.&lt;/span&gt;

&lt;span class="go"&gt;positional arguments:&lt;/span&gt;
&lt;span class="go"&gt;  ext                the extension(s) of the files to process. To use more&lt;/span&gt;
&lt;span class="go"&gt;                     than one extension, separate them with a space&lt;/span&gt;

&lt;span class="go"&gt;optional arguments:&lt;/span&gt;
&lt;span class="go"&gt;  -h, --help         show this help message and exit&lt;/span&gt;
&lt;span class="go"&gt;  -s SRC, --src SRC  the source path. Current dir if none is provided&lt;/span&gt;
&lt;span class="go"&gt;  -d DST, --dst DST  the destination path. Current dir if none is provided&lt;/span&gt;
&lt;span class="go"&gt;  -c, --copy         copy all the files with the given extension(s) to the&lt;/span&gt;
&lt;span class="go"&gt;                     destination directory.&lt;/span&gt;
&lt;span class="go"&gt;  -r, --remove       remove all the files with the given extension(s). Use&lt;/span&gt;
&lt;span class="go"&gt;                     with caution! remove also in the subdirectories&lt;/span&gt;
&lt;span class="go"&gt;  -v, --version      show program&amp;#39;s version number and exit&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;El script completo puede encontrarse en mi &lt;a href="https://bitbucket.org/joedicastro/python-recipes/src/tip/src/move_by_ext.py"&gt;repositorio&lt;/a&gt;.&lt;/p&gt;</summary><category term="python"></category><category term="script"></category><category term="mover"></category><category term="copiar"></category><category term="borrar"></category><category term="linux"></category><category term="windows"></category></entry><entry><title>Comparativa de Lenguajes de marcado ligero</title><link href="http://joedicastro.com/comparativa-de-lenguajes-de-marcado-ligero.html" rel="alternate"></link><updated>2011-04-02T20:29:00+02:00</updated><author><name>joe di castro</name></author><id>http://joedicastro.com/comparativa-de-lenguajes-de-marcado-ligero.html</id><summary type="html">&lt;p&gt;Esta es una comparativa de los lenguajes de marcado ligero más empleados de los 
disponibles actualmente. Dicha comparativa surge a partir de este articulo, 
&lt;a href="http://joedicastro.com/markdown-la-mejor-opcion-para-crear-contenidos-web.html"&gt;Markdown, la mejor opción para crear contenidos web&lt;/a&gt;, donde defiendo la 
idoneidad de markdown para crear contenidos web.&lt;/p&gt;
&lt;p&gt;En esta comparativa se verá como emplear cada uno de los lenguajes de marcado 
disponibles para crear un contenido web similar. Tomo como referencia a markdown, 
aunque no todos los lenguajes soportan todas o las mismas características que 
este.&lt;/p&gt;
&lt;h2 id="documento_de_ejemplo_realizado_con_markdown"&gt;Documento de ejemplo realizado con Markdown&lt;/h2&gt;
&lt;div&gt;
&lt;table&gt;
 &lt;thead&gt;
  &lt;tr&gt;
   &lt;th style="width: 50%;"&gt;Markdown&lt;/th&gt;&lt;th&gt;Resultado&lt;/th&gt;
  &lt;/tr&gt;
 &lt;tbody&gt;
  &lt;tr&gt;
   &lt;td&gt;
    &lt;pre class="no_mrkdwn"&gt;
&lt;p&gt;Documento de ejemplo
====================&lt;/p&gt;&lt;p&gt;Lorem ipsum [dolor sit amet](#mark), consectetur adipiscing elit. Curabitur eget ante nunc. Pellentesque a tortor ipsum, id rhoncus orci. Quisque leo sapien, rutrum id convallis id, rutrum in ligula. Vestibulum **semper adipiscing leo** et blandit.&lt;/p&gt;&lt;p&gt;Sed nibh quam, hendrerit _sit amet aliquam_ vel, pulvinar molestie augue.&lt;/p&gt;&lt;p&gt;&amp;gt; Integer cursus, nunc eu ultrices pellentesque, eros leo malesuada turpis, vel convallis neque dolor a nunc. Sed lacus risus, condimentum vitae posuere quis, ultrices pharetra nunc.&lt;/p&gt;&lt;p&gt;Lenguajes de marcado ligero&lt;/p&gt;&lt;p&gt; * **Markdown**
 * Textile
 * reStructuredText
 * Texy!
 * Txt2tags
 * Marcado Wiki
   1. Creole
   2. MediaWiki&lt;/p&gt;&lt;p&gt;![avatar](pictures/no_wysiwyg.png)&lt;/p&gt;&lt;p&gt;### Cabecera H3 ###&lt;/p&gt;&lt;p&gt;- - -&lt;/p&gt;&lt;p&gt;Morbi erat augue, feugiat eu pellentesque eget, hendrerit quis lectus. Fusce dignissim pretium nibh sed dignissim. Pellentesque lobortis ante eu dui fermentum vitae blandit risus aliquet.&lt;/p&gt;&lt;p&gt;|   | solo texto | HTML Limpio  |
| -------------- | -- | ------- |
| Markdown       | Si | Si      |
| Editor WYSISWG | X  | A veces |&lt;/p&gt;_Ejemplo de código_&lt;/p&gt;&lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;import lifetime
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;for each_day in lifetime.days():
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;carpe_diem()&lt;/p&gt;&lt;p&gt;Suspendisse posuere velit et velit vehicula at scelerisque orci suscipit. Nulla facilisis lorem eu sem viverra varius nec ut felis.&lt;/p&gt;&lt;p&gt;Esto es un texto con nota al pie [^ejemplo] y esta es otra nota [^segunda]&lt;/p&gt;&lt;p&gt;
 *[vehicula]: automobila
[^ejemplo]: Esto es una nota al pie.
[^segunda]: Esto es la segunda nota.&lt;/p&gt;&lt;/pre&gt;
   &lt;/td&gt;
   &lt;td&gt;
&lt;h1&gt;Documento de ejemplo&lt;/h1&gt;

&lt;p&gt;Lorem ipsum &lt;a href="#mark"&gt;dolor sit amet&lt;/a&gt;, consectetur adipiscing elit. Curabitur eget ante nunc. Pellentesque a tortor ipsum, id rhoncus orci. Quisque leo sapien, rutrum id convallis id, rutrum in ligula. Vestibulum &lt;strong&gt;semper adipiscing leo&lt;/strong&gt; et blandit.&lt;/p&gt;

&lt;p&gt;Sed nibh quam, hendrerit &lt;em&gt;sit amet aliquam&lt;/em&gt; vel, pulvinar molestie augue.&lt;/p&gt;

&lt;blockquote&gt; 
  &lt;p&gt;Integer cursus, nunc eu ultrices pellentesque, eros leo malesuada turpis, vel convallis neque dolor a nunc. Sed lacus risus, condimentum vitae posuere quis, ultrices pharetra nunc.&lt;/p&gt; 
&lt;/blockquote&gt;

&lt;p&gt;Lenguajes de marcado ligero&lt;/p&gt;

&lt;ul&gt; 
&lt;li&gt;&lt;strong&gt;Markdown&lt;/strong&gt;&lt;/li&gt; 
&lt;li&gt;Textile&lt;/li&gt; 
&lt;li&gt;reStructuredText&lt;/li&gt; 
&lt;li&gt;Texy!&lt;/li&gt; 
&lt;li&gt;Txt2tags&lt;/li&gt; 
&lt;li&gt;Marcado Wiki

&lt;ol&gt; 
&lt;li&gt;Creole&lt;/li&gt; 
&lt;li&gt;MediaWiki&lt;/li&gt; 
&lt;/ol&gt;&lt;/li&gt; 
&lt;/ul&gt;

&lt;p&gt;&lt;img src="pictures/no_wysiwyg.png" alt="avatar" /&gt;&lt;/p&gt;

&lt;h3&gt;Cabecera H3&lt;/h3&gt;

&lt;hr /&gt;

&lt;p&gt;Morbi erat augue, feugiat eu pellentesque eget, hendrerit quis lectus. Fusce dignissim pretium nibh sed dignissim. Pellentesque lobortis ante eu dui fermentum vitae blandit risus aliquet.&lt;/p&gt;

&lt;table&gt; 
&lt;thead&gt; 
&lt;tr&gt; 
  &lt;th&gt;&lt;/th&gt; 
  &lt;th&gt;solo texto&lt;/th&gt; 
  &lt;th&gt;HTML Limpio&lt;/th&gt; 
&lt;/tr&gt; 
&lt;/thead&gt; 
&lt;tbody&gt; 
&lt;tr&gt; 
  &lt;td&gt;Markdown&lt;/td&gt; 
  &lt;td&gt;Si&lt;/td&gt; 
  &lt;td&gt;Si&lt;/td&gt; 
&lt;/tr&gt; 
&lt;tr&gt; 
  &lt;td&gt;Editor WYSISWG&lt;/td&gt; 
  &lt;td&gt;X&lt;/td&gt; 
  &lt;td&gt;A veces&lt;/td&gt; 
&lt;/tr&gt; 
&lt;/tbody&gt; 
&lt;/table&gt;

&lt;p&gt;&lt;em&gt;Ejemplo de código&lt;/em&gt;&lt;/p&gt;

&lt;pre class="txt" style="font-family:monospace;"&gt;import lifetime
&amp;nbsp;
for each_day in lifetime.days():
    carpe_diem()&lt;/pre&gt;

&lt;p&gt;Suspendisse posuere velit et velit &lt;abbr title="automobila"&gt;vehicula&lt;/abbr&gt; at scelerisque orci suscipit. Nulla facilisis lorem eu sem viverra varius nec ut felis.&lt;/p&gt;

&lt;p&gt;Esto es un texto con nota al pie &lt;sup id="fnref:ejemplo"&gt;&lt;a href="#fn:ejemplo" rel="footnote"&gt;1&lt;/a&gt;&lt;/sup&gt; y esta es otra nota &lt;sup id="fnref:segunda"&gt;&lt;a href="#fn:segunda" rel="footnote"&gt;2&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;

&lt;div class="footnotes"&gt; 
&lt;hr /&gt; 
&lt;ol&gt;

&lt;li id="fn:ejemplo"&gt; 
&lt;p&gt;Esto es una nota al pie.&amp;#160;&lt;a href="#fnref:ejemplo" rev="footnote"&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt; 
&lt;/li&gt;

&lt;li id="fn:segunda"&gt; 
&lt;p&gt;Esto es la segunda nota.&amp;#160;&lt;a href="#fnref:segunda" rev="footnote"&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt; 
&lt;/li&gt;

&lt;p&gt;&lt;/ol&gt; 
&lt;/div&gt; 
   &lt;/td&gt;
  &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h2 id="textile"&gt;Textile&lt;/h2&gt;
&lt;p&gt;Es una buena alternativa a Markdown y bastante extendido, aunque quizás menos 
que Markdown. Tiene algunas posibilidades que no tiene Markdown como emplear 
colores, poder alinear el texto o emplear superindice y subindice. También tiene 
carencias como el no poder dibujar líneas horizontales o el poder emplear 
acronimos solo con mayusculas y tener que declararlos en cada una de las partes 
del texto que aparezcan. Pero quiźas para mi la mayor desventaja es la menor 
legibilidad del texto, es menos evidente a un vistazo que markdown.&lt;/p&gt;
&lt;table&gt;
 &lt;thead&gt;
  &lt;tr&gt;
   &lt;th style="width: 50%;"&gt;Textile&lt;/th&gt;&lt;th&gt;Resultado&lt;/th&gt;
  &lt;/tr&gt;
 &lt;tbody&gt;
  &lt;tr&gt;
   &lt;td&gt;
    &lt;pre class="no_mrkdwn"&gt;
h1. Documento de ejemplo

Lorem ipsum "dolor sit amet":#mark, consectetur adipiscing elit. Curabitur eget ante nunc. Pellentesque a tortor ipsum, id rhoncus orci. Quisque leo sapien, rutrum id convallis id, rutrum in ligula. Vestibulum **semper adipiscing leo** et blandit.

Sed nibh quam, hendrerit _sit amet aliquam_ vel, pulvinar molestie augue.

bq. Integer cursus, nunc eu ultrices pellentesque, eros leo malesuada turpis, vel convallis neque dolor a nunc. Sed lacus risus, condimentum vitae posuere quis, ultrices pharetra nunc.

Lenguajes de marcado ligero

* **Markdown**
* Textile
* reStructuredText
* Texy!
* Txt2tags
* Marcado Wiki
## Creole
## MediaWiki

!pictures/no_wysiwyg.png (avatar)!

h3. Cabecera H3

Morbi erat augue, feugiat eu pellentesque eget, hendrerit quis lectus. Fusce dignissim pretium nibh sed dignissim. Pellentesque lobortis ante eu dui fermentum vitae blandit risus aliquet.

|_. @@|_. solo texto|_. HTML Limpio|
|Markdown|Si|Si|
|Editor WYSISWG|X|A veces|

_Ejemplo de código_

bc.. import lifetime

for each_day in lifetime.days():
    carpe_diem()

p. Suspendisse posuere velit et velit VEHICULA(automobila) at scelerisque orci suscipit. Nulla facilisis lorem eu sem viverra varius nec ut felis.

Esto es un texto con nota al pie[1] y esta es otra nota[2]

fn1. Esto es una nota al pie.

fn2. Esto es la segunda nota.
    &lt;/pre&gt;
   &lt;/td&gt;
   &lt;td&gt;
&lt;h1&gt;Documento de ejemplo&lt;/h1&gt;

&lt;p&gt;Lorem ipsum &lt;a href="#mark"&gt;dolor sit amet&lt;/a&gt;, consectetur adipiscing elit. Curabitur eget ante nunc. Pellentesque a tortor ipsum, id rhoncus orci. Quisque leo sapien, rutrum id convallis id, rutrum in ligula. Vestibulum &lt;b&gt;semper adipiscing leo&lt;/b&gt; et blandit.&lt;/p&gt;

&lt;p&gt;Sed nibh quam, hendrerit &lt;em&gt;sit amet aliquam&lt;/em&gt; vel, pulvinar molestie augue.&lt;/p&gt;

&lt;blockquote&gt;
    &lt;p&gt;Integer cursus, nunc eu ultrices pellentesque, eros leo malesuada turpis, vel convallis neque dolor a nunc. Sed lacus risus, condimentum vitae posuere quis, ultrices pharetra nunc.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Lenguajes de marcado ligero &lt;/p&gt;

&lt;ul&gt;
    &lt;li&gt;&lt;b&gt;Markdown&lt;/b&gt;&lt;/li&gt;
    &lt;li&gt;Textile&lt;/li&gt;
    &lt;li&gt;reStructuredText&lt;/li&gt;
    &lt;li&gt;Texy!&lt;/li&gt;
    &lt;li&gt;Txt2tags&lt;/li&gt;
    &lt;li&gt;Marcado Wiki
&lt;ol&gt;
    &lt;li&gt;Creole&lt;/li&gt;
    &lt;li&gt;MediaWiki&lt;/li&gt;
&lt;/ol&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;img src="pictures/no_wysiwyg.png" title="avatar" alt="avatar" /&gt;&lt;/p&gt;

&lt;h3&gt;Cabecera H3&lt;/h3&gt;

&lt;p&gt;Morbi erat augue, feugiat eu pellentesque eget, hendrerit quis lectus. Fusce dignissim pretium nibh sed dignissim. Pellentesque lobortis ante eu dui fermentum vitae blandit risus aliquet.&lt;/p&gt;

&lt;table&gt;
    &lt;tr&gt;
        &lt;th&gt;&lt;/th&gt;
        &lt;th&gt;solo texto&lt;/th&gt;
        &lt;th&gt;&lt;span class="caps"&gt;&lt;span class="caps"&gt;HTML&lt;/span&gt;&lt;/span&gt; Limpio&lt;/th&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
        &lt;td&gt;Markdown&lt;/td&gt;
        &lt;td&gt;Si&lt;/td&gt;
        &lt;td&gt;Si&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
        &lt;td&gt;Editor &lt;span class="caps"&gt;&lt;span class="caps"&gt;WYSISWG&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
        &lt;td&gt;X&lt;/td&gt;
        &lt;td&gt;A veces&lt;/td&gt;
    &lt;/tr&gt;
&lt;/table&gt;

&lt;p&gt;&lt;em&gt;Ejemplo de código&lt;/em&gt;&lt;/p&gt;

&lt;pre&gt;
import lifetime

for each_day in lifetime.days():
    carpe_diem()
&lt;/pre&gt;

&lt;p&gt;Suspendisse posuere velit et velit &lt;acronym title="automobila"&gt;&lt;span class="caps"&gt;VEHICULA&lt;/span&gt;&lt;/acronym&gt; at scelerisque orci suscipit. Nulla facilisis lorem eu sem viverra varius nec ut felis.&lt;/p&gt;

&lt;p&gt;Esto es un texto con nota al pie&lt;sup class="footnote"&gt;&lt;a href="#fn8701048694d94660b0d4d4"&gt;1&lt;/a&gt;&lt;/sup&gt; y esta es otra nota&lt;sup class="footnote"&gt;&lt;a href="#fn13831162204d94660b122f5"&gt;2&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;

&lt;p id="fn8701048694d94660b0d4d4" class="footnote"&gt;&lt;sup&gt;1&lt;/sup&gt; Esto es una nota al pie.&lt;/p&gt;

&lt;p id="fn13831162204d94660b122f5" class="footnote"&gt;&lt;sup&gt;2&lt;/sup&gt; Esto es la segunda nota.&lt;/p&gt;

&lt;p&gt;&lt;/td&gt;
  &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h2 id="bbcode"&gt;BBCode&lt;/h2&gt;
&lt;p&gt;Es uno de los más extendido porque es ampliamente usado en foros por toda la red. 
Nació para ser empleado en foros y es prácticamente el único ámbito en el que se 
emplea. Es también muy limitado porque no soporta muchas de las características 
de los otros lenguajes y además hay múltiples variantes que no ayudan a crear un 
estándar. Por ejemplo las listas y las tablas no son contempladas en algunas de 
esas variantes.&lt;/p&gt;
&lt;table&gt;
 &lt;thead&gt;
  &lt;tr&gt;
   &lt;th style="width: 50%;"&gt;BBCode&lt;/th&gt;&lt;th&gt;Resultado&lt;/th&gt;
  &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
  &lt;tr&gt;
   &lt;td&gt;
    &lt;pre class="no_mrkdwn"&gt;
Documento de ejemplo

Lorem ipsum [url=http://joedicastro.com]dolor sit amet[/url], consectetur adipiscing elit. Curabitur eget ante nunc. Pellentesque a tortor ipsum, id rhoncus orci. Quisque leo sapien, rutrum id convallis id, rutrum in ligula. Vestibulum [b]semper adipiscing leo[/b] et blandit.

Sed nibh quam, hendrerit [i]sit amet aliquam[/i] vel, pulvinar molestie augue.

[quote]Integer cursus, nunc eu ultrices pellentesque, eros leo malesuada turpis, vel convallis neque dolor a nunc. Sed lacus risus, condimentum vitae posuere quis, ultrices pharetra nunc.[/quote]

[img]http://joedicastro/files/
imagenes/no_wysiwyg.png[/img]

Cabecera H3

Morbi erat augue, feugiat eu pellentesque eget, hendrerit quis lectus. Fusce dignissim pretium nibh sed dignissim. Pellentesque lobortis ante eu dui fermentum vitae blandit risus aliquet.

[i]Ejemplo de codigo[/i]

[code]
import lifetime

for each_day in lifetime.days():
    carpe_diem()
[/code]
    &lt;/pre&gt;
   &lt;/td&gt;
   &lt;td&gt;&lt;div&gt;
&lt;p&gt;
Documento de ejemplo&lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;
Lorem ipsum &lt;a href="http://joedicastro.com" target="_new"&gt;dolor sit amet&lt;/a&gt;, consectetur adipiscing elit. Curabitur eget ante nunc. Pellentesque a tortor ipsum, id rhoncus orci. Quisque leo sapien, rutrum id convallis id, rutrum in ligula. Vestibulum &lt;strong&gt;semper adipiscing leo&lt;/strong&gt; et blandit.
&lt;/p&gt;
&lt;p&gt;
Sed nibh quam, hendrerit &lt;em&gt;sit amet aliquam&lt;/em&gt; vel, pulvinar molestie augue.
&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;
Integer cursus, nunc eu ultrices pellentesque, eros leo malesuada turpis, vel convallis neque dolor a nunc. Sed lacus risus, condimentum vitae posuere quis, ultrices pharetra nunc.
&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;img src="pictures/no_wysiwyg.png" alt="" /&gt;&lt;/p&gt;
&lt;p&gt;Cabecera H3&lt;/p&gt;
&lt;p&gt;Morbi erat augue, feugiat eu pellentesque eget, hendrerit quis lectus. Fusce dignissim pretium nibh sed dignissim. Pellentesque lobortis ante eu dui fermentum vitae blandit risus aliquet.&lt;/p&gt;
&lt;em&gt;Ejemplo de codigo&lt;/em&gt;
&lt;pre class="code"&gt;
import lifetime

for each_day in lifetime.days():
carpe_diem()
&lt;/pre&gt;&lt;/div&gt;
   &lt;/td&gt;
  &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;

&lt;hr /&gt;
&lt;h2 id="restructuredtext"&gt;reStructuredText&lt;/h2&gt;
&lt;p&gt;Fue creado para crear documentación, en concreto documentación para lenguajes de 
programación como Python. Tiene algunas carencias al no estar orientado a HTML, 
pero también tiene posibilidades de las que carece Markdown. Es muy potente, 
bastante legible, pero un poco incomodo para según que cosas, a mi modo de ver. &lt;/p&gt;
&lt;table&gt;
 &lt;thead&gt;
  &lt;tr&gt;
   &lt;th style="width: 53%;"&gt;reStructuredText&lt;/th&gt;&lt;th&gt;Resultado&lt;/th&gt;
  &lt;/tr&gt;
 &lt;tbody&gt;
  &lt;tr&gt;
   &lt;td&gt;
    &lt;pre class="no_mrkdwn"&gt;
Documento de ejemplo
====================

Lorem ipsum `dolor sit amet &lt;#mark&gt;`_, consectetur adipiscing elit. Curabitur eget ante nunc. Pellentesque a tortor ipsum, id rhoncus orci. Quisque leo sapien, rutrum id convallis id, rutrum in ligula. Vestibulum **semper adipiscing leo** et blandit.

Sed nibh quam, hendrerit *sit amet aliquam* vel, pulvinar molestie augue.

Integer cursus, nunc eu ultrices pellentesque, eros leo malesuada turpis, vel convallis neque dolor a nunc. Sed lacus risus, condimentum vitae posuere quis, ultrices pharetra nunc.

Lenguajes de marcado ligero

&amp;nbsp;* **Markdown**
&amp;nbsp;* Textile
&amp;nbsp;* reStructuredText
&amp;nbsp;* Texy!
&amp;nbsp;* Txt2tags
&amp;nbsp;* Marcado Wiki

&amp;nbsp;&amp;nbsp;&amp;nbsp;1. Creole
&amp;nbsp;&amp;nbsp;&amp;nbsp;2. MediaWiki

.. image:: pictures/no_wysiwyg.png
   :alt: avatar

---------------

Cabecera H3
^^^^^^^^^^^

Morbi erat augue, feugiat eu pellentesque eget, hendrerit quis lectus. Fusce dignissim pretium nibh sed dignissim. Pellentesque lobortis ante eu dui fermentum vitae blandit risus aliquet.

============== ========== ===========
\              solo texto HTML Limpio
============== ========== ===========
Markdown           Si         Si      
Editor WYSISWG     X        A veces 
============== ========== ===========

*Ejemplo de código*::

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;import lifetime

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;for each_day in lifetime.days():
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;carpe_diem()

Suspendisse posuere velit et velit vehicula at scelerisque orci suscipit. Nulla facilisis lorem eu sem viverra varius nec ut felis.

Esto es un texto con nota al pie [1]_ y esta es otra nota [2]_

.. [1] Esto es una nota al pie.
.. [2] Esto es la segunda nota.
    &lt;/pre&gt;
   &lt;/td&gt;
   &lt;td&gt;
&lt;h1 id="documento-de-ejemplo"&gt;Documento de ejemplo&lt;/h1&gt;
&lt;p&gt;Lorem ipsum &lt;a href="#mark"&gt;dolor sit amet&lt;/a&gt;, consectetur adipiscing elit. Curabitur eget ante nunc. Pellentesque a tortor ipsum, id rhoncus orci. Quisque leo sapien, rutrum id convallis id, rutrum in ligula. Vestibulum &lt;strong&gt;semper adipiscing leo&lt;/strong&gt; et blandit.&lt;/p&gt;
&lt;p&gt;Sed nibh quam, hendrerit &lt;em&gt;sit amet aliquam&lt;/em&gt; vel, pulvinar molestie augue.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Integer cursus, nunc eu ultrices pellentesque, eros leo malesuada turpis, vel convallis neque dolor a nunc. Sed lacus risus, condimentum vitae posuere quis, ultrices pharetra nunc.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Lenguajes de marcado ligero&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Markdown&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;Textile&lt;/li&gt;
&lt;li&gt;reStructuredText&lt;/li&gt;
&lt;li&gt;Texy!&lt;/li&gt;
&lt;li&gt;Txt2tags&lt;/li&gt;
&lt;li&gt;Marcado Wiki
&lt;ol style="list-style-type: decimal"&gt;
&lt;li&gt;Creole&lt;/li&gt;
&lt;li&gt;MediaWiki&lt;/li&gt;
&lt;/ol&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;p /&gt;
&lt;img src="pictures/no_wysiwyg.png" alt="avatar" /&gt;
&lt;hr /&gt;
&lt;h2 id="cabecera-h3"&gt;Cabecera H3&lt;/h2&gt;
&lt;p&gt;Morbi erat augue, feugiat eu pellentesque eget, hendrerit quis lectus. Fusce dignissim pretium nibh sed dignissim. Pellentesque lobortis ante eu dui fermentum vitae blandit risus aliquet.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr class="header"&gt;
&lt;th align="left"&gt; &lt;/th&gt;
&lt;th align="left"&gt;solo texto&lt;/th&gt;
&lt;th align="left"&gt;HTML Limpio&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr class="odd"&gt;
&lt;td align="left"&gt;Markdown&lt;/td&gt;
&lt;td align="left"&gt;Si&lt;/td&gt;
&lt;td align="left"&gt;Si&lt;/td&gt;
&lt;/tr&gt;
&lt;tr class="even"&gt;
&lt;td align="left"&gt;Editor WYSISWG&lt;/td&gt;
&lt;td align="left"&gt;X&lt;/td&gt;
&lt;td align="left"&gt;A veces&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;em&gt;Ejemplo de código&lt;/em&gt;:&lt;/p&gt;
&lt;pre&gt;import lifetime

&lt;p&gt;for each_day in lifetime.days():
    carpe_diem()
&lt;/pre&gt;
&lt;p&gt;Suspendisse posuere velit et velit vehicula at scelerisque orci suscipit. Nulla facilisis lorem eu sem viverra varius nec ut felis.&lt;/p&gt;
&lt;p&gt;Esto es un texto con nota al pie &lt;sup&gt;&lt;a href="#fn1" class="footnoteRef" id="fnref1"&gt;1&lt;/a&gt;&lt;/sup&gt; y esta es otra nota &lt;sup&gt;&lt;a href="#fn2" class="footnoteRef" id="fnref2"&gt;2&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;div class="footnotes"&gt;
&lt;hr /&gt;
&lt;ol&gt;
&lt;li id="fn1"&gt;&lt;p&gt;Esto es una nota al pie. &lt;a href="#fnref1" class="footnoteBackLink" title="Jump back to footnote 1"&gt;↩&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li id="fn2"&gt;&lt;p&gt;Esto es la segunda nota. &lt;a href="#fnref2" class="footnoteBackLink" title="Jump back to footnote 2"&gt;↩&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
   &lt;/td&gt;
  &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h2 id="texy"&gt;Texy!&lt;/h2&gt;
&lt;p&gt;Fue específicamente diseñado para crear documentos XHTML, por ello es bastante 
completo, pero no está muy extendido. Es legible, pero también tiene carece de 
soporte para ciertos tags de HTML.&lt;/p&gt;
&lt;table&gt;
 &lt;thead&gt;
  &lt;tr&gt;
   &lt;th style="width: 53%;"&gt;Texy!&lt;/th&gt;&lt;th&gt;Resultado&lt;/th&gt;
  &lt;/tr&gt;
 &lt;tbody&gt;
  &lt;tr&gt;
   &lt;td&gt;
    &lt;pre class="no_mrkdwn"&gt;
Documento de ejemplo
====================

Lorem ipsum [dolor sit amet | http://joedicastro.com], consectetur adipiscing elit. Curabitur eget ante nunc. Pellentesque a tortor ipsum, id rhoncus orci. Quisque leo sapien, rutrum id convallis id, rutrum in ligula. Vestibulum **semper adipiscing leo** et blandit.

Sed nibh quam, hendrerit *sit amet aliquam* vel, pulvinar molestie augue.

&gt; Integer cursus, nunc eu ultrices pellentesque, eros leo malesuada turpis, vel convallis neque dolor a nunc. Sed lacus risus, condimentum vitae posuere quis, ultrices pharetra nunc.

Lenguajes de marcado ligero

- **Markdown**
- Textile
- reStructuredText
- Texy!
- Txt2tags
- Marcado Wiki
 1) Creole
 2) MediaWiki

[* pictures/no_wysiwyg.png .(alt text)[avatar] *]

### Cabecera H3 ###

-----

Morbi erat augue, feugiat eu pellentesque eget, hendrerit quis lectus. Fusce dignissim pretium nibh sed dignissim. Pellentesque lobortis ante eu dui fermentum vitae blandit risus aliquet.

|------------------------------
|   | solo texto | HTML Limpio  
|------------------------------
| Markdown       | Si | Si      
| Editor WYSISWG | X  | A veces

*Ejemplo de código*

/---code
&amp;nbsp;&amp;nbsp;import lifetime

&amp;nbsp;&amp;nbsp;for each_day in lifetime.days():
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;carpe_diem()
\---

Suspendisse posuere velit et velit "vehicula"((automobila)) at scelerisque orci suscipit. Nulla facilisis lorem eu sem viverra varius nec ut felis.
    &lt;/pre&gt;
   &lt;/td&gt;
   &lt;td&gt;

&lt;h1 id="toc-documento-de-ejemplo"&gt;Documento de ejemplo&lt;/h1&gt;

&lt;p&gt;Lorem ipsum &lt;a href="http://joedicastro.com"&gt;dolor sit amet&lt;/a&gt;, consectetur
adipiscing elit. Curabitur eget ante nunc. Pellentesque a tortor ipsum, id
rhoncus orci. Quisque leo sapien, rutrum id convallis id, rutrum in ligula.
Vestibulum &lt;strong&gt;semper adipiscing leo&lt;/strong&gt; et blandit.&lt;/p&gt;

&lt;p&gt;Sed nibh quam, hendrerit &lt;em&gt;sit amet aliquam&lt;/em&gt; vel, pulvinar
molestie&amp;nbsp;augue.&lt;/p&gt;

&lt;blockquote&gt;
    &lt;p&gt;Integer cursus, nunc eu ultrices pellentesque, eros leo malesuada turpis, vel
    convallis neque dolor a nunc. Sed lacus risus, condimentum vitae posuere quis,
    ultrices pharetra&amp;nbsp;nunc.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Lenguajes de marcado ligero&lt;/p&gt;

&lt;ul&gt;
    &lt;li&gt;&lt;strong&gt;Markdown&lt;/strong&gt;&lt;/li&gt;

&lt;li&gt;Textile&lt;/li&gt;

&lt;li&gt;reStructuredText&lt;/li&gt;

&lt;li&gt;Texy!&lt;/li&gt;

&lt;li&gt;Txt2tags&lt;/li&gt;

&lt;li&gt;Marcado Wiki
        &lt;ol&gt;
            &lt;li&gt;Creole&lt;/li&gt;

&lt;li&gt;MediaWiki&lt;/li&gt;
        &lt;/ol&gt;
    &lt;/li&gt;
&lt;/ul&gt;

&lt;div&gt;&lt;img src="pictures/no_wysiwyg.png" class="avatar"
alt="alt text"&gt;&lt;/div&gt;

&lt;h1 id="toc-cabecera-h3"&gt;Cabecera H3&lt;/h1&gt;

&lt;hr&gt;

&lt;p&gt;Morbi erat augue, feugiat eu pellentesque eget, hendrerit quis lectus. Fusce
dignissim pretium nibh sed dignissim. Pellentesque lobortis ante eu dui
fermentum vitae blandit risus aliquet.&lt;/p&gt;

&lt;table&gt;
    &lt;thead&gt;
        &lt;tr&gt;
            &lt;th&gt;&amp;nbsp;&lt;/th&gt;

&lt;th&gt;solo texto&lt;/th&gt;

&lt;th&gt;HTML Limpio&lt;/th&gt;
        &lt;/tr&gt;
    &lt;/thead&gt;

&lt;tbody&gt;
        &lt;tr&gt;
            &lt;td&gt;Markdown&lt;/td&gt;

&lt;td&gt;Si&lt;/td&gt;

&lt;td&gt;Si&lt;/td&gt;
        &lt;/tr&gt;

&lt;tr&gt;
            &lt;td&gt;Editor WYSISWG&lt;/td&gt;

&lt;td&gt;X&lt;/td&gt;

&lt;td&gt;A&amp;nbsp;veces&lt;/td&gt;
        &lt;/tr&gt;
    &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;&lt;em&gt;Ejemplo de código&lt;/em&gt;&lt;/p&gt;

&lt;pre&gt;import lifetime

for each_day in lifetime.days():
    carpe_diem()&lt;/pre&gt;

&lt;p&gt;Suspendisse posuere velit et velit &lt;acronym
title="automobila"&gt;vehicula&lt;/acronym&gt; at scelerisque orci suscipit. Nulla
facilisis lorem eu sem viverra varius nec ut&amp;nbsp;felis.&lt;/p&gt;

&lt;p&gt;&lt;/td&gt;
  &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h2 id="txt2tags"&gt;txt2tags&lt;/h2&gt;
&lt;p&gt;Está escrito en Python y es muy potente, al igual que RestructuredText, 
permitiendo macros. Permite la salida en muchos formatos, incluido el XHTML. 
Es muy legible y muy fácil de emplear, es una pena que no esté más extendido y 
soportado. Aunque aún tiene algunas carencias como las notas al pie o las 
abreviaturas, que pueden ser soportadas con macros, también tiene un desarrollo 
muy activo. En la futura versión 3.0 serán soportados directamente las notas al 
pie. Es una alternativa con muy buen futuro.&lt;/p&gt;
&lt;table&gt;
 &lt;thead&gt;
  &lt;tr&gt;
   &lt;th style="width: 53%;"&gt;txt2tags&lt;/th&gt;&lt;th&gt;Resultado&lt;/th&gt;
  &lt;/tr&gt;
 &lt;tbody&gt;
  &lt;tr&gt;
   &lt;td&gt;
    &lt;pre class="no_mrkdwn"&gt;
=Documento de ejemplo=

Lorem ipsum [dolor sit amet #mark], consectetur adipiscing elit. Curabitur eget ante nunc. Pellentesque a tortor ipsum, id rhoncus orci. Quisque leo sapien, rutrum id convallis id, rutrum in ligula. Vestibulum **semper adipiscing leo** et blandit.

Sed nibh quam, hendrerit //sit amet aliquam// vel, pulvinar molestie augue.

&lt;tab&gt;Integer cursus, nunc eu ultrices pellentesque, eros leo malesuada turpis, vel convallis neque dolor a nunc. Sed lacus risus, condimentum vitae posuere quis, ultrices pharetra nunc.

Lenguajes de marcado ligero

- **Markdown**
 - Textile
 - reStructuredText
 - Texy!
 - Txt2tags
 - Marcado Wiki
  + Creole
  + MediaWiki

[pictures/no_wysiwyg.png]

===Cabecera H3===

--------------------

Morbi erat augue, feugiat eu pellentesque eget, hendrerit quis lectus. Fusce dignissim pretium nibh sed dignissim. Pellentesque lobortis ante eu dui fermentum vitae blandit risus aliquet.

||   | solo texto | HTML Limpio
| Markdown       |  Si  |  Si  |
| Editor WYSISWG |  X  |  A veces  |

//Ejemplo de codigo//

```
import lifetime

for each_day in lifetime.days():
    carpe_diem()
```
    &lt;/pre&gt;
   &lt;/td&gt;
   &lt;td&gt;
&lt;h1&gt;Documento de ejemplo&lt;/h1&gt;

&lt;p&gt;
Lorem ipsum &lt;a href="#mark"&gt;dolor sit amet&lt;/a&gt;, consectetur adipiscing elit. Curabitur eget ante nunc. Pellentesque a tortor ipsum, id rhoncus orci. Quisque leo sapien, rutrum id convallis id, rutrum in ligula. Vestibulum &lt;b&gt;semper adipiscing leo&lt;/b&gt; et blandit.
&lt;/p&gt;
&lt;p&gt;
Sed nibh quam, hendrerit &lt;i&gt;sit amet aliquam&lt;/i&gt; vel, pulvinar molestie augue.
&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;
Integer cursus, nunc eu ultrices pellentesque, eros leo malesuada turpis, vel convallis neque dolor a nunc. Sed lacus risus, condimentum vitae posuere quis, ultrices pharetra nunc.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;
Lenguajes de marcado ligero 
&lt;/p&gt;

&lt;ul&gt;
 &lt;li&gt;&lt;b&gt;Markdown&lt;/b&gt;
 &lt;li&gt;Textile
 &lt;li&gt;reStructuredText
 &lt;li&gt;Texy!
 &lt;li&gt;Txt2tags
 &lt;li&gt;Marcado Wiki
  &lt;ol&gt;
  &lt;li&gt;Creole
  &lt;li&gt;MediaWiki
  &lt;/ol&gt;
 &lt;/ul&gt;

&lt;p&gt;
&lt;img src="pictures/no_wysiwyg.png" alt=""&gt;
&lt;/p&gt;

&lt;h3&gt;Cabecera H3&lt;/h3&gt;

&lt;hr /&gt;

&lt;p&gt;
Morbi erat augue, feugiat eu pellentesque eget, hendrerit quis lectus. Fusce dignissim pretium nibh sed dignissim. Pellentesque lobortis ante eu dui fermentum vitae blandit risus aliquet.
&lt;/p&gt;

&lt;table cellpadding="4"&gt;
  &lt;tr&gt;
    &lt;th&gt;&lt;/th&gt;
    &lt;th&gt;solo texto&lt;/th&gt;
    &lt;th&gt;HTML Limpio&lt;/th&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;Markdown&lt;/td&gt;
    &lt;td align="center"&gt;Si&lt;/td&gt;
    &lt;td align="center"&gt;Si&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;Editor WYSISWG&lt;/td&gt;
    &lt;td align="center"&gt;X&lt;/td&gt;
    &lt;td align="center"&gt;A veces&lt;/td&gt;
  &lt;/tr&gt;
&lt;/table&gt;

&lt;p&gt;
 &lt;i&gt;Ejemplo de codigo&lt;/i&gt;
&lt;/p&gt;

&lt;pre&gt;
import lifetime

for each_day in lifetime.days():
    carpe_diem()
&lt;/pre&gt;
   &lt;/td&gt;
  &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;

&lt;hr /&gt;
&lt;h2 id="creole"&gt;Creole&lt;/h2&gt;
&lt;p&gt;Creado a partir de los lenguajes más empleados en los Wikis y usado 
fundamentalmente en Wikis, por lo que también tiene ciertas carencias.&lt;/p&gt;
&lt;table&gt;
 &lt;thead&gt;
  &lt;tr&gt;
   &lt;th style="width: 50%;"&gt;Creole&lt;/th&gt;&lt;th&gt;Resultado&lt;/th&gt;
  &lt;/tr&gt;
 &lt;tbody&gt;
  &lt;tr&gt;
   &lt;td&gt;
    &lt;pre class="no_mrkdwn"&gt;
== Documento de ejemplo ==

Lorem ipsum [[#mark|dolor sit amet]], consectetur adipiscing elit. Curabitur eget ante nunc. Pellentesque a tortor ipsum, id rhoncus orci. Quisque leo sapien, rutrum id convallis id, rutrum in ligula. Vestibulum **semper adipiscing leo** et blandit.

Sed nibh quam, hendrerit //sit amet aliquam// vel, pulvinar molestie augue.

&gt; Integer cursus, nunc eu ultrices pellentesque, eros leo malesuada turpis, vel convallis neque dolor a nunc. Sed lacus risus, condimentum vitae posuere quis, ultrices pharetra nunc.

Lenguajes de marcado ligero

* **Markdown**
 * Textile
 * reStructuredText
 * Texy!
 * Txt2tags
 * Marcado Wiki
 ## Creole
 ## MediaWiki

{{pictures/no_wysiwyg.png|avatar}}

==== Cabecera H3 ====

-----

Morbi erat augue, feugiat eu pellentesque eget, hendrerit quis lectus. Fusce dignissim pretium nibh sed dignissim. Pellentesque lobortis ante eu dui fermentum vitae blandit risus aliquet.

|= |= solo texto |= HTML Limpio |
| Markdown       | Si | Si      |
| Editor WYSISWG | X  | A veces |

//Ejemplo de código//
{{{
import lifetime

for each_day in lifetime.days():
    carpe_diem()
}}}
    &lt;/pre&gt;
   &lt;/td&gt;
   &lt;td&gt;
&lt;h1&gt;Documento de ejemplo&lt;/h1&gt;

&lt;p&gt;Lorem ipsum &lt;a href="#mark"&gt;dolor sit amet&lt;/a&gt;, consectetur adipiscing elit. Curabitur eget ante nunc. Pellentesque a tortor ipsum, id rhoncus orci. Quisque leo sapien, rutrum id convallis id, rutrum in ligula. Vestibulum &lt;strong&gt;semper adipiscing leo&lt;/strong&gt; et blandit.&lt;/p&gt;
&lt;p&gt;Sed nibh quam, hendrerit &lt;em&gt;sit amet aliquam&lt;/em&gt; vel, pulvinar molestie augue.&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;Integer cursus, nunc eu ultrices pellentesque, eros leo malesuada turpis, vel convallis neque dolor a nunc. Sed lacus risus, condimentum vitae posuere quis, ultrices pharetra nunc.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Lenguajes de marcado ligero &lt;/p&gt;

&lt;ul&gt;&lt;li&gt;&lt;strong&gt;Markdown&lt;/strong&gt;
&lt;/li&gt;&lt;li&gt;Textile
&lt;/li&gt;&lt;li&gt;reStructuredText
&lt;/li&gt;&lt;li&gt;Texy!
&lt;/li&gt;&lt;li&gt;Txt2tags
&lt;/li&gt;&lt;li&gt;Marcado Wiki
&lt;ol&gt;&lt;li&gt;Creole
&lt;/li&gt;&lt;li&gt;MediaWiki
&lt;/li&gt;&lt;/ol&gt;&lt;/li&gt;&lt;/ul&gt;
&lt;p&gt;&lt;img src="pictures/no_wysiwyg.png" alt="avatar" title="avatar" /&gt;&lt;/p&gt;
&lt;h3&gt;Cabecera H3&lt;/h3&gt;
&lt;p&gt;-----&lt;/p&gt;
&lt;p&gt;Morbi erat augue, feugiat eu pellentesque eget, hendrerit quis lectus. Fusce dignissim pretium nibh sed dignissim. Pellentesque lobortis ante eu dui fermentum vitae blandit risus aliquet.&lt;/p&gt;

&lt;table&gt;&lt;tr&gt;&lt;th&gt;&lt;/th&gt;&lt;th&gt;solo texto&lt;/th&gt;&lt;th&gt;HTML Limpio&lt;/th&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Markdown&lt;/td&gt;&lt;td&gt;Si&lt;/td&gt;&lt;td&gt;Si&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Editor WYSISWG&lt;/td&gt;&lt;td&gt;X&lt;/td&gt;&lt;td&gt;A veces&lt;/td&gt;&lt;/tr&gt;
&lt;/table&gt;
&lt;p&gt;&lt;em&gt;Ejemplo de código&lt;/em&gt;&lt;/p&gt;
&lt;pre&gt;
import lifetime

&lt;p&gt;for each_day in lifetime.days():
    carpe_diem()
&lt;/pre&gt;&lt;/p&gt;
&lt;p&gt;&lt;/td&gt;
  &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h2 id="mediawiki"&gt;MediaWiki&lt;/h2&gt;
&lt;p&gt;Quizás el más extendido, no en vano MediaWiki es el Wiki empleado por la 
Wikipedia. Igual que Creole, tiene ciertas limitaciones que suple con plugins y 
etiquetas HTML. No me acaba de gustar. La manera que tiene de crear tablas - 
por ejemplo - aunque potente, no me parece nada legible en texto plano.&lt;br /&gt;
&lt;/p&gt;
&lt;table&gt;
 &lt;thead&gt;
  &lt;tr&gt;
   &lt;th style="width: 53%;"&gt;MediaWiki&lt;/th&gt;&lt;th&gt;Resultado&lt;/th&gt;
  &lt;/tr&gt;
 &lt;tbody&gt;
  &lt;tr&gt;
   &lt;td&gt;
    &lt;pre class="no_mrkdwn"&gt;
==Documento de ejemplo==

Lorem ipsum [http://joedicastro.com dolor sit amet], consectetur adipiscing elit. Curabitur eget ante nunc. Pellentesque a tortor ipsum, id rhoncus orci. Quisque leo sapien, rutrum id convallis id, rutrum in ligula. Vestibulum '''semper adipiscing leo''' et blandit.

Sed nibh quam, hendrerit ''sit amet aliquam'' vel, pulvinar molestie augue.

&lt;blockquote&gt;Integer cursus, nunc eu ultrices pellentesque, eros leo malesuada turpis, vel convallis neque dolor a nunc. Sed lacus risus, condimentum vitae posuere quis, ultrices pharetra nunc.&lt;/blockquote&gt;

Lenguajes de marcado ligero

* '''Markdown'''
* Textile
* reStructuredText
* Texy!
* Txt2tags
* Marcado Wiki
*# Creole
*# MediaWiki

[[File:pictures/no_wysiwyg.png|caption]]

=== Cabecera H3 ===

----

Morbi erat augue, feugiat eu pellentesque eget, hendrerit quis lectus. Fusce dignissim pretium nibh sed dignissim. Pellentesque lobortis ante eu dui fermentum vitae blandit risus aliquet.

{| 
! 
! solo texto
! HTML Limpio
|-
|Markdown
|Si
|Si
|-
|Editor WYSISWG
|X
|A veces
|}

''Ejemplo de código''

&amp;nbsp;&amp;nbsp;import lifetime
&amp;nbsp;&amp;nbsp; 
&amp;nbsp;&amp;nbsp;for each_day in lifetime.days():
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;carpe_diem()

Suspendisse posuere velit et velit &lt;span title="automobila"&gt;vehicula&lt;/span&gt; at scelerisque orci suscipit. Nulla facilisis lorem eu sem viverra varius nec ut felis.

Esto es un texto con nota al pie &lt;ref name="ejemplo"&gt;Esto es una nota al pie.&lt;/ref&gt; y esta es otra nota &lt;ref name="segunda"&gt; Esto es la segunda nota.&lt;/ref&gt;

{{reflist}}
    &lt;/pre&gt;
   &lt;/td&gt;
   &lt;td&gt;
&lt;h2&gt;&lt;span class="mw-headline" id="Documento_de_ejemplo"&gt;Documento de ejemplo&lt;/span&gt;&lt;/h2&gt;
&lt;p&gt;Lorem ipsum &lt;a href="http://joedicastro.com" class="external text" rel="nofollow"&gt;dolor sit amet&lt;/a&gt;, consectetur adipiscing elit. Curabitur eget ante nunc. Pellentesque a tortor ipsum, id rhoncus orci. Quisque leo sapien, rutrum id convallis id, rutrum in ligula. Vestibulum &lt;b&gt;semper adipiscing leo&lt;/b&gt; et blandit.&lt;/p&gt;

&lt;p&gt;Sed nibh quam, hendrerit &lt;i&gt;sit amet aliquam&lt;/i&gt; vel, pulvinar molestie augue.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Integer cursus, nunc eu ultrices pellentesque, eros leo malesuada turpis, vel convallis neque dolor a nunc. Sed lacus risus, condimentum vitae posuere quis, ultrices pharetra nunc.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Lenguajes de marcado ligero&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;b&gt;Markdown&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;Textile&lt;/li&gt;
&lt;li&gt;reStructuredText&lt;/li&gt;

&lt;li&gt;Texy!&lt;/li&gt;
&lt;li&gt;Txt2tags&lt;/li&gt;
&lt;li&gt;Marcado Wiki
&lt;ol&gt;
&lt;li&gt;Creole&lt;/li&gt;
&lt;li&gt;MediaWiki&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src="pictures/no_wysiwyg.png" alt="" /&gt;&lt;/p&gt;
&lt;h3&gt;&lt;span class="mw-headline" id="Cabecera_H3"&gt;Cabecera H3&lt;/span&gt;&lt;/h3&gt;

&lt;hr /&gt;
&lt;p&gt;Morbi erat augue, feugiat eu pellentesque eget, hendrerit quis lectus. Fusce dignissim pretium nibh sed dignissim. Pellentesque lobortis ante eu dui fermentum vitae blandit risus aliquet.&lt;/p&gt;
&lt;table&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;solo texto&lt;/th&gt;
&lt;th&gt;HTML Limpio&lt;/th&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Markdown&lt;/td&gt;
&lt;td&gt;Si&lt;/td&gt;
&lt;td&gt;Si&lt;/td&gt;

&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Editor WYSISWG&lt;/td&gt;
&lt;td&gt;X&lt;/td&gt;
&lt;td&gt;A veces&lt;/td&gt;
&lt;/tr&gt;
&lt;/table&gt;
&lt;p&gt;&lt;i&gt;Ejemplo de código&lt;/i&gt;&lt;/p&gt;
&lt;pre&gt;
import lifetime

&lt;p&gt;for each_day in lifetime.days():
    carpe_diem()&lt;/p&gt;
&lt;p&gt;&lt;/pre&gt;
&lt;p&gt;Suspendisse posuere velit et velit &lt;span title="automobila"&gt;vehicula&lt;/span&gt; at scelerisque orci suscipit. Nulla facilisis lorem eu sem viverra varius nec ut felis.&lt;/p&gt;&lt;/p&gt;
&lt;p&gt;Esto es un texto con nota al pie &lt;sup id="cite_ref-ejemplo_0-0" class="reference"&gt;&lt;a href="#cite_note-ejemplo-0"&gt;&lt;span&gt;[&lt;/span&gt;1&lt;span&gt;]&lt;/span&gt;&lt;/a&gt;&lt;/sup&gt; y esta es otra nota &lt;sup id="cite_ref-segunda_1-0" class="reference"&gt;&lt;a href="#cite_note-segunda-1"&gt;&lt;span&gt;[&lt;/span&gt;2&lt;span&gt;]&lt;/span&gt;&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;

&lt;div class="reflist" style="list-style-type: decimal;"&gt;
&lt;ol class="references"&gt;
&lt;li id="cite_note-ejemplo-0"&gt;&lt;b&gt;&lt;a href="#cite_ref-ejemplo_0-0"&gt;^&lt;/a&gt;&lt;/b&gt; Esto es una nota al pie.&lt;/li&gt;
&lt;li id="cite_note-segunda-1"&gt;&lt;b&gt;&lt;a href="#cite_ref-segunda_1-0"&gt;^&lt;/a&gt;&lt;/b&gt; Esto es la segunda nota.&lt;/li&gt;

&lt;p&gt;&lt;/ol&gt;
   &lt;/td&gt;
  &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;&lt;/p&gt;</summary><category term="markdown"></category><category term="textile"></category><category term="bbcode"></category><category term="restructuredtext"></category><category term="texy!"></category><category term="txt2tags"></category><category term="creole"></category><category term="mediawiki"></category><category term="marcado"></category><category term="html"></category><category term="xhtml"></category></entry><entry><title>Markdown, la mejor opción para crear contenidos web</title><link href="http://joedicastro.com/markdown-la-mejor-opcion-para-crear-contenidos-web.html" rel="alternate"></link><updated>2011-04-02T20:29:00+02:00</updated><author><name>joe di castro</name></author><id>http://joedicastro.com/markdown-la-mejor-opcion-para-crear-contenidos-web.html</id><summary type="html">&lt;p&gt;Normalmente cuando se crea contenido en un blog o CMS, como Wordpress, Blogger, 
Drupal, Joomla, Plone, Typo, etc, se hace a través de un editor visual (&lt;abbr title="What You See Is What You Get (en inglés, &amp;quot;lo que ves es lo que obtienes&amp;quot;)"&gt;WYSIWYG&lt;/abbr&gt;). 
Algunos editores de este tipo son &lt;a href="http://tinymce.moxiecode.com/"&gt;TinyMCE&lt;/a&gt;, &lt;a href="http://ckeditor.com/"&gt;CKeditor&lt;/a&gt;, &lt;a href="http://nicedit.com/"&gt;NicEdit&lt;/a&gt;, 
&lt;a href="http://www.wymeditor.org/"&gt;WYMEditor&lt;/a&gt;, &lt;a href="http://markitup.jaysalvat.com/home/"&gt;markItUP!&lt;/a&gt;, &lt;a href="http://www.openwebware.com/"&gt;openWYSIWYG&lt;/a&gt;, etc. &lt;/p&gt;
&lt;p&gt;Yo mismo he empleado varios de ellos durante años, en varias plataformas como 
Blogger, Wordpress, Joomla, Drupal, etc. Y si, hay que reconocerlo, te hacen la 
vida muy fácil, sobre todo para aquellos que no quieran preocuparse más que de 
añadir contenido en su página. E incluso usuarios más avanzados como 
administradores o diseñadores web, lo suelen emplear por comodidad frente a un 
área de texto plano. Y si, es cómodo, muy cómodo, a corto plazo. A largo plazo... 
a largo plazo es cuando empiezas a verle los peros y los problemas, que los 
tienen, y bastantes. Veamos cuales son esos problemas y la alternativa, que para 
mi personalmente, es la solución. &lt;/p&gt;
&lt;h2 id="los_problemas_de_los_editores_wysiwyg"&gt;Los problemas de los editores &lt;abbr title="What You See Is What You Get (en inglés, &amp;quot;lo que ves es lo que obtienes&amp;quot;)"&gt;WYSIWYG&lt;/abbr&gt;&lt;/h2&gt;
&lt;p&gt;Los editores visuales están pensados para que de una forma muy fácil, se pueda 
editar el contenido de forma visualmente atractiva, sin tenerse que andar 
preocupando del HTML que se genera a partir de él, esto es, no es necesario 
conocer nada de HTML para usar un editor &lt;abbr title="What You See Is What You Get (en inglés, &amp;quot;lo que ves es lo que obtienes&amp;quot;)"&gt;WYSIWYG&lt;/abbr&gt;. Puedes añadir negritas, 
cursivas, alinear el texto, cambiar el tamaño, el color, crear tablas, etc, sin 
conocimiento alguno de HTML o CSS. Se supone que el editor genera HTML valido 
correctamente por ti. Y aquí es donde empiezan los problemas:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Los editores visuales generalmente crean el formato con &lt;strong&gt;CSS embebido dentro 
 del HTMl&lt;/strong&gt;, es decir mezclando el contenido y el estilo de la página. Algunos 
 incluso te permiten incluir tus propias hojas de estilo para que el contenido 
 se ajuste al estilo actual de tu web. &lt;/p&gt;
&lt;p&gt;¿Pero que ocurre cuando al cabo de un tiempo, con decenas o más de artículos 
generados quieres cambiar el estilo de tú página? Pues dependiendo de como 
haya realizado el trabajo tu editor &lt;abbr title="What You See Is What You Get (en inglés, &amp;quot;lo que ves es lo que obtienes&amp;quot;)"&gt;WYSIWYG&lt;/abbr&gt;, te puedes encontrar con el 
desagradable problema de que todo queda desencajado o no se ajusta al estilo 
actual. Yo me he tenido que enfrentar con este problema en el pasado al 
actualizar una versión de Drupal y cambiar completamente el tema, y no fue 
nada cómodo de solucionar, o al importar contenido de un CMS a otro. &lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Los editores visuales &lt;strong&gt;se crearon inicialmente para impresión en papel&lt;/strong&gt;, no 
 para HTML. Y HTML se emplea en multitud de dispositivos (desde móviles hasta 
 ereaders) y bien empleado proporciona una flexibilidad que se rompe al mezclar 
 la estructura del documento (HTML) con el estilo del mismo (CSS) en el mismo 
 archivo. Así que a la hora de crear versiones para dispositivos móviles, con 
 todas esas etiquetas CSS embebidas, puede ser un verdadero quebradero de cabeza. &lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Se supone que &lt;strong&gt;tienen que generar HTML válido&lt;/strong&gt;, pero durante mucho tiempo 
   &lt;strong&gt;esto no ha sido siempre así&lt;/strong&gt;, sobre todo si el editor no está bien 
   configurado o tiene algún plug-in que no respeta este tema. Además, generan 
   HTML valido ahora, ¿que pasa en el futuro? Es decir, si quieres pasar 
   contenido antiguo para por ejemplo pasar de HTML 2.0 a HTML 4.0 o incluso 5 
   y eliminar etiquetas obsoletas y generar HTML que cumpla con el estándar, 
   prepárate, porque te espera una ardua tarea. Por no decir que muchos de estos 
   editores tienen errores en su propio código javascript, lo que lleva a 
   frecuentes actualizaciones de los mismos.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Pueden &lt;strong&gt;generar cantidades absurdas de HTML&lt;/strong&gt;, sobre todo si no están 
 correctamente configurados. Algunos editores (a veces con la configuración por 
 defecto hacen esto, y tienes que ser tú quien lo ajuste para que no ocurra) 
 crean toneladas de &lt;code&gt;&amp;lt;div&amp;gt;&lt;/code&gt;,&lt;code&gt;&amp;lt;p&amp;gt;&lt;/code&gt; y &lt;code&gt;&amp;lt;span&amp;gt;&lt;/code&gt; sin sentido y totalmente superfluos. 
 Cuando no emplean tablas para formatear algunas presentaciones complejas. Y ni 
 que decir tiene de aquellos editores visuales que te dejan pegar contenido 
 desde un procesador de textos como Word, el HTML generado en estos casos, 
 bueno, hay que verlo para creerlo, un absoluto despropósito. &lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Es incluso factible que un usuario inexperto pueda &lt;strong&gt;romper el diseño de una 
 página a través de un editor &lt;abbr title="What You See Is What You Get (en inglés, &amp;quot;lo que ves es lo que obtienes&amp;quot;)"&gt;&lt;abbr title="What You See Is What You Get (en inglés, &amp;quot;lo que ves es lo que obtienes&amp;quot;)"&gt;WYSIWYG&lt;/abbr&gt;&lt;/abbr&gt;&lt;/strong&gt;, creando párrafos o &lt;code&gt;&amp;lt;div&amp;gt;&lt;/code&gt; de forma 
 inadecuada. Incluso al reescribir un texto es posible que se mezclen  estilos 
 CSS embebidos antiguos con nuevos, generando unos embrollos inmensos a nivel de 
 HTML. Y ya no digo nada de los que rompen el estilo visual de un tema abusando 
 de colores en las fuentes, tamaños, etc&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;El &lt;strong&gt;contenido&lt;/strong&gt; que se guarda &lt;strong&gt;en la base de datos&lt;/strong&gt; esta &lt;strong&gt;mezclado con&lt;/strong&gt; 
 las etiquetas &lt;strong&gt;HTML y CSS&lt;/strong&gt;, todo junto, vamos que no se parece en nada a lo 
 que has introducido en tu editor. Todo esto en principio carece de importancia, 
 pero puede suponer hasta el 20% del tamaño de las páginas de tu sitio, espacio 
 absurdamente desperdiciado en tu BDD. De hecho el contenido XHTML se genera por 
 lo normal cada vez que se visualiza la página de forma dinámica. Si te preocupa 
 el rendimiento, tranquilo, los sistemas de cache están ahí para echarte una 
 mano y que te dejes de preocupar por eso. &lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Distraen la atención&lt;/strong&gt;, el problema de absolutamente todos los editores 
 visuales, incluidos procesadores de texto, el centrarse en el formato y no en 
 el contenido. Una gran mayoría de usuarios desperdicia un tiempo notable 
 preocupándose por más el aspecto visual del documento, que por el contenido del 
 mismo. Que si esto en negrita, que si mejor con esta fuente, que si mejor con 
 la otra... Se desvía la atención de lo importante, el contenido. Mal amigo de 
 la productividad, se tarda bastante más en general, por esta razón, en generar 
 el mismo contenido en un editor &lt;abbr title="What You See Is What You Get (en inglés, &amp;quot;lo que ves es lo que obtienes&amp;quot;)"&gt;WYSIWYG&lt;/abbr&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Son bastante poco flexibles&lt;/strong&gt;, si quieres introducir tu propio HTMl en el 
 contenido (e.g. para tener un control más fino sobre las tablas, yo lo hago en 
 este articulo) puede que te encuentres que lo genera como lo tiene 
 preestablecido, eliminado la estructura que tu quieras darle. Y embeber 
 contenido multimedia o scripts también es frecuentemente un problema, siendo 
 hasta habitual que existan plugins para estos editores para insertar contenidos 
 de sitios como YouTube.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Todos los editores &lt;abbr title="What You See Is What You Get (en inglés, &amp;quot;lo que ves es lo que obtienes&amp;quot;)"&gt;WYSIWYG&lt;/abbr&gt; &lt;strong&gt;aumentan el peso por página y disminuyen la 
 velocidad de carga&lt;/strong&gt;, al necesitar pesado código javascipt que necesita tanto 
 ser descargado como ejecutado por tu navegador. Ya de por si los CMS suelen 
 emplear bastante código javascript, como para encima añadirle un editor visual. 
 Además realizan bastantes llamadas HTTP, ralentizando aún más la velocidad de 
 carga. Ejemplo: tienes un articulo de 80 lineas de texto cuyo contenido XHTML 
 puede pesar tranquilamente de 10 á 20 veces menos que el javascript del editor, 
 ¿no es un poco ridículo?. Aunque muy pocos editores web se preocupan de esto, 
 la verdad, es que hay que pensar que no todo el mundo disfruta de buenas 
 conexiones a la red. &lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Por último y no menos importante, &lt;strong&gt;no cuidan la accesibilidad&lt;/strong&gt;. El HTML 
 valido que cumpla con los estándares, es esencial para crear páginas con una 
 buena accesibilidad, pensando en aquellos que no lo tienen tan fácil para 
 navegar por nuestros sitios web. Deberían tenerse al menos en cuenta un mínimo 
 de puntos sobre este tema al crear contenidos en la web.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;En conclusión, que los editores &lt;abbr title="What You See Is What You Get (en inglés, &amp;quot;lo que ves es lo que obtienes&amp;quot;)"&gt;WYSIWYG&lt;/abbr&gt; pueden no dar problemas hasta que tus 
necesidades cambian, entonces pueden darte más de un quebradero de cabeza. &lt;/p&gt;
&lt;br /&gt;

&lt;h2 id="mark"&gt;Texto plano y Markdown&lt;/h2&gt;
&lt;p&gt;¿Cual sería entonces la solución para esquivar estos problemas? Texto plano, 
nada más, que se transforme automáticamente en HTML valido respetando el formato 
que nosotros queramos darle. Esto existe, y es posible gracias a los lenguajes 
de marcado ligero, entre los que se encuentra &lt;a href="http://daringfireball.net/projects/markdown/"&gt;Markdown&lt;/a&gt;. Nada mejor que 
empezar demostrándolo con un ejemplo:&lt;/p&gt;
&lt;div&gt;
&lt;table&gt;
  &lt;thead&gt;&lt;tr&gt;&lt;th style="width: 50%;"&gt;Markdown&lt;/th&gt;&lt;th&gt;Resultado&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
    &lt;td&gt;&lt;pre class="no_mrkdwn"&gt;
&lt;p&gt;Documento de ejemplo
====================&lt;/p&gt;&lt;p&gt;Lorem ipsum [dolor sit amet](#mark), consectetur adipiscing elit. Curabitur eget ante nunc. Pellentesque a tortor ipsum, id rhoncus orci. Quisque leo sapien, rutrum id convallis id, rutrum in ligula. Vestibulum **semper adipiscing leo** et blandit.&lt;/p&gt;&lt;p&gt;Sed nibh quam, hendrerit _sit amet aliquam_ vel, pulvinar molestie augue.&lt;/p&gt;&lt;p&gt;&amp;gt; Integer cursus, nunc eu ultrices pellentesque, eros leo malesuada turpis, vel convallis neque dolor a nunc. Sed lacus risus, condimentum vitae posuere quis, ultrices pharetra nunc.&lt;/p&gt;&lt;p&gt;Lista numerada (ordenada)&lt;/p&gt;&lt;p&gt;1. Este es el primer elemento
2. Este es el segundo elemento
   * Una lista de puntos anidada
   * Se llama también desordenada
     * Tercer nivel de anidamiento
3. Este es el tercer elemento&lt;/p&gt;&lt;p&gt;![avatar](pictures/avatar.png)&lt;/p&gt;&lt;p&gt;### Cabecera ###&lt;/p&gt;&lt;p&gt;- - -&lt;/p&gt;&lt;p&gt;Morbi erat augue, feugiat eu pellentesque eget, hendrerit quis lectus. Fusce dignissim pretium nibh sed dignissim. Pellentesque lobortis ante eu dui fermentum vitae blandit risus aliquet.&lt;/p&gt;&lt;p&gt;
|   | solo texto | HTML Limpio  |
| -------------- | -- | ------- |
| Markdown       | Si | Si      |
| Editor WYSISWG | X  | A veces |&lt;/p&gt;&lt;p&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;:::python
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;import lifetime
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;for each_day in lifetime.days():
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;carpe_diem()&lt;/p&gt;&lt;p&gt;Suspendisse posuere velit et velit vehicula at scelerisque orci suscipit. Nulla facilisis lorem eu sem viverra varius nec ut felis.&lt;/p&gt;&lt;p&gt;Esto es un texto con nota al pie [^prima] y esta es otra nota [^secunda]&lt;/p&gt;&lt;p&gt;
 *[vehicula]: automobila
[^prima]: Esto es una nota al pie.
[^secunda]: Esto es la segunda nota.&lt;/p&gt;&lt;/pre&gt;
     &lt;/td&gt;
     &lt;td&gt;
&lt;h1&gt;Documento de ejemplo&lt;/h1&gt;

&lt;p&gt;Lorem ipsum &lt;a href="#mark"&gt;dolor sit amet&lt;/a&gt;, consectetur adipiscing elit. Curabitur eget ante nunc. Pellentesque a tortor ipsum, id rhoncus orci. Quisque leo sapien, rutrum id convallis id, rutrum in ligula. Vestibulum &lt;strong&gt;semper adipiscing leo&lt;/strong&gt; et blandit.&lt;/p&gt;

&lt;p&gt;Sed nibh quam, hendrerit &lt;em&gt;sit amet aliquam&lt;/em&gt; vel, pulvinar molestie augue.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Integer cursus, nunc eu ultrices pellentesque, eros leo malesuada turpis, vel convallis neque dolor a nunc. Sed lacus risus, condimentum vitae posuere quis, ultrices pharetra nunc.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Lista numerada (ordenada)&lt;/p&gt;

&lt;ol&gt;&lt;li&gt;Este es el primer elemento&lt;/li&gt;
&lt;li&gt;Este es el segundo elemento

&lt;ul&gt;&lt;li&gt;Una lista de puntos anidada&lt;/li&gt;

&lt;li&gt;Se llama también desordenada

&lt;ul&gt;&lt;li&gt;Tercer nivel de anidamiento&lt;/li&gt;
&lt;/ul&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;/li&gt;
&lt;li&gt;Este es el tercer elemento&lt;/li&gt;
&lt;/ol&gt;&lt;p&gt;&lt;img src="pictures/avatar.png" alt="avatar" /&gt;&lt;/p&gt;

&lt;h3&gt;Cabecera&lt;/h3&gt;

&lt;p&gt;&lt;hr /&gt;&lt;p&gt;Morbi erat augue, feugiat eu pellentesque eget, hendrerit quis lectus. Fusce dignissim pretium nibh sed dignissim. Pellentesque lobortis ante eu dui fermentum vitae blandit risus aliquet.&lt;/p&gt;&lt;/p&gt;
&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;&lt;/th&gt;
  &lt;th&gt;solo texto&lt;/th&gt;

&lt;th&gt;HTML Limpio&lt;/th&gt;
&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;Markdown&lt;/td&gt;
  &lt;td&gt;Si&lt;/td&gt;
  &lt;td&gt;Si&lt;/td&gt;
&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Editor WYSISWG&lt;/td&gt;
  &lt;td&gt;X&lt;/td&gt;
  &lt;td&gt;A veces&lt;/td&gt;

&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;

&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;lifetime&lt;/span&gt;

&lt;p&gt;&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;each_day&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;lifetime&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;days&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
&lt;span class="n"&gt;    carpe_diem&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Suspendisse posuere velit et velit &lt;abbr title="automobila"&gt;vehicula&lt;/abbr&gt; at scelerisque orci suscipit. Nulla facilisis lorem eu sem viverra varius nec ut felis.&lt;/p&gt;&lt;/p&gt;
&lt;p&gt;Esto es un texto con nota al pie &lt;sup id="fnref:prima"&gt;&lt;a href="#fn:prima" rel="footnote"&gt;1&lt;/a&gt;&lt;/sup&gt; y esta es otra nota &lt;sup id="fnref:secunda"&gt;&lt;a href="#fn:secunda" rel="footnote"&gt;2&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;

&lt;div class="footnotes"&gt;
&lt;hr /&gt;&lt;ol&gt;&lt;li id="fn:prima"&gt;
&lt;p&gt;Esto es una nota al pie. &lt;a href="#fnref:prima" rev="footnote"&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn:secunda"&gt;
&lt;p&gt;Esto es la segunda nota. &lt;a href="#fnref:secunda" rev="footnote"&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;

&lt;p&gt;&lt;/ol&gt;
    &lt;/td&gt;
  &lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;/div&gt;&lt;/p&gt;
&lt;p&gt;Es así de sencillo, el texto plano que se escribe en la columna de la izquierda 
genera el HTML que se puede ver representado en la derecha. Es además HTML 
valido, sin CSS embebido (exceptuando el código con resaltado de sintaxis, pero 
esto es necesario y tampoco es generado por &lt;strong&gt;Markdown&lt;/strong&gt; si no por 
&lt;a href="http://qbnz.com/highlighter/"&gt;GeSHi&lt;/a&gt; anteriormente y ahora por &lt;a href="http://pygments.org/"&gt;Pygments&lt;/a&gt;) y empleando el 
mínimo necesario, siendo lo más limpio posible. 
Pero el contenido que se guarda en la base de datos y el que tú editas es el de 
la izquierda. Este contenido generará HTML valido hoy y mañana, es totalmente 
independiente del estilo que emplees en tu página y puedes migrarlo de un CMS a 
otro sin problema alguno. Todo son ventajas, el único inconveniente es que tienes 
que aprender a usar &lt;strong&gt;Markdown&lt;/strong&gt;, algo que es sumamente sencillo, a la par que 
incrementa la legibilidad del texto plano. &lt;/p&gt;
&lt;p&gt;La legibilidad del texto es uno de los pilares fundamentales de &lt;strong&gt;Markdown&lt;/strong&gt;, tal 
y como el mismo autor, &lt;a href="http://en.wikipedia.org/wiki/John_Gruber"&gt;John Gruber&lt;/a&gt;, lo cuenta&lt;sup id="fnref:1"&gt;&lt;a href="#fn:1" rel="footnote"&gt;1&lt;/a&gt;&lt;/sup&gt;:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;El objetivo fundamental de diseño para la sintaxis de Markdown es hacerlo tan 
legible como sea posible. La idea es que un documento formateado con Markdown 
debería poder ser publicado tal y como está, como texto plano, sin que parezca 
que ha sido marcado con etiquetas o instrucciones de formateado. Mientras que la 
sintaxis de Markdown ha sido influenciada por muchos filtros texto-a-HTML 
existentes, la principal fuente de inspiración es el formato de los correos 
electronicos en texto plano. &lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;No voy ahora, en este articulo, a enseñarte a emplear &lt;strong&gt;Markdown&lt;/strong&gt;, pero tienes 
una guía de prácticamente todas las posibilidades que te brinda en 
&lt;a href="http://joedicastro.com/pages/markdown.html"&gt;Markdown &amp;amp; Pygments Lexers Cheat Sheet&lt;/a&gt;. 
Además, si somos así de vagos, podemos emplear también algunos editores visuales 
que generan y emplean markdown, como &lt;a href="http://markitup.jaysalvat.com/home/"&gt;markItUP!&lt;/a&gt; o el conocido &lt;a href="http://code.google.com/p/wmd/"&gt;WMD&lt;/a&gt; que 
empleamos en &lt;a href="http://python.majibu.org"&gt;python majibu&lt;/a&gt;. Aunque ambos editores 
solo soportan Markdown estándar, cuando en este sitio también soporto las 
capacidades adicionales de &lt;a href="http://michelf.com/projects/php-markdown/extra/"&gt;Markdown Extra&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Todo el contenido de este sitio (exceptuando el automático, como las búsquedas, 
etiquetas, acerca de, ...) está generado empleando &lt;strong&gt;Markdown&lt;/strong&gt; y todo está en 
HTML 5 valido. Un ejemplo del HTML que genera Markdown sería el siguiente:&lt;/p&gt;
&lt;table&gt;
 &lt;thead&gt;
  &lt;tr&gt;
   &lt;th style="width: 50%;"&gt;Markdown&lt;/th&gt;&lt;th&gt;HTML&lt;/th&gt;
  &lt;/tr&gt;
 &lt;tbody&gt;
  &lt;tr&gt;
   &lt;td&gt;
    &lt;pre class="no_mrkdwn"&gt;
Documento de ejemplo
====================

Lorem ipsum [dolor sit amet](#mark), consectetur adipiscing elit. Curabitur eget ante nunc. Pellentesque a tortor ipsum, id rhoncus orci. Quisque leo sapien, rutrum id convallis id, rutrum in ligula. Vestibulum **semper adipiscing leo** et blandit.

Sed nibh quam, hendrerit _sit amet aliquam_ vel, pulvinar molestie augue.
    &lt;/pre&gt;
   &lt;/td&gt;
   &lt;td&gt;
    &lt;pre class="no_mrkdwn"&gt;
&amp;lt;h1&amp;gt;Documento de ejemplo&amp;lt;/h1&amp;gt;

&amp;lt;p&amp;gt;Lorem ipsum &amp;lt;a href="#mark"&amp;gt;dolor sit amet&amp;lt;/a&amp;gt;, consectetur adipiscing elit. Curabitur eget ante nunc. Pellentesque a tortor ipsum, id rhoncus orci. Quisque leo sapien, rutrum id convallis id, rutrum in ligula. Vestibulum &amp;lt;strong&amp;gt;semper adipiscing leo&amp;lt;/strong&amp;gt; et blandit.&amp;lt;/p&amp;gt;

&amp;lt;p&amp;gt;Sed nibh quam, hendrerit &amp;lt;em&amp;gt;sit amet aliquam&amp;lt;/em&amp;gt; vel, pulvinar molestie augue.&amp;lt;/p&amp;gt; 
     &lt;/pre&gt;
   &lt;/td&gt;
  &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;Como se puede ver es el HTMl justo, limpio y cumpliendo estándares, ni más ni 
menos. Este es un ejemplo muy sencillo, y posiblemente cualquier editor &lt;abbr title="What You See Is What You Get (en inglés, &amp;quot;lo que ves es lo que obtienes&amp;quot;)"&gt;WYSIWYG&lt;/abbr&gt; 
sea capaz de dar el mismo resultado, el problema aparece con documentos más 
complejos, con sucesivas re-ediciones del texto y con editores mal configurados. 
Eso si, lo que se almacena en la BDD con &lt;strong&gt;Markdown&lt;/strong&gt; es texto plano, con los 
otros editores, el texto, las etiquetas HTML y CSS embebido. &lt;/p&gt;
&lt;h2 id="+porque_markdown_y_no_otros"&gt;¿Porque Markdown y no otros?&lt;/h2&gt;
&lt;p&gt;Evidentemente &lt;strong&gt;Markdown&lt;/strong&gt; no es el único &lt;a href="http://es.wikipedia.org/wiki/Lenguajes_de_marcas_ligeros"&gt;lenguaje de marcado ligero&lt;/a&gt;, 
existen otros también conocidos y extendidos como &lt;a href="http://textile.thresholdstate.com/"&gt;Textile&lt;/a&gt;, &lt;a href="http://www.bbcode.org/"&gt;BBCode&lt;/a&gt;, 
&lt;a href="http://docutils.sourceforge.net/rst.html"&gt;reStructuredText&lt;/a&gt;, &lt;a href="http://texy.info/en/"&gt;Texy!&lt;/a&gt;, &lt;a href="http://txt2tags.org/"&gt;Txt2tags&lt;/a&gt; o los empleados en los Wikis 
como &lt;a href="http://www.wikicreole.org/"&gt;Creole&lt;/a&gt; o el de &lt;a href="http://www.mediawiki.org/wiki/Help:Formatting"&gt;MediaWiki&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;En primer lugar &lt;strong&gt;Markdown&lt;/strong&gt; es uno de los que más características soporta, uno 
de los que más salidas puede generar (no solo HTML, también LaTeX, RTF, PDF, 
EPUB, ...) y además es probablemente el más extendido y soportado de todos 
(exceptuando BBCode y los de los Wikis, empleados en sus nichos particulares). 
Pero también es uno de los más fáciles de emplear (saliendo del formato básico 
como negritas, etc) y que produce un texto plano más vistoso y legible. &lt;/p&gt;
&lt;h3 id="comparativa"&gt;Comparativa&lt;/h3&gt;
&lt;p&gt;Como no, lo mejor, es ver una comparativa co un ejemplo de el mismo documento y 
el texto empleado por cada uno de los lenguajes para generarlo. Para ello he 
creado un articulo aparte para mostrarla.&lt;/p&gt;
&lt;p&gt;&lt;a href="http://joedicastro.com/comparativa-de-lenguajes-de-marcado-ligero.html"&gt;Comparativa&lt;/a&gt;&lt;/p&gt;
&lt;h2 id="+qui+n_emplea_markdown"&gt;¿Quién emplea Markdown?&lt;/h2&gt;
&lt;p&gt;Una de las razones para emplear &lt;strong&gt;Markdown&lt;/strong&gt; es porque es uno de los más 
extendidos, sobre todo en el mundo de la programación. Por ejemplo, 
&lt;a href="http://stackoverflow.com/"&gt;Stack Overflow&lt;/a&gt; y todos los sitios de &lt;a href="http://stackexchange.com/"&gt;Stack Exchange&lt;/a&gt; emplean una 
variante de Markdown para la entrada de texto. Repositorios de código como 
&lt;a href="https://github.com/"&gt;GitHub&lt;/a&gt; y &lt;a href="https://bitbucket.org/"&gt;Bitbucket&lt;/a&gt; también lo emplean para ciertas funciones. 
También lo emplea el sistema de seguimiento de incidencias &lt;a href="http://lighthouseapp.com/"&gt;LightHouse&lt;/a&gt;. &lt;/p&gt;
&lt;p&gt;Fuera del ámbito de la programación, sitios tan conocidos como &lt;a href="http://www.reddit.com/"&gt;Reddit&lt;/a&gt; lo 
emplean. Plataformas para la educación online como &lt;a href="http://moodle.org/"&gt;Moodle&lt;/a&gt; o 
&lt;a href="http://podmedics.heroku.com/"&gt;Podmedics&lt;/a&gt; también hacen uso de él. Un Wiki como &lt;a href="http://www.instiki.org/"&gt;Instiki&lt;/a&gt; permite 
emplear Markdown. Plataformas de blogs y contenidos como &lt;a href="https://posterous.com/"&gt;Posterous&lt;/a&gt;, 
&lt;a href="http://www.tumblr.com/"&gt;Tumblr&lt;/a&gt; y &lt;a href="http://www.squarespace.com/"&gt;Squarespace&lt;/a&gt; lo ofrecen como opción. Y seguro que me estoy 
dejando en el tintero muchos más lugares donde es empleado habitualmente. &lt;/p&gt;
&lt;p&gt;Hay que tener en cuenta de que aquí no he hablado de software CMS que lo soporta, 
eso lo contemplo en el próximo punto, si no más bien de organizaciones/compañías.&lt;/p&gt;
&lt;h2 id="excusas_para_no_emplearlo"&gt;Excusas para no emplearlo&lt;/h2&gt;
&lt;p&gt;La primera que dice todo el mundo, es un incordio usarlo y aprenderlo, la 
pregunta es: ¿Has intentado emplearlo? Créeme se aprende en nada, sobre la 
marcha, y una vez que te acostumbras a él, lo elegirás frente a los editores 
&lt;abbr title="What You See Is What You Get (en inglés, &amp;quot;lo que ves es lo que obtienes&amp;quot;)"&gt;WYSIWYG&lt;/abbr&gt;, casi con toda seguridad. Una vez aprendido no tienes qune separar los 
dedos de tu teclado, no necesitas para nada el ratón para crear tu contenido. 
Ganarás mucho tiempo para ti mismo y lo agredeceras, créeme.&lt;/p&gt;
&lt;p&gt;La segunda, no puedo usarlo en mi CMS o blog. ¿Seguro? A continuación te detallo 
las opciones que conozco para publicar contenidos empleando &lt;strong&gt;Markdown&lt;/strong&gt;.&lt;/p&gt;
&lt;h3 id="cms_y_blogs"&gt;CMS y Blogs:&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Por defecto, como opción o nativamente:&lt;ul&gt;
&lt;li&gt;&lt;a href="http://nestacms.com/"&gt;Nesta&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://kohanut.com/"&gt;Kohanut&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://www.movabletype.org/"&gt;MovableType&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://fdv.github.com/typo/"&gt;Typo&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Con añadidos:&lt;ul&gt;
&lt;li&gt;&lt;a href="http://drupal.org/"&gt;Drupal&lt;/a&gt; A través de un modulo, &lt;a href="http://drupal.org/project/markdown"&gt;Markdown Filter&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://wordpress.org/"&gt;Wordpress&lt;/a&gt; Hay varios plugins disponibles para emplearlo.&lt;/li&gt;
&lt;li&gt;&lt;a href="http://djangoproject.com/"&gt;Django&lt;/a&gt; Hay varias formas de soportarlo.&lt;/li&gt;
&lt;li&gt;&lt;a href="http://plone.org"&gt;Plone&lt;/a&gt; Se puede habilitar a través de un modulo.&lt;/li&gt;
&lt;li&gt;&lt;a href="http://blogger.com"&gt;Blogger&lt;/a&gt; A través de algunos proyectos externos, &lt;a href="http://code.google.com/p/blogger-markdown-editor/"&gt;Blogger-markdown-editor&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://expressionengine.com/"&gt;ExpressionEngine&lt;/a&gt; A través de un plugin.&lt;/li&gt;
&lt;li&gt;&lt;a href="http://www.joomla.org/"&gt;Joomla&lt;/a&gt; A través de una extensión, &lt;a href="http://extensions.joomla.org/extensions/edition/code-display/8391"&gt;jMarkdown&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="generadores_de_sitios_con_contenido_est+tico_html"&gt;Generadores de sitios con contenido estático (HTML):&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/ametaireau/pelican/"&gt;Pelican&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://ringce.com/hyde"&gt;hyde&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://www.blogofile.com/"&gt;Blogofile&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://bitbucket.org/obensonne/poole/src"&gt;Poole&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/xfire/growl/tree"&gt;Growl&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://markdoc.org/"&gt;Markdoc&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://webgen.rubyforge.org/"&gt;Webgen&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://nanoc.stoneship.org/"&gt;nanoc&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://jekyllrb.com/"&gt;jekyll&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://jaspervdj.be/hakyll/"&gt;Hakyll&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://webby.rubyforge.org/"&gt;Webby&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://cloudhead.io/toto"&gt;toto&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://rote.rubyforge.org/"&gt;Rote&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="plataforma_de_blogs_con_contenido_est+tico_html"&gt;Plataforma de Blogs con contenido estático (HTML):&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="http://calepin.co/"&gt;Calepin.co&lt;/a&gt; es un &lt;strong&gt;Pelican&lt;/strong&gt; hospedado, que lee ficheros 
markdown desde &lt;strong&gt;DropBox&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="wiki"&gt;Wiki:&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Por defecto, como opción o nativamente:&lt;ul&gt;
&lt;li&gt;&lt;a href="http://instiki.org"&gt;Instiki&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://ikiwiki.info/"&gt;ikiwiki&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://sputnik.freewisdom.org/"&gt;sputnik&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://alt.textdrive.com/nanoki/"&gt;nanoki&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/jgm/gitit"&gt;gitit&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Con añadidos: &lt;ul&gt;
&lt;li&gt;&lt;a href="http://moinmo.in/"&gt;MoinMoin&lt;/a&gt; con una &lt;a href="http://moinmo.in/ParserMarket/Markdown"&gt;extensión&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://www.mediawiki.org"&gt;MediaWiki&lt;/a&gt; con una &lt;a href="http://www.mediawiki.org/wiki/Extension:MarkdownSyntax"&gt;extensión&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://www.dokuwiki.org/"&gt;DokuWiki&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://oddmuse.org/"&gt;Oddmuse&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://www.pmwiki.org/"&gt;PmWiki&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="foros"&gt;Foros:&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="http://www.phpbb.com/"&gt;phpBB&lt;/a&gt; A través de un 
&lt;a href="http://www.phpbb.com/community/viewtopic.php?f=70&amp;amp;t=2093183"&gt;MOD&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="conversor_markdown_desdea_otros_formatos"&gt;Conversor Markdown desde/a otros formatos:&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="http://johnmacfarlane.net/pandoc/try"&gt;Pandoc&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="editores_de_texto_que_lo_soportan_marcado_de_sintaxis"&gt;Editores de Texto que lo soportan (marcado de sintaxis):&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="http://www.vim.org/"&gt;Vim&lt;/a&gt; con &lt;a href="https://github.com/plasticboy/vim-markdown"&gt;Vim-Markdown&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://www.gnu.org/software/emacs/"&gt;Emacs&lt;/a&gt; con &lt;a href="http://jblevins.org/projects/markdown-mode/"&gt;markdown-mode&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://projects.gnome.org/gedit/"&gt;Gedit&lt;/a&gt; con &lt;a href="http://live.gnome.org/Gedit/MarkdownSupport"&gt;gedit-markdown&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://www.eclipse.org/"&gt;Eclipse&lt;/a&gt; con el experimental &lt;a href="http://www.winterwell.com/software/markdown-editor.php"&gt;markdown editor&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://macromates.com/"&gt;TextMate&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://www.codingmonkeys.de/subethaedit/"&gt;SubEthaEdit&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://ecto.kung-foo.tv/"&gt;Ecto&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://www.red-sweater.com/marsedit/"&gt;MarsEdit&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="editor_markdown"&gt;Editor Markdown:&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="http://sourceforge.net/p/retext/home/"&gt;ReText&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://markdownpad.com/"&gt;Markdown Pad&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://bywordapp.com/"&gt;Byword&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="editor_offline_para_blogs"&gt;Editor Offline para blogs:&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="http://qtm.blogistan.co.uk/"&gt;QTM&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="editores_online_para_probar_markdown"&gt;Editores Online para probar Markdown:&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="http://daringfireball.net/projects/markdown/dingus"&gt;Dingus&lt;/a&gt; por &lt;a href="http://daringfireball.net/"&gt;John Gruber&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://michelf.com/projects/php-markdown/dingus/"&gt;Dingus PHP&lt;/a&gt; por Michel Fortin&lt;/li&gt;
&lt;li&gt;&lt;a href="http://anthonybush.com/markdown_extra_geshi/"&gt;Markdown Extra + GeShi&lt;/a&gt; por Anthony Bush&lt;/li&gt;
&lt;li&gt;&lt;a href="http://dillinger.io/"&gt;Dillinger&lt;/a&gt; es una aplicación en HTML 5 por Joe McCann&lt;/li&gt;
&lt;li&gt;&lt;a href="http://babelmark.bobtfish.net/?markdown=*This+**is+a+test*.&amp;amp;normalize=on"&gt;Babelmark&lt;/a&gt; para comparar las distintas implementaciones de Markdown&lt;/li&gt;
&lt;li&gt;&lt;a href="http://joncom.be/experiments/markdown-editor/edit/"&gt;Markdown Editor&lt;/a&gt; por John Combe&lt;/li&gt;
&lt;li&gt;&lt;a href="http://softwaremaniacs.org/playground/showdown-highlight/"&gt;Showdown&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://markdownr.com/"&gt;Markdownr&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Y si eres desarrollador, tienes disponibles distintas implementaciones de Markdown:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Lenguaje&lt;/th&gt;
&lt;th&gt;Implementaciones&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Python&lt;/td&gt;
&lt;td&gt;&lt;a href="http://www.freewisdom.org/projects/python-markdown/"&gt;Python-markdown&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;PHP&lt;/td&gt;
&lt;td&gt;&lt;a href="http://michelf.com/projects/php-markdown/"&gt;PHP Markdown y PHP Markdown Extra&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Perl&lt;/td&gt;
&lt;td&gt;&lt;a href="http://daringfireball.net/projects/markdown/"&gt;Original&lt;/a&gt; y &lt;a href="https://github.com/fletcher/MultiMarkdown"&gt;MultiMarkdown&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Ruby&lt;/td&gt;
&lt;td&gt;&lt;a href="http://deveiate.org/projects/BlueCloth"&gt;BlueCloth&lt;/a&gt;, &lt;a href="https://github.com/nex3/maruku"&gt;Maruku&lt;/a&gt; y &lt;a href="http://kramdown.rubyforge.org/"&gt;Kramdown&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;C#&lt;/td&gt;
&lt;td&gt;&lt;a href="http://aspnetresources.com/blog/markdown_announced"&gt;Markdown.NET&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;C&lt;/td&gt;
&lt;td&gt;&lt;a href="http://www.pell.portland.or.us/~orc/Code/markdown/"&gt;Discount&lt;/a&gt; y &lt;a href="https://github.com/jgm/peg-markdown"&gt;Peg-Markdown&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;C++&lt;/td&gt;
&lt;td&gt;&lt;a href="http://cpp-markdown.sourceforge.net/"&gt;Cpp-markdown&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Java&lt;/td&gt;
&lt;td&gt;&lt;a href="http://sourceforge.net/projects/markdownj/"&gt;MarkdownJ&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Javascript&lt;/td&gt;
&lt;td&gt;&lt;a href="https://github.com/coreyti/showdown"&gt;Showdown&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Lua&lt;/td&gt;
&lt;td&gt;&lt;a href="http://www.frykholm.se/files/markdown.lua"&gt;markdown.lua&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Haskell&lt;/td&gt;
&lt;td&gt;&lt;a href="http://johnmacfarlane.net/pandoc/"&gt;Pandoc&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Common Lisp&lt;/td&gt;
&lt;td&gt;&lt;a href="http://common-lisp.net/project/cl-markdown/"&gt;CL-Markdown&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Scala&lt;/td&gt;
&lt;td&gt;&lt;a href="http://tristanhunt.com/projects/knockoff/"&gt;Knockoff&lt;/a&gt; y &lt;a href="http://henkelmann.eu/projects/actuarius/"&gt;Actuarius&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Entonces, habiendo tantas opciones, ¿por qué no lo pruebas?&lt;/p&gt;
&lt;p&gt;Y si hay más excusas, pues la verdad, no las conozco, dímelas tú.&lt;/p&gt;
&lt;div class="footnote"&gt;
&lt;hr /&gt;
&lt;ol&gt;
&lt;li id="fn:1"&gt;
&lt;p&gt;The overriding design goal for Markdown’s formatting syntax is to make 
it as readable as possible. The idea is that a Markdown-formatted document 
should be publishable as-is, as plain text, without looking like it’s been 
marked up with tags or formatting instructions. While Markdown’s syntax has 
been influenced by several existing text-to-HTML filters, the single biggest 
source of inspiration for Markdown’s syntax is the format of plain text 
email. &lt;a href="http://daringfireball.net/projects/markdown/"&gt;fuente&lt;/a&gt;&amp;#160;&lt;a href="#fnref:1" rev="footnote" title="Jump back to footnote 1 in the text"&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;</summary><category term="markdown"></category><category term="wysiwyg"></category><category term="textile"></category><category term="editores"></category><category term="marcado"></category><category term="html"></category><category term="xhtml"></category></entry><entry><title>python majibu, preguntas y respuestas sobre python</title><link href="http://joedicastro.com/python-majibu-preguntas-y-respuestas-sobre-python.html" rel="alternate"></link><updated>2011-03-30T00:32:00+02:00</updated><author><name>joe di castro</name></author><id>http://joedicastro.com/python-majibu-preguntas-y-respuestas-sobre-python.html</id><summary type="html">&lt;p&gt;&lt;a href="http://python.majibu.org/" title="python majibu, preguntas y respuestas sobre python"&gt;&lt;img alt="python majibu" src="pictures/majibu.png" title="python majibu" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Aunque ya hace casi dos meses que está funcionando, no es hasta ahora cuando he 
tenido un momento para escribir sobre este nuevo proyecto que he iniciado. Este 
surgió a partir de otro proyecto anterior que resulto ser un fracaso, deaparatos 
PyR, un sitio de &lt;strong&gt;Q&amp;amp;A&lt;/strong&gt; (preguntas y respuestas) sobre gadgets y aparatos de 
electrónica que se apoyaba en deaparatos.com mi anterior página sobre la misma 
temática. Una vez realizado el esfuerzo de traducción al español y adaptación 
del tema, se me ocurrió que podía aprovechar ese trabajo ya realizado para una 
aventura bastante más práctica. Así el día 3 de febrero 
&lt;a href="http://mail.python.org/pipermail/python-es/2011-February/029151.html"&gt;sugerí en la lista de Python en español&lt;/a&gt;, donde se agrupa una buena parte de 
la comunidad hispana de Python, el poder aplicar este mismo formato a un sitio 
de &lt;strong&gt;Q&amp;amp;A&lt;/strong&gt; sobre &lt;strong&gt;Python&lt;/strong&gt;. La idea tuvo una buena acogida, y así 
&lt;a href="http://mail.python.org/pipermail/python-es/2011-February/029167.html"&gt;11 horas después&lt;/a&gt;, nacía &lt;a href="http://python.majibu.org/" title="python majibu, preguntas y respuestas sobre python"&gt;Python majibu&lt;/a&gt;, mi pequeña contribución a la 
comunidad Python en español. Y como decía en ese correo:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Escogí la palabra &lt;strong&gt;majibu&lt;/strong&gt; porque &lt;strong&gt;significa respuestas en suajili&lt;/strong&gt;. Es un
nombre sonoro y corto, en la moda de nombres como Ubuntu.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id="origen_del_formato_qa"&gt;Origen del formato Q&amp;amp;A&lt;/h2&gt;
&lt;p&gt;Este sitio se monta sobre la plataforma &lt;a href="http://www.osqa.net/" title="OSQA"&gt;&lt;strong&gt;OSQA&lt;/strong&gt;&lt;/a&gt; (Open Source Questions &amp;amp; 
Answers) que la gente de &lt;strong&gt;&lt;a href="http://www.dzone.com"&gt;DZone&lt;/a&gt;&lt;/strong&gt; creo a partir del extinto proyecto 
chino &lt;a href="https://github.com/cnprog/CNPROG"&gt;CNProg&lt;/a&gt; de &lt;a href="http://gchen.cn/about/"&gt;Mike Chen&lt;/a&gt; and &lt;a href="http://www.linkedin.com/pub/sailing-cai/b/2bb/383"&gt;Sailing Cai&lt;/a&gt;. Estos dos proyectos 
eran un clon del original formato empleado por &lt;strong&gt;StackOverflow&lt;/strong&gt;, funcionando 
sobre &lt;strong&gt;Django&lt;/strong&gt; (ergo &lt;strong&gt;Python&lt;/strong&gt;).&lt;/p&gt;
&lt;p&gt;&lt;a href="http://stackoverflow.com/"&gt;Stack Overflow&lt;/a&gt; es el sitio que ha conseguido revolucionar el formato de 
Preguntas y Respuestas y causado un gran impacto mediático en la red, sobre todo 
en el mundo anglosajón. Esto formato lo crearon &lt;a href="http://en.wikipedia.org/wiki/Joel_Spolsky"&gt;Joel Sposky&lt;/a&gt; y 
&lt;a href="http://en.wikipedia.org/wiki/Jeff_Atwood"&gt;Jeff Atwwood&lt;/a&gt; para su red de sitios &lt;a href="http://stackexchange.com/"&gt;StackExchange&lt;/a&gt; (hoy ya constituida 
como empresa) y que ha dado lugar a  sitios tan exitosos en el mundo anglosajón 
como el propio &lt;strong&gt;Stack Overflow&lt;/strong&gt;, &lt;a href="http://serverfault.com/"&gt;Server Fault&lt;/a&gt;, &lt;a href="http://superuser.com/"&gt;Super User&lt;/a&gt;, 
&lt;a href="http://askubuntu.com/"&gt;Ask Ubuntu&lt;/a&gt;, &lt;a href="http://www.howtogeek.com/"&gt;How to Geek&lt;/a&gt;, etc. Un formato que se creo para 
StackOverflow, por programadores y para programadores, centrado únicamente en 
aspectos técnicos de la programación informática. Y su éxito ha sido tan brutal 
que se ha extendido a multitud de temáticas que no paran de crecer día a día. 
Es el formato del futuro inmediato, ya lo es ahora mismo del presente para 
ciertas temáticas y es sin duda el formato que podría enterrar a los clásicos, 
veteranos y populares foros. Y no es ninguna broma, la red de sitios de 
preguntas y respuestas StackExchange arroja unas cifras de 
&lt;a href="http://techcrunch.com/2011/01/26/stack-overflow-steps-up-to-the-qa-plate-growing-131-in-2010-from-7m-to-over-16m-uniques/"&gt;16.6 millones de usuarios únicos mensuales&lt;/a&gt;&lt;sup id="fnref:1"&gt;&lt;a href="#fn:1" rel="footnote"&gt;1&lt;/a&gt;&lt;/sup&gt; y 
&lt;a href="http://www.javipas.com/2011/01/26/%C2%BFquora-nah-stack-overflow-es-el-verdadero-fenomeno/"&gt;72,8 millones de paginas vistas al mes&lt;/a&gt; y todo esto en poco más de dos años.&lt;/p&gt;
&lt;p&gt;Aunque cronológicamente, primero fueron los foros, luego &lt;em&gt;Yahoo Answers&lt;/em&gt;, sitios 
como &lt;em&gt;todoexpertos&lt;/em&gt;, luego el casi difunto servicio de &lt;em&gt;Ask&lt;/em&gt;, luego propuestas 
como &lt;a href="http://www.wolframalpha.com/"&gt;Wolfram|Alpha&lt;/a&gt; e &lt;a href="http://www-03.ibm.com/innovation/us/watson/index.html"&gt;IBM Jeopardy&lt;/a&gt;&lt;sup id="fnref:2"&gt;&lt;a href="#fn:2" rel="footnote"&gt;2&lt;/a&gt;&lt;/sup&gt;, más tarde y centrado en un 
perfil de gente muy determinado y elitista: &lt;a href="http://www.quora.com/"&gt;Quora&lt;/a&gt; Y por fin, 
democratizando el formato y el conocimiento para todos los públicos: la red 
StackExchange. De ahí surge luego &lt;strong&gt;OSQA&lt;/strong&gt; como clon sobre Django de Stack 
Overflow, y el futuro &lt;a href="http://qato.com/"&gt;Qato&lt;/a&gt; por parte de la misma gente de DZone, pero de 
pago y centrado en perfiles corporativos y grandes sitios de Q&amp;amp;A. Incluso la 
gran red social, &lt;strong&gt;Facebook&lt;/strong&gt;, se ha sumado, creando &lt;a href="http://www.facebook.com/questions"&gt;su propia propuesta&lt;/a&gt; 
del formato de Preguntas y Respuestas. &lt;/p&gt;
&lt;p&gt;Y en español han surgido también varias alternativas. Desde la red de 
&lt;a href="http://shapado.com/"&gt;Shapado&lt;/a&gt;, otro clon de Stack Overflow creado en &lt;em&gt;Ruby&lt;/em&gt;, a sitios como 
&lt;a href="http://chaxpert.com/es_ES/"&gt;ChaxPert&lt;/a&gt;, &lt;a href="http://respuestasparatodo.com/"&gt;Respuestas para todo&lt;/a&gt; y otros sitios en español montados 
también sobre OSQA como son &lt;a href="http://respuestas.trabber.com/"&gt;Trabber&lt;/a&gt; y &lt;a href="http://cientifi.net/"&gt;Cientifi.net&lt;/a&gt;&lt;/p&gt;
&lt;h2 id="ventajas_del_formato_qa"&gt;Ventajas del formato Q&amp;amp;A&lt;/h2&gt;
&lt;p&gt;Básicamente un sitio de Q&amp;amp;A, es una web de preguntas y respuestas dirigida por 
sus propios usuarios, que son quienes hacen las preguntas, las responden 
compartiendo su conocimiento y moderan la comunidad. Es un formato basado en el 
karma de sus usuarios y ellos son los protagonistas absolutos. &lt;/p&gt;
&lt;p&gt;La &lt;strong&gt;ventaja principal&lt;/strong&gt; es que a través de la participación de los usuarios 
las &lt;strong&gt;respuestas más acertadas y valiosas ganan más visibilidad&lt;/strong&gt; que las menos 
ajustadas a la solución. Lo que es una gran ventaja para los que se vuelvan a 
plantear esa misma pregunta, pues al leerla encuentran la solución más idónea 
antes, sin verse obligados a navegar entre un mar de respuestas, o incluso 
off-topics, como es tan frecuente en en los habituales foros. A tal extremo llega 
esto que si uno realiza preguntas sobre programación en inglés en un buscador 
como Google, la probabilidad de que la respuesta esté en Stack Overflow y entre 
los primeros resultados del buscador es muy elevada. &lt;/p&gt;
&lt;p&gt;Por lo tanto es un formato bastante más eficaz y dinámico que el de los foros y 
el sistema de karma ayuda a crear una comunidad de gente muy competente. Además 
el que la propia comunidad modere el sitio (gracias al sistema de karma) causa 
que apenas exista la lacra del spam, que en estos sitios brilla por su ausencia, 
así como la inexistencia de trolls que con este formato tienen muy pocas 
posibilidades de causar problemas. Pero lo fundamental es que consigue que las 
personas se animen a preguntar y a compartir sus conocimientos con mayor 
naturalidad que con los foros y con muchos mejores resultados.&lt;/p&gt;
&lt;p style="text-align:center;"&gt;&lt;img src="pictures/python_majibu.png" title="Aspecto actual de python majibu"/&gt;&lt;/p&gt;

&lt;h2 id="python_majibu"&gt;Python majibu&lt;/h2&gt;
&lt;p&gt;Yo echaba de menos algo como Stack Overflow  en español, aunque no tengo 
problemas con el inglés y consulto habitualmente los sitios de StackExchange, 
era una pena que la comunidad en español no tuviera un sitio como este. Como 
abarcar un campo tan amplio como el de la programación se me antojaba demasiado, 
que menos que hacer mi humilde aportación a mi lenguaje de programación favorito, 
&lt;strong&gt;Python&lt;/strong&gt;. Aunque yo solo puse la base, el sitio lo crean sus usuarios, 
&lt;strong&gt;python majibu&lt;/strong&gt; no sería nada sin la gente que forma la comunidad de Python en 
español. Ellos son los que aportan el conocimiento, la verdadera esencia del 
sitio y los que decidirán el éxito o no del proyecto y su rumbo. &lt;/p&gt;
&lt;p&gt;Para personalizar el sitio, con respecto a la instalación por defecto de OSQA, 
completé la traducción que otros habían iniciado anteriormente al Español y cree 
un nuevo tema para majibu. Este trabajo &lt;a href="http://code.joedicastro.com/majibu/"&gt;está disponible en mi repositorio&lt;/a&gt; 
para todo aquel que quiera aprovecharlo.&lt;/p&gt;
&lt;p&gt;Adelante, usadlo si queréis, resolver vuestras dudas sobre Python y ayudar a 
resolver las de los demás... el sitio es todo vuestro, yo solamente soy el 
intermediario necesario para que sea posible. Para utilizar este nuevo sitio no 
es necesario crear una cuenta nueva, se puede entrar usando tu cuenta de 
Facebook o Twitter. También puedes usar cualquier otro servicio que soporte 
OpenID como Google, Yahoo, myOpenID, Blogger, Wordpress, Flickr, etc.&lt;/p&gt;
&lt;div class="footnote"&gt;
&lt;hr /&gt;
&lt;ol&gt;
&lt;li id="fn:1"&gt;
&lt;p&gt;A Diciembre de 2010
&amp;#160;&lt;a href="#fnref:1" rev="footnote" title="Jump back to footnote 1 in the text"&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:2"&gt;
&lt;p&gt;Propuestas mucho más complejas, automatizadas y más pruebas de concepto 
que alternativas reales en el presente, pero un claro indicio de lo que nos 
espera en el futuro.&amp;#160;&lt;a href="#fnref:2" rev="footnote" title="Jump back to footnote 2 in the text"&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;</summary><category term="python"></category><category term="majibu"></category><category term="preguntas"></category><category term="respuestas"></category><category term="osqa"></category><category term="stackoverflow"></category></entry><entry><title>Sincronizar una carpeta local y una remota a través de FTP: lftp-mirror</title><link href="http://joedicastro.com/sincronizar-una-carpeta-local-y-una-remota-a-traves-de-ftp-lftp-mirror.html" rel="alternate"></link><updated>2010-12-19T14:58:00+01:00</updated><author><name>joe di castro</name></author><id>http://joedicastro.com/sincronizar-una-carpeta-local-y-una-remota-a-traves-de-ftp-lftp-mirror.html</id><summary type="html">&lt;p&gt;A veces tenemos la necesidad de subir (o bajar) contenido a un servidor
y posteriormente tener actualizados los cambios que se produzcan en uno
(o ambos) de los lados. Es decir tener sincronizados el directorio
remoto y el local. Esto es relativamente fácil cuanto tenemos acceso via
 &lt;a href="http://es.wikipedia.org/wiki/L%C3%ADnea_de_comandos"&gt;consola&lt;/a&gt; y &lt;a href="http://es.wikipedia.org/wiki/Ssh"&gt;ssh&lt;/a&gt; al servidor y podemos utilizar programas tan
 potentes como &lt;a href="http://es.wikipedia.org/wiki/Rsync"&gt;rsync&lt;/a&gt;. ¿Pero que ocurre cuando el único método del
 que disponemos para intercambiar ficheros con el servidor es a través
 del protocolo &lt;a href="http://es.wikipedia.org/wiki/Ftp"&gt;FTP&lt;/a&gt;, como ocurre con muchos servidores web?&lt;/p&gt;
&lt;p&gt;Bien, en ese caso, tenemos un pequeño problema. El protocolo &lt;strong&gt;FTP&lt;/strong&gt;
aunque perfectamente valido para las funciones para las que fue
originalmente creado, la transferencia de archivos, no contempla este
caso. La solución manual y menos efectiva es volver a transferir todos
los archivos cada vez que se produce un cambio, solución nada
recomendable a nada que el tamaño de estos empiece a ser superior a
decenas de Megabytes. También podríamos ir comprobando manualmente que
ficheros han cambiado y transferir únicamente estos, algo también muy
poco recomendable si el número de archivos es elevado. Afortunadamente
algunos clientes gráficos de &lt;strong&gt;FTP&lt;/strong&gt; nos permiten comprobar que ficheros
 son distintos en uno y otro lado y luego transferir únicamente estos,
 lo cual ya es un método bastante más efectivo y adecuado. Aunque si se
 trata de directorios con muchos archivos y una estructura jerárquica
 compleja (muchos directorios y subdirectorios) el proceso es bastante
 lento pues ha de ir comprobando en un lado y en el otro las diferencias
  entre los archivos (fecha, tamaño y atributos únicamente)
recorriendolos todos. ¿Pero que ocurre si queremos realizar esta
operación de forma periódica y automática? entonces esta solución
tampoco es valida, pues necesitaríamos un programa de línea de comandos
o un script para realizarlo.&lt;/p&gt;
&lt;p&gt;Por suerte para nosotros, esta solución también está disponible a través
 de varios programas y scripts para consola, entre los cuales el mejor
 es &lt;a href="http://lftp.yar.ru/"&gt;&lt;strong&gt;lftp&lt;/strong&gt;&lt;/a&gt; de &lt;strong&gt;Alexander V. Lukyanov&lt;/strong&gt;. Este fantástico programa
  es una navaja suiza para todo aquello que necesitemos hacer a través
  de &lt;strong&gt;FTP&lt;/strong&gt;, siendo uno de los mejores clientes &lt;strong&gt;FTP&lt;/strong&gt;, si no el
mejor, que existen. Y una de las innumerables posibilidades que ofrece
 es precisamente la de &lt;strong&gt;sincronizar dos directorios con la opción
 mirror&lt;/strong&gt; (espejar). De esta manera podemos mantener perfectamente
 sincronizados dos directorios de forma automática. &lt;strong&gt;Nos permite hacer
 la sincronización en ambas direcciones, remoto → local y local →
 remoto&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Como ya he mencionado es muy potente y repleto de opciones y permite muchas más
operaciones más allá de la sincronización entre directorios. Por este motivo
&lt;strong&gt;he creado un &lt;a href="http://es.wikipedia.org/wiki/Script"&gt;script&lt;/a&gt; en &lt;a href="http://es.wikipedia.org/wiki/Python"&gt;Python&lt;/a&gt; que empleando lftp, se centra
únicamente en la sincronización entre directorios a través de FTP y añade
algunas nuevas funcionalidades, &lt;a href="http://code.joedicastro.com/lftp-mirror/wiki/Leer_en_espanol"&gt;lftp-mirror&lt;/a&gt;.&lt;/strong&gt;&lt;/p&gt;
&lt;h2 id="+que_ventajas_aporta_este_script"&gt;¿Que ventajas aporta este script?&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Proporciona un log detallado y legible&lt;/strong&gt; que graba en un fichero en disco
y &lt;strong&gt;que puede ser enviado por correo electrónico&lt;/strong&gt; a una o varias direcciones
empleando el servidor de correo local o uno externo.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Permite crear una copia comprimida por día de la semana del directorio
local sincronizado&lt;/strong&gt;. Esto nos permite tener el directorio actualizado y una
copia de seguridad por cada uno de los últimos 7 días, para poder revertir algún
 cambio o borrado accidental.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Se centra únicamente en la sincronización (mirror)&lt;/strong&gt; entre directorios, 
  obviando las otras opciones que nos ofrece lftp&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Nos proporciona&lt;/strong&gt; (en el log) &lt;strong&gt;el tamaño del espacio ocupado en el disco 
  duro por el directorio local y las copias de seguridad.&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Permite tres modos de ejecución distintos,&lt;/strong&gt; lo que lo convierte en muy 
  versátil:&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Como tarea programada&lt;/strong&gt;. En este modo los parámetros de la sincronización 
se incluyen directamente dentro del script y solo es necesario programar su 
ejecución para automatizar el proceso. Es ideal para la sincronización 
periódica de un único directorio/servidor &lt;strong&gt;FTP&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Interactivo.&lt;/strong&gt; En este modo los parámetros se introducen directamente 
como argumentos en la línea de comandos. Es ideal para ejecutar una 
sincronización puntual manual&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Importando los parámetros desde un fichero de configuración.&lt;/strong&gt; Este modo 
es similar al primero, con la diferencia de que en este caso los parámetros 
los tomamos de un fichero de configuración externo. Este fichero que podemos 
crear nosotros mismos (se sirve uno de ejemplo) nos permite establecer 
múltiples operaciones de sincronización que se ejecutaran de manera 
secuencial una detrás de otra.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;En sistemas operativos que lo soporten nos muestra notificaciones 
  emergentes&lt;/strong&gt; a través de la librería libnotify de la ejecución del script y 
  su correcta finalización. Por ejemplo, a través de las notificaciones 
  emergentes de &lt;a href="http://es.wikipedia.org/wiki/Ubuntu"&gt;&lt;strong&gt;Ubuntu&lt;/strong&gt;&lt;/a&gt;. Muy útil para conocer cuando se está ejecutando 
  una tarea programada sin salida por consola.&lt;/li&gt;
&lt;li&gt;Si empleamos los modos de ejecución no interactivos, &lt;strong&gt;emplea &lt;a href="http://es.wikipedia.org/wiki/Base64"&gt;base64&lt;/a&gt; 
  para una mínima protección de la contraseñas de acceso&lt;/strong&gt; a los servidores 
  &lt;strong&gt;FTP&lt;/strong&gt; y evitar almacenarlas las mismas en texto claro. No es una fuerte 
  medida de seguridad, pero es lo mínimo que deberíamos tener en cuenta.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="+para_que_nos_puede_servir_este_script"&gt;¿Para que nos puede servir este script?&lt;/h2&gt;
&lt;p&gt;Vamos a ver un ejemplo de lo más común, las &lt;strong&gt;copias de seguridad de una página web&lt;/strong&gt;. En muchos &lt;a href="http://es.wikipedia.org/wiki/Hosting#Alojamiento_compartido_.28shared_hosting.29"&gt;hosting compartidos&lt;/a&gt; la única posibilidad de transferir archivos con el servidor es a través de una cuenta &lt;strong&gt;FTP&lt;/strong&gt;. Empleando este script, podemos crear un directorio en local donde haremos las copias de seguridad de los ficheros de la web y luego sincronizarlo automáticamente todos los días, descargando únicamente los ficheros que han cambiado. Con esto tendremos no solo el directorio actualizado diariamente, si no que además dispondremos de una copia de seguridad por cada uno de los siete días anteriores para poder corregir cualquier problema ocurrido entre esas fechas. Configurar algo así es realmente sencillo, únicamente tendríamos que cambiar los valores incorporados dentro del script por los que necesitamos y luego programar su ejecución diaria con cron.&lt;/p&gt;
&lt;p&gt;Para una introducción más detallada, instrucciones de ejecución, control de versiones y enlaces para la descarga, acudir al &lt;a href="https://bitbucket.org/joedicastro/lftp-mirror/wiki/Leer_en_espanol"&gt;&lt;strong&gt;repositorio del script&lt;/strong&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Un extracto del código de &lt;strong&gt;lftp-mirror.py&lt;/strong&gt;:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;mirror&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;Mirror the directories.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;

    &lt;span class="n"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;&amp;#39;&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;anonymous&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="s"&gt;&amp;#39; &amp;#39;&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;login&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;local&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;remote&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;normpath&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;local&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;normpath&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;remote&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;port&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;-p {0}&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;port&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;port&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;&amp;#39;&lt;/span&gt;
    &lt;span class="n"&gt;include&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;#39; --include-glob {0}&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;inc_glob&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;inc_glob&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;&amp;#39;&lt;/span&gt;
    &lt;span class="n"&gt;exclude&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;#39; --exclude-glob {0}&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;exc_glob&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;exc_glob&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;&amp;#39;&lt;/span&gt;

    &lt;span class="n"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;http://code.joedicastro.com/lftp-mirror&amp;#39;&lt;/span&gt;
    &lt;span class="n"&gt;msg&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;Connected to {1} as {2}{0}&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;linesep&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;site&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;anonymous&amp;#39;&lt;/span&gt;
                                              &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;anonymous&lt;/span&gt;
                                              &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;login&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
    &lt;span class="n"&gt;msg&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;Mirror {0} to {1}&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;local&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;reverse&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="n"&gt;remote&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                                      &lt;span class="n"&gt;remote&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;reverse&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="n"&gt;local&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;header&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;Start time&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;notify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;Mirroring with {0}...&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;site&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;sync&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;exists&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;local&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mkdir&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;local&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;Created new directory&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;local&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;chdir&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;local&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pardir&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

    &lt;span class="c"&gt;# create the script file to import with lftp&lt;/span&gt;
    &lt;span class="n"&gt;scp_args&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;-vvv&amp;#39;&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;erase&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;newer&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;parallel&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;reverse&lt;/span&gt;
                &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;del_first&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;depth_first&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;no_empty_dir&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;
                &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;no_recursion&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dry_run&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;use_cache&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;
                &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;del_source&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;missing&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;existing&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;loop&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;
                &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;size&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;time&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;no_perms&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;no_umask&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;
                &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;no_symlinks&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;suid&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;chown&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dereference&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;
                &lt;span class="n"&gt;exclude&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;include&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nb"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;ftpscript&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;w&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;script&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;lines&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;open {0}ftp://{1} {2}&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;secure&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;site&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;port&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
                 &lt;span class="s"&gt;&amp;#39;user {0}&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
                 &lt;span class="s"&gt;&amp;#39;mirror {0} {1} {2}&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;scp_args&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                                             &lt;span class="n"&gt;local&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;reverse&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="n"&gt;remote&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                                             &lt;span class="n"&gt;remote&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;reverse&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="n"&gt;local&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
                &lt;span class="s"&gt;&amp;#39;exit&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;script&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;linesep&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;lines&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

    &lt;span class="c"&gt;# mirror&lt;/span&gt;
    &lt;span class="n"&gt;cmd&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;lftp&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;-d&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;-f&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;script&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="n"&gt;sync&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Popen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cmd&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;stdout&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;PIPE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;stderr&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;STDOUT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;}[&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;quiet&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
    &lt;span class="c"&gt;# end mirroring&lt;/span&gt;

    &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;lftp output&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sync&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;stdout&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;readlines&lt;/span&gt;&lt;span class="p"&gt;()))&lt;/span&gt;

    &lt;span class="c"&gt;# compress the dir and create a .gz file with date&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;reverse&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;no_compress&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;notify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;Compressing folder...&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;info&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;Rotate compressed copies&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;compress&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;local&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="c"&gt;# end compress&lt;/span&gt;

    &lt;span class="n"&gt;gz_size&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="n"&gt;get_size&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;gz&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;gz&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;glob&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;glob&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;{0}*.gz&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;local&lt;/span&gt;&lt;span class="p"&gt;))])&lt;/span&gt;
    &lt;span class="n"&gt;log_size&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;get_size&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;exists&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
    &lt;span class="n"&gt;local_size&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;get_size&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;local&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;size&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;best_unit_size&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;local_size&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;gz_size&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;log_size&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;block&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;Disk space used&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;{0:&amp;gt;76.2f} {1}&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;s&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;u&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]))&lt;/span&gt;
    &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;End Time&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;free&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;linesep&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;remove&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;script&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Para obtener el código completo, ir al &lt;a href="https://bitbucket.org/joedicastro/lftp-mirror/src/tip/src/lftp_mirror.py"&gt;fichero fuente&lt;/a&gt;.&lt;/p&gt;</summary><category term="linux"></category><category term="python"></category><category term="script"></category><category term="lftp"></category><category term="ftp mirror"></category><category term="sincronizar"></category><category term="lftp-mirror"></category><category term="ftp sync"></category><category term="ftp"></category></entry><entry><title>Combatir el spam en Drupal</title><link href="http://joedicastro.com/combatir-el-spam-en-drupal.html" rel="alternate"></link><updated>2010-10-14T01:42:00+02:00</updated><author><name>joe di castro</name></author><id>http://joedicastro.com/combatir-el-spam-en-drupal.html</id><summary type="html">&lt;h3 id="articulo_publicado_originalmente_en_el_antiguo_sitio_deaparatoscom"&gt;Articulo publicado originalmente en el antiguo sitio deaparatos.com&lt;/h3&gt;
&lt;p style="text-align: center;"&gt;&lt;img src="pictures/spam_stats.png" 
 alt="deaparatos spam statistiscs" title="estadísticas de spam en deaparatos" 
 height="550" width="593" /&gt;&lt;/p&gt;

&lt;p&gt;En esta gráfica se puede observar la &lt;strong&gt;disminución a lo largo del tiempo de los 
ataques de spam a este sitio&lt;/strong&gt;, &lt;strong&gt;deaparatos.com&lt;/strong&gt;, que funciona sobre 
&lt;strong&gt;Drupal&lt;/strong&gt;. Esto se ha conseguido gracias a una doble estrategia:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;emplear uno de los mejores módulos antispam existentes para Drupal, &lt;strong&gt;Mollom&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;emplear un script en python de elaboración propia, &lt;strong&gt;ban_drupal_spammers.py&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Esta doble estrategia no solo ha conseguido una más que &lt;strong&gt;notable reducción&lt;/strong&gt; 
de la incidencia &lt;strong&gt;del molesto spam&lt;/strong&gt; en este sitio, &lt;strong&gt;de casi un 70%&lt;/strong&gt;, si no 
que además ha conseguido &lt;strong&gt;una más que notable reducción del ancho de banda 
consumido por los spammers&lt;/strong&gt;, como se puede observar en la siguiente tabla:&lt;/p&gt;
&lt;p&gt;Estadísticas de Trafico generado por ataques de spam en deaparatos.com&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Estrategia&lt;/th&gt;
&lt;th&gt;Días&lt;/th&gt;
&lt;th&gt;Ataques&lt;/th&gt;
&lt;th&gt;Trafico (GB)&lt;/th&gt;
&lt;th&gt;Media pagina (KB)&lt;/th&gt;
&lt;th&gt;Trafico mes (MB)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Mollom&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;359&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;48741&lt;/td&gt;
&lt;td&gt;7,116&lt;/td&gt;
&lt;td&gt;146,000&lt;/td&gt;
&lt;td&gt;602,927&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Mollom + script&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;359&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;358666&lt;/td&gt;
&lt;td&gt;0,016&lt;/td&gt;
&lt;td&gt;0,046&lt;/td&gt;
&lt;td&gt;1,389&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Total&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;359&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;407407&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;7,133&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;17,507&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;604,316&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Si solo hubiera empleado el modulo Mollom, sin emplear mi script&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Trafico spam&lt;/th&gt;
&lt;th&gt;Calculo&lt;/th&gt;
&lt;th&gt;Trafico (GB)&lt;/th&gt;
&lt;th&gt;Ahorro (GB)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Trafico total&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;(358666 * (146,000 – 0,046) KB) + 7,133 GB =&lt;/td&gt;
&lt;td&gt;59,481&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;52,349&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Trafico mensual&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;((59,465 GB * 365) / 359) / 12) MB =&lt;/td&gt;
&lt;td&gt;5,040&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;4,435&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Como podemos ver en las cifras mostradas de esta tabla, &lt;strong&gt;se ha conseguido en&lt;/strong&gt; 
un periodo de aproximadamente &lt;strong&gt;un año reducir el ancho de banda consumido por 
los ataques de spam en más de 52 Gigabytes!&lt;/strong&gt;, una autentica barbaridad de 
tráfico que de otro modo se hubiera malgastado. Estamos hablando de &lt;strong&gt;un ahorro 
de&lt;/strong&gt; consumo de tráfico &lt;strong&gt;de casi 4,5 Gigabytes al mes!!!&lt;/strong&gt; Un ahorro de ancho 
de banda mensual que en un hosting compartido puede tranquilamente suponer el 
cambio de un plan de hosting a otro, simplemente basta con que los molestos 
spammers pongan tu sitio web en su punto de mira. Y &lt;strong&gt;ha de tenerse en cuenta&lt;/strong&gt; 
una cosa, &lt;strong&gt;que este trafico mensual hubiera sido muy superior si&lt;/strong&gt; esta doble 
estrategia &lt;strong&gt;no hubiera conseguido reducir el numero de spammers en un 69.25%&lt;/strong&gt;, 
no quiero ni pensar en las cifras que hubieran resultado...&lt;/p&gt;
&lt;p&gt;Para que nos hagamos una idea del ahorro de ancho de banda que ha supuesto el 
emplear mi script &lt;strong&gt;python&lt;/strong&gt;, en el siguiente gráfico podemos ver la diferencia 
entre emplear solo &lt;strong&gt;Mollom&lt;/strong&gt; y emplear &lt;strong&gt;Mollom&lt;/strong&gt; combinado con
&lt;strong&gt;ban_drupal_spammers.py&lt;/strong&gt;&lt;/p&gt;
&lt;p style="text-align: center;"&gt;&lt;img src="pictures/Ahorros.png" 
alt="Eficacia del script" title="Eficacia del script" height="292" 
width="600" /&gt;&lt;/p&gt;

&lt;p&gt;El gráfico es meridianamente claro, como podemos ver, &lt;strong&gt;por cada 1% de ataques 
que son rechazados por ban_drupal_spammers.py&lt;/strong&gt; y no por Mollom, &lt;strong&gt;ahorramos un 
1% de ancho de banda&lt;/strong&gt;, tanto en el peso por página como en el tráfico total. 
Como podemos ver, &lt;strong&gt;hemos ahorrado un total de un 88% de ancho de banda del 
trafico que sería generado por los ataques de spam en deaparatos.com&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Después de comprobar la eficacia de esta doble estrategia durante más de un año 
(las estadísticas se interrumpen antes por el cambio de hosting) voy a 
explicaros el porqué y el como he llegado a ella, a continuación. También se
puede ver el script que ha marcado la diferencia de tráfico.&lt;/p&gt;
&lt;h2 id="el_spam_en_internet"&gt;El spam en internet&lt;/h2&gt;
&lt;p&gt;El &lt;a href="http://es.wikipedia.org/wiki/Spam"&gt;spam&lt;/a&gt; es una de las lacras más tediosas y difíciles de combatir en 
Internet, por no mencionar las tareas delictivas que se apoyan en él. Después 
de 15 años combatiendo el spam en el correo electrónico, el problema aún está 
lejos de solucionarse, si bien es cierto que con una adecuada configuración de 
las herramientas de correo, se ha convertido en una molestia trivial para el 
usuario final. Pero para los servidores de correo y el tráfico de internet
sigue siendo un problema de dimensiones colosales, la lucha contra el mismo se 
ha convertido una tarea titánica en la que se invierten ingentes sumas de dinero 
todos los años. De hecho gran parte del tráfico de toda internet se
debe al spam (hay quien arroja cifras del 80%, e incluso superiores al 90%), lo 
que ha acarreado un costosísimo sobredimensionamiento en el equipamiento de 
proveedores de internet y servidores.&lt;/p&gt;
&lt;p&gt;Como antes comentaba, lejos de una solución definitiva (en gran medida depende 
de un tipo muy común de usuario final con escasa cultura informática), esta 
lacra se expandió hace unos años a hilos en foros, a comentarios en blogs,
redes sociales, irc,... es decir, se ha expandido por toda la red. La explosión 
de la llamada web 2.0 no ha incrementado si no este problema, multiplicándolo. 
Y he aquí como un problema que afectaba a los usuarios de email y a los 
proveedores de internet, se ha convertido también en un gran problema para los 
webmasters.  Todo aquel que gestione un sitio web, ha tenido que enfrentarse 
antes o después con este maldito problema. Un problema que no solo se traduce en 
cientos o miles de detestables mensajes de spam, que se han de combatir de uno 
u de otro modo (algunos o bien se rinden o bien tienen abandonados sus sitios y 
se convierten en auténticos cementerios de spam), si no que además se traduce en 
un serio problema para el trafico de una web. El número de solicitudes que 
producen los ataques de spam puede llegar a ser tan elevado, que congestione 
totalmente ya no solo la página, si no el servidor cuando se trate de un hosting 
compartido, convirtiéndose casi de facto en un &lt;a href="http://es.wikipedia.org/wiki/DoS"&gt;ataque DoS&lt;/a&gt; en toda regla. 
Aún sin llegar a este indeseable extremo, el incremento del tráfico en el sitio 
debido al spam puede llegar a suponer un porcentaje muy importante del ancho de 
banda contratado (incluso más del 50% con contramedidas ineficientes), con los 
consiguientes perjuicios económicos que suponen al webmaster. Los spammers 
siempre han ido por delante de las contra-medidas, y la actual situación, con 
extensas  &lt;a href="http://es.wikipedia.org/wiki/Botnet"&gt;botnets&lt;/a&gt; a su disposición y con el &lt;a href="http://es.wikipedia.org/wiki/Cloud_computing"&gt;cloud computing&lt;/a&gt; (se ha 
detectado el año pasado la primera 
&lt;a href="http://www.idg.es/pcworldtech/Los-hackers-controlan-una-botnet-desde-Amazon-EC2/doc88089-actualidad.htm"&gt;botnet que empleaba los servicios de Amazon EC2&lt;/a&gt;) , nos ha llevado a un 
combate continuo en las que tienen todas las de ganar a medio plazo... y observo 
esto con cierta tristeza, por que entiendo que la solución final pasa 
necesariamente por la educación del usuario final, haciéndole inmune a los -en 
gran medida patéticos, infantiles, ridículos y chapuceros- reclamos del spam. Y 
esto último desgraciadamente dista mucho de acercarse a una situación ideal. 
También cabe mencionar que 
&lt;a href="http://googlewebmaster-es.blogspot.com/2009/12/comentarios-spam-la-dura-realidad.html?utm_source=feedburner&amp;amp;utm_medium=feed&amp;amp;utm_campaign=Feed%3A+ElBlogParaWebmasters+%28El+Blog+para+Webmasters%29"&gt;el spam también perjudica al posicionamiento de una web,&lt;/a&gt; a su prestigio, a 
su funcionalidad, a su aspecto, etc.&lt;/p&gt;
&lt;h2 id="deaparatoscom_y_el_spam"&gt;deaparatos.com y el spam&lt;/h2&gt;
&lt;p&gt;Y &lt;strong&gt;deaparatos&lt;/strong&gt; no está exento de esta amenaza, de hecho se había convertido en 
un serio problema en el 2009. Este sitio está gestionado con &lt;a href="http://drupal.org/"&gt;Drupal&lt;/a&gt;, y 
después de probar con distintos módulos y métodos, unos más frustrantes que 
otros, ninguno solucionaba por completo el problema, ni me satisfacía como 
solución. Al final, combinando el módulo más idóneo para combatir esta plaga 
(idóneo por resultados y por comportamiento) con un script de factura propia en 
&lt;a href="http://drupal.org/project/Modules"&gt;Python&lt;/a&gt;, he logrado, no acabar con todo el spam (se me antoja tarea cuasi 
imposible), pero si minimizar sus efectos a un nivel muchísimo más que aceptable. 
Y minimizar los efectos tanto a la hora de impedir/eliminar los comentarios spam, 
como de reducir el abultado tráfico que estos ataques consumían. ¿Por qué un 
script en &lt;strong&gt;Python&lt;/strong&gt;? bueno buscaba algo rápido, un prototipo para probar la 
solución que tenia en la cabeza y porque estoy "enamorado" de este lenguaje de 
programación. Quizás si veo que merece la pena, me plantee migrarlo a PHP y 
convertirlo en un módulo de &lt;strong&gt;Drupal&lt;/strong&gt;, o bien modifique el modulo oficial que 
estoy empleando para mitigar el spam y le incorpore el código que empleo ahora. 
Bueno, veamos como he llevado a cabo esta solución y porqué.&lt;/p&gt;
&lt;p&gt;Generalmente los métodos para combatir el spam se centran en:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;medidas activas: análisis &lt;a href="http://es.wikipedia.org/wiki/Heuristica"&gt;heurísticos&lt;/a&gt;, &lt;a href="http://es.wikipedia.org/wiki/Inferencia_estad%C3%ADstica"&gt;filtros estadísticos&lt;/a&gt; (&lt;a href="http://en.wikipedia.org/wiki/Bayesian_spam_filtering"&gt;bayesianos&lt;/a&gt;), diferenciación de &lt;a href="http://es.wikipedia.org/wiki/Bot"&gt;bots&lt;/a&gt;/humanos (&lt;a href="http://es.wikipedia.org/wiki/Captcha"&gt;captchas&lt;/a&gt;), filtros por &lt;a href="http://es.wikipedia.org/wiki/Host"&gt;host&lt;/a&gt;/email, mediante &lt;a href="http://es.wikipedia.org/wiki/Cookie"&gt;cookies&lt;/a&gt;, &lt;a href="http://es.wikipedia.org/wiki/Timestamp"&gt;timestamps&lt;/a&gt;, filtros por &lt;a href="http://es.wikipedia.org/wiki/User_agent"&gt;user-agent&lt;/a&gt;, ...&lt;/li&gt;
&lt;li&gt;medidas pasivas: &lt;a href="http://es.wikipedia.org/wiki/Ofuscaci%C3%B3n"&gt;ofuscar&lt;/a&gt; direcciones de correo, moderación de comentarios, &lt;a href="http://es.wikipedia.org/wiki/Nofollow"&gt;enlaces nofollow&lt;/a&gt;, permisos de publicación, políticas de contraseñas, campos ocultos mediante &lt;a href="http://es.wikipedia.org/wiki/Css"&gt;css&lt;/a&gt;/&lt;a href="http://es.wikipedia.org/wiki/Javascript"&gt;javascript&lt;/a&gt;, cerrar los comentarios de un post pasado un tiempo, ...&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;De entrada tenía, y tengo, algo muy claro, no voy a emplear ninguno de estos 
tres métodos habituales: &lt;em&gt;captchas&lt;/em&gt;, &lt;em&gt;moderación de comentarios&lt;/em&gt; y &lt;em&gt;requerir 
registro&lt;/em&gt; para enviar comentarios. Y es una decisión inamovible, no pienso
claudicar de ningún modo en este sentido. Son métodos que o bien me harían 
perder un tiempo del que ni dispongo, ni estoy dispuesto a perder, o bien 
suponen un incordio que me personalmente me incomodan mucho cuando me los
encuentro en otros sitios y por los que no quiero hacer pasar a mis lectores. 
Esto evidentemente deja fuera algunas de las medidas más efectivas para combatir 
el spam, pero son medidas en las que el usuario o el webmaster siempre pierden, 
de un modo u otro, y no estoy dispuesto a permitir que los spammers condicionen 
en ningún modo el compartimiento de este sitio. Aunque suene contradictorio con 
lo que acabo de decir, el método que voy a comentar aquí, y que empleo 
actualmente, emplea en alguna medida el uso de captchas, aunque de modo tan 
limitado, que afecta a menos del 0,5% de los comentarios enviados. Digamos que 
lo acepto como una razonable excepción a la regla. Si empleo en cierta medida 
algunos de los otros métodos.&lt;/p&gt;
&lt;div&gt;
&lt;script src="ammap/swfobject.js" type="text/javascript"&gt;&lt;/script&gt;
&lt;div id="flashcontent" style="text-align: center;"&gt;
&lt;strong&gt;You need to upgrade your Flash Player&lt;/strong&gt;
&lt;/div&gt;

&lt;script type="text/javascript"&gt;// &lt;![CDATA[

var so = new SWFObject("ammap/ammap.swf", "ammap", "600", "400", "8", "#ffffff");
  so.addVariable("path", "ammap/");
  so.addVariable("settings_file", escape("ammap/ammap_settings.xml"));
  so.addVariable("data_file", escape("ammap/ammap_data.xml"));  2
  so.addVariable("loading_settings", "Cargando configuracion");
  so.addVariable("loading_data", "Cargando datos"); 
  so.addVariable("preloader_color", "#999999");       
  so.write("flashcontent");
// ]]&gt;&lt;/script&gt;
&lt;/div&gt;

&lt;p&gt;En este mapa podemos ver el país de origen de los ataques de spam contra 
deaparatos.com&lt;/p&gt;
&lt;h2 id="drupal_y_el_spam"&gt;Drupal y el spam&lt;/h2&gt;
&lt;p&gt;Con que armas contamos en &lt;strong&gt;Drupal&lt;/strong&gt; para combatir el spam? Por un lado tenemos 
el clásico modulo &lt;a href="http://drupal.org/project/spam"&gt;Spam&lt;/a&gt;, que emplee en este mismo sitio durante más de dos 
años,  y que su mayor ventaja es contar con un &lt;a href="http://es.wikipedia.org/wiki/Clasificador_bayesiano_ingenuo"&gt;filtro Bayesiano&lt;/a&gt;. Este 
módulo es usado actualmente en unos 4.893 sitios &lt;sup id="fnref:1"&gt;&lt;a href="#fn:1" rel="footnote"&gt;1&lt;/a&gt;&lt;/sup&gt; con &lt;strong&gt;Drupal&lt;/strong&gt;. Durante 
mucho tiempo funcionó perfectamente, de vez en cuando se colaba algún comentario 
spam, pero era cuestión de reportarselo al modulo y el iba aprendiendo, así como 
podíamos crear nuestros propios filtros personalizados. El problema comenzó 
cuando los que se colaban ya eran unos 20 spam diarios y aumentando, y entonces 
ya no era viable, ni cómodo, perder tanto tiempo para que el filtro bayesiano 
aprendiera a combatir unos ataques que eran cada vez más sofisticados. Así que 
tocaba mirar otra alternativa de entre alguna de las disponibles:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="http://drupal.org/project/captcha"&gt;Captcha&lt;/a&gt;, uno de los más usados en &lt;strong&gt;Drupal&lt;/strong&gt; 
  (&lt;strong&gt;&lt;em&gt;80.286 sitios&lt;/em&gt;&lt;/strong&gt;), y uno de los métodos más empleados en la red. 
  Es la clásica opción donde mediante una pregunta al usuario se intenta 
  diferenciar entre humano y maquina. Los captchas pueden ser de varios tipos, 
  desde cálculos matemáticos sencillos hasta gráficos donde se encuentran unos 
  caracteres ofuscados y que el usuario debe introducir. Hay varios módulos que 
  lo complementan, aportando distintos tipos de captchas, donde &lt;a href="http://drupal.org/project/recaptcha"&gt;reCaptcha&lt;/a&gt; 
  es uno de los más empleados (16.684). &lt;/li&gt;
&lt;li&gt;&lt;a href="http://drupal.org/project/akismet"&gt;Akismet&lt;/a&gt;, todo un clásico, basado en el servicio homónimo, 
  &lt;a href="http://wordpress.org/"&gt;Akismet&lt;/a&gt;, que creara en su día &lt;a href="http://wordpress.org/"&gt;Wordpress&lt;/a&gt; y que hoy es sostenido por 
  &lt;a href="http://automattic.com/"&gt;Automattic&lt;/a&gt;, empresa donde trabajan la mayoría de los desarrolladores 
  oficiales de &lt;strong&gt;Wordpress&lt;/strong&gt;. Es uno de los métodos más difundidos en la red, 
  en parte por venir de la mano de la empresa más emblemática de los blogs. 
  Esta implementación del servicio &lt;strong&gt;akismet&lt;/strong&gt; es ya un tanto antigua (ya no 
  tiene soporte) y hay un modulo más reciente que lo supera y aporta más 
  funcionalidades, &lt;strong&gt;Antispam&lt;/strong&gt;, también en esta lista. Es usado actualmente por 
  solo &lt;strong&gt;&lt;em&gt;947 sitios&lt;/em&gt;&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;&lt;a href="http://drupal.org/project/spamicide"&gt;Spamicide&lt;/a&gt;, se basa en la premisa de que la mayoría de los ataques spam 
  se producen con bots que acceden a la página con navegadores en modo texto 
  (&lt;a href="http://es.wikipedia.org/wiki/Script_%28inform%C3%A1tica%29"&gt;scripts&lt;/a&gt; en realidad), por lo tanto ni hacen uso de &lt;strong&gt;css&lt;/strong&gt;, ni de 
  &lt;strong&gt;javascript&lt;/strong&gt;. Aprovechando esta circunstancia, crean un campo de formulario 
  que es ocultado con css, con lo cual el usuario normal no lo ve, pero si el 
  bot, que si lo rellena con texto, queda descartado. Pero los spammers aprenden 
  muy rapido, así que la medida no es muy efectiva. Además últimamente empiezan 
  a aparecer personas dedicadas a introducir comentarios spam a mano (de ahí 
  vienen muchos de esos comentarios &lt;a href="http://www.frikipedia.es/friki/HOYGAN"&gt;hoygan&lt;/a&gt; absurdos que no parecen tener 
  mucho sentido) y que cobraran una miseria en países subdesarrollados, en parte 
  para saltarse los captcha. Por eso su efectividad es muy dudosa. Aunque si 
  puede ser usado combinado con otros módulos spam, para reforzar su eficacia. 
  Es muy poco usado, apenas &lt;strong&gt;&lt;em&gt;377 sitios&lt;/em&gt;&lt;/strong&gt; lo emplean. &lt;/li&gt;
&lt;li&gt;&lt;a href="http://drupal.org/project/antispam"&gt;Antispam&lt;/a&gt;, uno de los mejores módulos antispam para drupal. Con el se 
  puede usar algunos de los mejores servicios antispam externos  que hay en la 
  red: &lt;a href="http://akismet.com/"&gt;Akismet&lt;/a&gt;, &lt;a href="http://antispam.typepad.com/"&gt;Typepad&lt;/a&gt; y &lt;a href="http://defensio.com/"&gt;Defensio&lt;/a&gt;. Con él podemos abrir una 
  cuenta en uno de estos servicios y configurar el módulo para emplearlo. Su 
  eficacia es muy elevada, ya que son algunas de los mejores armas disponibles 
  contra el spam. El funcionamiento básico es consultar la base de datos de 
  alguno de estos servicios, muy completas, para comprobar si el comentario u 
  el posteador son probable spam, y bloquearlo en caso de que la probabilidad 
  sea muy elevada. En caso de duda, aparecerá un captcha para descartar bots. 
  Tiene unas gráficas estadísticas muy útiles para comprobar la evolución del 
  problema en nuestro sitio. No es demasiado empleado, estando instalado en 
  unos &lt;strong&gt;&lt;em&gt;1.718 sitios&lt;/em&gt;&lt;/strong&gt;. &lt;/li&gt;
&lt;li&gt;&lt;a href="http://drupal.org/project/badbehavior%20"&gt;Bad Behavior&lt;/a&gt;, otro viejo conocido de las medidas antispam. Este se 
  basa en parte en un análisis heurístico de las peticiones HTTP del bot y 
  comparándolo con las bases de datos que poseen de spambots conocidos. Este 
  hace uso también de las base de datos del &lt;strong&gt;Proyecto Honey Pot&lt;/strong&gt;, para 
  reforzar la identificación de spammers. Es bastante eficiente, pero el 
  problema está en que van por detrás siempre de los spammers y a veces se les 
  cuela algún que otro comentario spam. Básicamente porque se basa en que uno 
  reporte los spammers que aún no están en sus bases de datos, y hasta que 
  alguien reporta a un spammer, este puede habernos colado unos cuantos 
  mensajes. Es empleado en unos &lt;strong&gt;&lt;em&gt;918 sitios&lt;/em&gt;&lt;/strong&gt; drupal.&lt;/li&gt;
&lt;li&gt;&lt;a href="http://drupal.org/project/httpbl"&gt;http:BL&lt;/a&gt;, ese se basa enteramente en el &lt;a href="http://www.projecthoneypot.org/"&gt;Proyecto Honey Pot&lt;/a&gt;. Usa 
  sus bases de datos (&lt;a href="http://es.wikipedia.org/wiki/Lista_negra"&gt;DNS blacklist&lt;/a&gt;) para prevenir comentarios spam y 
  recolectores de direcciones email. Es eficiente en la misma medida que el 
  anterior, depende de su base de datos, que no es tan completa como las de los 
  servicios que soporta el modulo &lt;strong&gt;Antispam&lt;/strong&gt;. Una de las virtudes de este 
  modulo es que bloquea las solicitudes de pagina de aquellas Ips que están en 
  su lista negra, con el consiguiente beneficio que esto reporta para el trafico 
  de nuestro sitio. Permite también el uso de &lt;a href="http://en.wikipedia.org/wiki/Whitelist"&gt;whitelists&lt;/a&gt; y &lt;a href="http://en.wikipedia.org/wiki/Greylist"&gt;greylists&lt;/a&gt;. 
  Podría ser uno de los mejores módulos antispam para Drupal si no se colaran 
  más comentarios spam de lo deseable. Actualmente solo&lt;strong&gt;&lt;em&gt;443 sitios&lt;/em&gt;&lt;/strong&gt; emplean 
  este modulo.&lt;/li&gt;
&lt;li&gt;&lt;a href="http://drupal.org/project/phpids"&gt;PHPIDS&lt;/a&gt;, esta emplea una aproximación al problema diferente. Emplea un 
  sistema de detección de intrusos desarrollado y mantenido por &lt;a href="http://php-ids.org/"&gt;PHPIDS&lt;/a&gt;. 
  Este no solo detecta ataques de spam, si no que también otro tipo de ataques 
  maliciosos al sitio, como &lt;a href="http://es.wikipedia.org/wiki/XSS"&gt;XSS (cross site scripting)&lt;/a&gt;, 
  &lt;a href="http://es.wikipedia.org/wiki/Inyeccion_SQL"&gt;inyecciones sql&lt;/a&gt;, DoS, etc. El problema es que arroja demasiados falsos 
  positivos y hemos de ir afinando la detección poco a poco, lo cual puede 
  llegar a ser bastante tedioso. Se puede usar conjuntamente con otros módulos 
  antispam, pero normalmente este bloqueará el ataque antes de que el otro se 
  percate. Lo malo, claro, es que hasta que no esté completamente afinado, a 
  los usuarios les puede dar mucho la lata ante comentarios completamente inocuos. 
  También puede llegar a generar unos logs muy extensos que pueden incrementar 
  bastante nuestra base de datos. Puede ser muy útil para aquellos sitios en 
  los que los ataques van más allá del simple spam. Tampoco es muy empleado, 
  solo &lt;strong&gt;&lt;em&gt;361 sitios&lt;/em&gt;&lt;/strong&gt; lo tienen implantado.&lt;/li&gt;
&lt;li&gt;&lt;a href="http://drupal.org/project/mollom"&gt;Mollom&lt;/a&gt;, uno de los últimos en llegar, pero lo ha hecho arrasando, en 
  dos años ha conseguido que ya sea empleado en &lt;strong&gt;23.983 sitios&lt;/strong&gt; drupal. Esto 
  se debe en parte a que uno de los co-autores es el creador de &lt;strong&gt;Drupal&lt;/strong&gt;, 
  &lt;a href="http://buytaert.net/"&gt;Dries Buytaert&lt;/a&gt;. &lt;a href="http://mollom.com/"&gt;Mollom&lt;/a&gt; es un servicio web en la misma linea que 
  &lt;strong&gt;Askimet&lt;/strong&gt; o &lt;strong&gt;Defensio&lt;/strong&gt;, con una base de datos de usuarios en la que aparte 
  de spammers, se registran reputaciones de usuario en función de parámetros 
  como comentarios ofensivos, comentarios de "baja-calidad" (hoygans), 
  comentarios off-topic, etc según como nosotros lo reportemos a &lt;strong&gt;Mollom&lt;/strong&gt;. Es 
  decir que nos ayuda también a mejorar la calidad de nuestro sitio filtrando 
  también a usuarios con baja reputación en función de los parametros que 
  nosotros marquemos. Esto desde luego es un punto a favor del servicio, que 
  nos permite matar dos pajaros de un tiro. El servicio analiza el texto del 
  mensaje, y si es spam, lo bloquea y en caso de dudas mostrara un captcha como 
  el de la imagen (menos del 2% de las ocasiones). Además todo el código es 
  opensource, tanto el del modulo como el de la API de &lt;strong&gt;Mollom&lt;/strong&gt; y hay 
  disponibles módulos para otros gestores de contenidos como &lt;strong&gt;Wordpress&lt;/strong&gt;, 
  &lt;a href="http://www.joomla.org/"&gt;Joomla&lt;/a&gt; o &lt;a href="http://radiantcms.org/"&gt;Radiant&lt;/a&gt; y librerías para múltiples lenguajes (Java, PHP, 
  Ruby, Python, Perl, .Net, ...).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Después de analizar las posibilidades y probar unos cuantos módulos 
(&lt;strong&gt;Antispam&lt;/strong&gt;, &lt;strong&gt;Bad Behavior&lt;/strong&gt;, &lt;strong&gt;http:BL&lt;/strong&gt;, &lt;strong&gt;PHPIDS&lt;/strong&gt; y &lt;strong&gt;Mollom&lt;/strong&gt;) llegué a 
valorar que las dos mejores soluciones en mi caso eran &lt;strong&gt;Antispam&lt;/strong&gt; Y
&lt;strong&gt;Mollom&lt;/strong&gt;. Aunque &lt;strong&gt;PHPIDS&lt;/strong&gt; y &lt;strong&gt;http:BL&lt;/strong&gt; tenían algunas características 
únicas que echaba de menos en ellos. Después de probar durante unas semanas 
tanto &lt;strong&gt;Antispam&lt;/strong&gt; como &lt;strong&gt;Mollom&lt;/strong&gt;, observe que el indice de fallos de
&lt;strong&gt;Mollom&lt;/strong&gt; era mucho menor y además era más transparente al usuario, mostrando 
el captcha en menos ocasiones. Si, &lt;strong&gt;Captcha&lt;/strong&gt; es la opción más socorrida por la 
mayoría de los usuarios de &lt;strong&gt;Drupal&lt;/strong&gt;, en cuanto que es la que menos molesta al 
webmaster, claro, pero le traspasa la molestia al usuario. Yo odio directamente 
los captcha, no los soporto, y he pasado de utilizar alguna web por ellos. 
&lt;strong&gt;Mollom&lt;/strong&gt; tiene la ventaja de reducir esta molestia a la minima expresión, por 
lo que el 98% de los usuarios de la web ni siquiera se darán cuenta de que en 
ella funciona un sistema antispam, que es lo que buscaba desde el principio, un 
servicio efectivo y transparente.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Mollom&lt;/strong&gt; era pues, la opción elegida y la que está funcionando en este sitio 
desde entonces.&lt;/p&gt;
&lt;p style="text-align: center;"&gt;&lt;img src="pictures/captcha.png" 
alt="Ejemplo de captcha de Mollom" title="Ejemplo de captcha de Mollom" 
height="114" width="600" /&gt;&lt;/p&gt;

&lt;p&gt;Ejemplo de captcha generado por Mollom&lt;/p&gt;
&lt;h2 id="la_soluci+n_definitiva_mollom__ban_drupal_spammerspy"&gt;La solución definitiva, Mollom + ban_drupal_spammers.py&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Aunque Mollom funciona de manera muy efectiva, bloqueando aprox. el 99,98% 
(en deaparatos.com) de los mensajes spam&lt;/strong&gt;, esto no impide que los atacantes 
sigan intentando una y otra vez colar su spam en el sitio. Esto nos lleva a que 
las páginas se cargan una y otra vez, consumiendo ancho de banda, ya que 
&lt;strong&gt;Mollom&lt;/strong&gt; actúa a posteriori, cuando se envía el comentario, no antes de cargar 
la página, lo que es el funcionamiento normal de estos sistemas antispam. Y 
además los spammers tienen cierta inclinación a intentar introducir el spam en 
las paginas más populares, las que suelen tener más comentarios y por lo tanto 
de mayor peso por lo general. Basta con decir que el ancho de banda medio 
generado por cada uno de estos ataques en este sitio ha sido de 146Kb.&lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Y es en este aspecto donde echaba de menos una de las características de 
&lt;strong&gt;http:BL&lt;/strong&gt;, bloquear el acceso al sitio a los que están en su lista negra. 
Empecé entonces a darle vueltas a la manera de implementar esta característica
en mi sitio, pero pronto me di cuenta de dos cosas:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;No quería hacer una consulta a projecthoneypot.org cada vez que alguien 
  accediera al sitio, por evidentes mermas en el rendimiento del sitio.&lt;/li&gt;
&lt;li&gt;No quería tampoco tener una lista negra local que se alimentara 
  periódicamente de projecthoneypot.org, porque no quería tener que comprobar 
  miles de ips que probablemente nunca accederían a mi sitio.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;La solución entonces pasaba por bloquear solo a los que ya hubieran ejecutado 
un ataque de spam contra el sitio y que hubieran sido bloqueados al menos una 
vez por &lt;strong&gt;Mollom&lt;/strong&gt;, de modo que en los sucesivos ataques fueran rechazados antes 
siquiera de cargar la página.&lt;/p&gt;
&lt;p&gt;Hay una forma de hacer esto de forma manual en &lt;strong&gt;Drupal&lt;/strong&gt;, simplemente hay que 
añadir las ips de los spammers a través de las &lt;em&gt;reglas de acceso&lt;/em&gt; en el menú de 
&lt;em&gt;Administración&lt;/em&gt;. Claro que el método es evidentemente tedioso y aparatoso, 
comprobar las ips atacantes e ir añadiéndolas una por una a través del 
formulario. Tenía que hacerse de una manera automatizada.&lt;/p&gt;
&lt;p&gt;La primera idea y más evidente era modificar el modulo &lt;strong&gt;Mollom&lt;/strong&gt; para lograr 
esto, pero no me gusta PHP y procuro evitarlo, además quería un prototipo rápido 
para evaluar la eficacia de la solución y su repercusión en el ancho de
banda, así que todo empezó con un sencillo script en &lt;strong&gt;python&lt;/strong&gt;. Pronto me di 
cuenta de que &lt;strong&gt;Mollom&lt;/strong&gt; registraba las ips de todos los atacantes que bloqueaba 
en el registro de eventos de &lt;strong&gt;Drupal&lt;/strong&gt; (la tabla &lt;em&gt;watchdog&lt;/em&gt; del modulo opcional 
&lt;em&gt;Database logging&lt;/em&gt;), y que alguna de ellas tenía hasta 30 entradas diferentes 
en el registro. Y como &lt;strong&gt;Drupal&lt;/strong&gt; incorpora el método que citaba antes para 
banear IPs, lo único necesario era añadir estas IPs a la tabla &lt;em&gt;access&lt;/em&gt;.&lt;/p&gt;
&lt;p style="text-align: center;"&gt;&lt;img src="pictures/banned.png" 
alt="Ejemplo de pagina de ip bloqueada por Drupal" 
title="Ejemplo de pagina de ip bloqueada por Drupal" height="30" 
width="233" /&gt;&lt;/p&gt;

&lt;p&gt;Este es un ejemplo de la pàgina que se encontraria un atacante de spam bloqueado 
a través de la tabla &lt;em&gt;access&lt;/em&gt; en Drupal&lt;/p&gt;
&lt;p&gt;Ahora bien, si añadimos automáticamente estas IPs, llegara un momento en que 
tendremos varias miles de ellas, y el rendimiento de la página se vera afectado, 
al tener que comprobar todas estas ips cada vez que alguien accede a la página. 
Además hay que tener en cuenta que algunas de estas IPs tendrán como origen a un 
usuario que teniendo el ordenador o router infectado por un 
&lt;a href="http://es.wikipedia.org/wiki/Rootkit"&gt;rootkit&lt;/a&gt;/&lt;a href="http://es.wikipedia.org/wiki/Troyano_%28inform%C3%A1tica%29"&gt;troyano&lt;/a&gt;,  pertenezca a una &lt;a href="http://es.wikipedia.org/wiki/Botnet"&gt;Botnet&lt;/a&gt; sin saberlo. Es
posible que estos usuarios acaben limpiando de &lt;a href="http://es.wikipedia.org/wiki/Malware"&gt;malware&lt;/a&gt; su equipo y en un 
momento determinado quieran acceder legítimamente al sitio, por lo que no 
deberían estar bloqueados de por vida. Esto lo solucioné en el script rotando 
las IPs al llegar a umbral determinado, marcado por el número máximo de las IPs 
que deseemos almacenar en esta tabla. Al llegar a este número máximo, se borra 
un porcentaje de IPs, eligiendo siempre a las más antiguas. En estos momentos, 
en función del rendimiento y tiempo que quiero que permanezcan en la tabla, 
tengo este valor establecido en unas 2000 IPs. Para controlar la fecha en que 
fueron introducidas cada una de las ips en la tabla, modifico la tabla access, 
añadiéndole un campo &lt;em&gt;timestamp&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;Como se pudo ver al principio del articulo, la efectividad del script es muy 
elevada y a día de hoy sigo con este método, con un script que ha evolucionado 
varias veces desde entonces y que se adapta perfectamente a mis necesidades.
Los picos que se pueden ver en el primer gráfico del spam bloqueado por el 
modulo &lt;strong&gt;Mollom&lt;/strong&gt;, se deben precisamente a los breves periodos de tiempo en los 
que por una u otra razón el script no estaba funcionando.&lt;/p&gt;
&lt;p&gt;El porqué del fantástico ahorro de ancho de banda se puede explicar con la 
anterior imagen, que es un ejemplo de la página que se encontraría un atacante 
bloqueado por &lt;strong&gt;ban_drupal_spammers.py&lt;/strong&gt;. &lt;strong&gt;Esta página tiene un peso ridículo 
de entre 33 y 39 bytes, del orden de unas 4000 veces menos que el peso medio de 
146 Kilobytes por página del trafico generado por los spammers&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Este script se puede ejecutar en remoto, para hostings compartidos que no pueden 
correr scripts en &lt;strong&gt;python&lt;/strong&gt; pero si permiten acceso remoto a la base de datos en 
MySQL, como mi anterior &lt;a href="http://www.hostsuar.com/"&gt;hosting&lt;/a&gt; (quede muy satisfecho). Pero
también puede ser ejecutado de manera local, en hosting compartidos (que 
soporten python), en &lt;a href="http://es.wikipedia.org/wiki/Servidor_virtual_privado"&gt;VPS&lt;/a&gt; y en servidores dedicados. No muchos hostings 
compartidos permiten la ejecución de scripts en python, ni siquiera ssh o
acceso remoto a la BDD. Afortunadamente, mi hosting actual, &lt;strong&gt;&lt;a href="http://www.webfaction.com/?affiliate=joedicastro"&gt;Webfaction&lt;/a&gt;&lt;/strong&gt;, 
me permite todas esas posibilidades y no es ningún un problema. De hecho es el 
mejor hosting compartido que haya probado nunca y uno de los mejores del mercado, 
porque su manera de trabajar es única y es lo más parecido a un VPS, pero con 
una facilidad para administrar las tareas más cotidianas apabullante. Eso si, 
es distinto a todos los demás y necesita uno adaptarse a su manera de hacer las 
cosas, pero luego ya no quieres saber nada de otros hosting compartidos.  Si 
además quieres trabajar con ruby o python, pocos puede competir con su 
flexibilidad, lo que me hizo decidirme por él.&lt;/p&gt;
&lt;h2 id="el_script_ban_drupal_spammerspy"&gt;El script, ban_drupal_spammers.py&lt;/h2&gt;
&lt;p&gt;El script (siempre la versión más actualizada), los ficheros auxiliares y las 
instrucciones de como emplearlos, pueden ser encontrados en mi repositorio&lt;/p&gt;
&lt;p&gt;&lt;a href="http://code.joedicastro.com/ban-drupal-spammers/wiki/Leer_en_espanol"&gt;&lt;strong&gt;code.joedicastro.com/ban-drupal-spammers&lt;/strong&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;donde tambien se puede encontrar el script python que empleo para recoger los 
datos que se muestran en el mapa de este árticulo.&lt;/p&gt;
&lt;p&gt;El código de &lt;strong&gt;ban_drupal_spammers.py&lt;/strong&gt; es el siguiente:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="c"&gt;#!/usr/bin/env python&lt;/span&gt;
&lt;span class="c"&gt;# -*- coding: utf8 -*-&lt;/span&gt;

&lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="sd"&gt;    ban drupal spammers.py: ban spammers in Drupal with Mollom&amp;#39;s aid&lt;/span&gt;
&lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;

&lt;span class="c"&gt;#===============================================================================&lt;/span&gt;
&lt;span class="c"&gt;# This Script uses the Mollom reports in Drupal for ban spammers&amp;#39; ips and&lt;/span&gt;
&lt;span class="c"&gt;# reduce the bandwith usage in the website.&lt;/span&gt;
&lt;span class="c"&gt;#===============================================================================&lt;/span&gt;

&lt;span class="c"&gt;#===============================================================================&lt;/span&gt;
&lt;span class="c"&gt;#    Copyright 2010 joe di castro &amp;lt;joe@joedicastro.com&amp;gt;&lt;/span&gt;
&lt;span class="c"&gt;#&lt;/span&gt;
&lt;span class="c"&gt;#    This program is free software: you can redistribute it and/or modify&lt;/span&gt;
&lt;span class="c"&gt;#    it under the terms of the GNU General Public License as published by&lt;/span&gt;
&lt;span class="c"&gt;#    the Free Software Foundation, either version 3 of the License, or&lt;/span&gt;
&lt;span class="c"&gt;#    (at your option) any later version.&lt;/span&gt;
&lt;span class="c"&gt;#&lt;/span&gt;
&lt;span class="c"&gt;#    This program is distributed in the hope that it will be useful,&lt;/span&gt;
&lt;span class="c"&gt;#    but WITHOUT ANY WARRANTY; without even the implied warranty of&lt;/span&gt;
&lt;span class="c"&gt;#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the&lt;/span&gt;
&lt;span class="c"&gt;#    GNU General Public License for more details.&lt;/span&gt;
&lt;span class="c"&gt;#&lt;/span&gt;
&lt;span class="c"&gt;#    You should have received a copy of the GNU General Public License&lt;/span&gt;
&lt;span class="c"&gt;#    along with this program.  If not, see &amp;lt;http://www.gnu.org/licenses/&amp;gt;.&lt;/span&gt;
&lt;span class="c"&gt;#&lt;/span&gt;
&lt;span class="c"&gt;#===============================================================================&lt;/span&gt;

&lt;span class="n"&gt;__author__&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;joe di castro - joe@joedicastro.com&amp;quot;&lt;/span&gt;
&lt;span class="n"&gt;__license__&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;GNU General Public License version 3&amp;quot;&lt;/span&gt;
&lt;span class="n"&gt;__date__&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;15/05/2010&amp;quot;&lt;/span&gt;
&lt;span class="n"&gt;__version__&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;0.52&amp;quot;&lt;/span&gt;

&lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;sys&lt;/span&gt;
    &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;os&lt;/span&gt;
    &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;time&lt;/span&gt;
    &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;base64&lt;/span&gt;
    &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;collections&lt;/span&gt;
    &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;MySQLdb&lt;/span&gt;
    &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;pygeoip&lt;/span&gt;
    &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;logger&lt;/span&gt;
&lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="ne"&gt;ImportError&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="c"&gt;# Checks the installation of the necessary python modules&lt;/span&gt;
    &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;linesep&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;join&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;An error found importing one module:&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;exc_info&lt;/span&gt;&lt;span class="p"&gt;()[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]),&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;You need to install it&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;Exit...&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;]))&lt;/span&gt;
    &lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;exit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;connect_db&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;host&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;pass_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;port&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;3306&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;Connect to MySQL database.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;data_base&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;MySQLdb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;host&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;host&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;passwd&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;pass_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                                    &lt;span class="n"&gt;port&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;port&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;client_flag&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;65536&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="c"&gt;# flag 65536 is to allow multiple statements in a single string, equals&lt;/span&gt;
        &lt;span class="c"&gt;# to CLIENT_MULTI_STATEMENTS&lt;/span&gt;
    &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="n"&gt;MySQLdb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;OperationalError&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;Database connection fails, check that you gave the right &amp;quot;&lt;/span&gt;
              &lt;span class="s"&gt;&amp;quot;credentials to access the database{0}Exit...&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;linesep&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;exit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;data_base&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;curs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sql&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;Runs a SQL SELECT query and returns a tuple as output.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
    &lt;span class="n"&gt;curs&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sql&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;curs&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fetchall&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;alter_table&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;curs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;db_table&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;Create the aux field in the table if no exists, else do nothing.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
    &lt;span class="n"&gt;database_string&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="s"&gt;                        ALTER TABLE {0}&lt;/span&gt;
&lt;span class="s"&gt;                        ADD timestamp INT(11) NOT NULL DEFAULT &amp;#39;0&amp;#39;;&lt;/span&gt;
&lt;span class="s"&gt;                        &amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;db_table&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;curs&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;database_string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;Aux Field &amp;#39;timestamp&amp;#39; in table &amp;#39;{0}&amp;#39; created.&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;db_table&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="n"&gt;MySQLdb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;OperationalError&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;print&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;Can&amp;#39;t create the aux field, seems this exists previously.&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="c"&gt;# This output is not reported in the log, it will be repetitive.&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;ins_qstr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;q_mask&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;q_timestamp&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;Create a SQL INSERT query string for the given ip.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
    &lt;span class="n"&gt;iqstr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="s"&gt;            INSERT INTO `access`&lt;/span&gt;
&lt;span class="s"&gt;            (mask, type, status, timestamp)&lt;/span&gt;
&lt;span class="s"&gt;            VALUES (&amp;#39;{0}&amp;#39;, &amp;#39;host&amp;#39;, &amp;#39;0&amp;#39;, {1});{2}&lt;/span&gt;
&lt;span class="s"&gt;            &amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;q_mask&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;q_timestamp&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;linesep&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;iqstr&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;del_qstr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;q_timestamp&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;Create a DELETE query string for the given timestamp.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
    &lt;span class="n"&gt;dqstr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="s"&gt;            DELETE FROM access&lt;/span&gt;
&lt;span class="s"&gt;            WHERE timestamp=&amp;#39;{0}&amp;#39;;{1}&lt;/span&gt;
&lt;span class="s"&gt;            &amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;q_timestamp&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;linesep&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;dqstr&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;ip_and_country&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;l_ips&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;geo&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;Create the log lines about the ips and their countries.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
    &lt;span class="n"&gt;output&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;l_ips&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;total&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;{0} IPs&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;l_ips&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="n"&gt;ips_and_countries&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[(&lt;/span&gt;&lt;span class="n"&gt;geo&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;country_name_by_addr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;l&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;l&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;l&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;l_ips&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="n"&gt;ips&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;linesep&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;join&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;{0:16} {1}&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt;  &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt;
                               &lt;span class="nb"&gt;sorted&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ips_and_countries&lt;/span&gt;&lt;span class="p"&gt;)])&lt;/span&gt;
        &lt;span class="n"&gt;output&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;linesep&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;join&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="n"&gt;total&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ips&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;output&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;renew_geoip&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;gip_path&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;Check if the geoip data file is too old.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
    &lt;span class="n"&gt;out_str&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;&amp;#39;&lt;/span&gt;
    &lt;span class="n"&gt;gz_file&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;http://geolite.maxmind.com/download/geoip/database/&amp;quot;&lt;/span&gt;
                &lt;span class="s"&gt;&amp;quot;GeoLiteCountry/GeoIP.dat.gz&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;web_url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;http://www.maxmind.com/app/geolitecountry&amp;quot;&lt;/span&gt;
    &lt;span class="n"&gt;geoip_file_date&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;getmtime&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;gip_path&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;geoip_file_date&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;2592000&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="c"&gt;# 2592000s = 30 days&lt;/span&gt;
        &lt;span class="n"&gt;out_str&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;Your GeoIP data file* is older than 30 days!{0}{0}&amp;quot;&lt;/span&gt;
                    &lt;span class="s"&gt;&amp;quot;You can look for a new version in:{0}{1}{0}or{0}{2}{0}{0}&amp;quot;&lt;/span&gt;
                    &lt;span class="s"&gt;&amp;quot;  *{3}&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;linesep&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;gz_file&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;web_url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;gip_path&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;out_str&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;main section&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;

&lt;span class="c"&gt;#===============================================================================&lt;/span&gt;
&lt;span class="c"&gt;# SCRIPT PARAMATERS&lt;/span&gt;
&lt;span class="c"&gt;#===============================================================================&lt;/span&gt;

    &lt;span class="c"&gt;# database host, name or ip (&amp;#39;localhost&amp;#39; by default)&lt;/span&gt;
    &lt;span class="n"&gt;host&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;localhost&amp;#39;&lt;/span&gt;
    &lt;span class="c"&gt;# database user name (&amp;#39;root&amp;#39; by default)&lt;/span&gt;
    &lt;span class="n"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;root&amp;#39;&lt;/span&gt;
    &lt;span class="c"&gt;# database password, with a minimum security measure, encoded by base64&lt;/span&gt;
    &lt;span class="c"&gt;# (&amp;#39;password&amp;#39; by default)&lt;/span&gt;
    &lt;span class="n"&gt;password&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;base64&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;b64decode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;cGFzc3dvcmQ=&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="c"&gt;# database name (&amp;#39;database&amp;#39; by default)&lt;/span&gt;
    &lt;span class="n"&gt;database&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;database&amp;#39;&lt;/span&gt;

    &lt;span class="c"&gt;# path to geolocation data file GeoIP.dat&lt;/span&gt;
    &lt;span class="n"&gt;geoip_path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;/your/path/to/file/GeoIP.dat&amp;#39;&lt;/span&gt;

    &lt;span class="c"&gt;# mail server, smtp protocol, to send the log (&amp;#39;localhost&amp;#39; by default)&lt;/span&gt;
    &lt;span class="n"&gt;smtp_server&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;localhost&amp;#39;&lt;/span&gt;
    &lt;span class="c"&gt;# sender&amp;#39;s email address (&amp;#39;&amp;#39; by default)&lt;/span&gt;
    &lt;span class="n"&gt;from_addr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;&amp;#39;&lt;/span&gt;
    &lt;span class="c"&gt;# a list of receiver(s)&amp;#39; email addresses ([&amp;#39;&amp;#39;] by default)&lt;/span&gt;
    &lt;span class="n"&gt;to_addrs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="c"&gt;# smtp server user (&amp;#39;&amp;#39; by default)&lt;/span&gt;
    &lt;span class="n"&gt;smtp_user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;&amp;#39;&lt;/span&gt;
    &lt;span class="c"&gt;# smtp server password, with a minimum security measure, encoded by base64&lt;/span&gt;
    &lt;span class="c"&gt;# (&amp;#39;password&amp;#39; by default)&lt;/span&gt;
    &lt;span class="n"&gt;smtp_pass&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;base64&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;b64decode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;cGFzc3dvcmQ=&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c"&gt;# set the perfomace threshold (number of banned ips) for you site&lt;/span&gt;
    &lt;span class="n"&gt;threshold&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2000&lt;/span&gt;

&lt;span class="c"&gt;#===============================================================================&lt;/span&gt;
&lt;span class="c"&gt;# END PARAMETERS&lt;/span&gt;
&lt;span class="c"&gt;#===============================================================================&lt;/span&gt;

    &lt;span class="c"&gt;# Initialize the log&lt;/span&gt;
    &lt;span class="n"&gt;log&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Logger&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="c"&gt;# log the header&lt;/span&gt;
    &lt;span class="n"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;http://code.joedicastro.com/ban-drupal-spammers&amp;#39;&lt;/span&gt;
    &lt;span class="n"&gt;connected&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;Connected to {0} in {1} as {2}&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;database&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;host&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;header&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;connected&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c"&gt;# log the start time&lt;/span&gt;
    &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;Start Time&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c"&gt;# log the warning about old geolocation data file&lt;/span&gt;
    &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;The GeoIp.dat file is old&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;renew_geoip&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;geoip_path&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

    &lt;span class="c"&gt;# connect to database, create the cursors &amp;amp; initialize the geolocation info&lt;/span&gt;
    &lt;span class="n"&gt;mysql_db&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;connect_db&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;host&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;password&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;database&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;cursor&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;mysql_db&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cursor&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;dict_cursor&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;mysql_db&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cursor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;MySQLdb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cursors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DictCursor&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;gip&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pygeoip&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GeoIP&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;geoip_path&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c"&gt;# optimize the database (instead a cron task in the server)&lt;/span&gt;
    &lt;span class="n"&gt;all_tables&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;tabl&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;tabl&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cursor&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;SHOW TABLES&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
    &lt;span class="n"&gt;cursor&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;OPTIMIZE TABLE {0}&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;, &amp;#39;&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;all_tables&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;

    &lt;span class="c"&gt;# Adds the timestamp field to the &amp;#39;access&amp;#39; table if no exists&lt;/span&gt;
    &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;New aux table field created&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;alter_table&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cursor&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;access&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

    &lt;span class="c"&gt;# Query the database and obtain the result. We collect the &amp;#39;access&amp;#39; table&lt;/span&gt;
    &lt;span class="c"&gt;# ips and ips from spammers reported by Mollom in &amp;#39;watchdog&amp;#39; table&lt;/span&gt;
    &lt;span class="c"&gt;# access = ({&amp;#39;timestamp&amp;#39;:timestamp, &amp;#39;mask&amp;#39;: &amp;#39;ip&amp;#39;}, ...)&lt;/span&gt;
    &lt;span class="c"&gt;# mollom = ({&amp;#39;timestamp&amp;#39;:timestamp, &amp;#39;mask&amp;#39;: &amp;#39;ip&amp;#39;}, ...)&lt;/span&gt;
    &lt;span class="n"&gt;access&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dict_cursor&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;&amp;quot;&amp;quot;SELECT mask, timestamp FROM access&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;mollom&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dict_cursor&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;&amp;quot;&amp;quot;SELECT hostname as mask, timestamp&lt;/span&gt;
&lt;span class="s"&gt;                                    FROM `watchdog`&lt;/span&gt;
&lt;span class="s"&gt;                                    WHERE `type` LIKE &amp;#39;%mollom%&amp;#39;&lt;/span&gt;
&lt;span class="s"&gt;                                    AND `message` LIKE &amp;#39;&lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s"&gt;pam:%&amp;#39;&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c"&gt;# From the &amp;#39;access&amp;#39; ips, select the ips blocked by this script from Mollom,&lt;/span&gt;
    &lt;span class="c"&gt;# discarding those introduced through the Drupal administration interface&lt;/span&gt;
    &lt;span class="c"&gt;# from_access = {&amp;#39;ip&amp;#39;:timestamp, ...}&lt;/span&gt;
    &lt;span class="n"&gt;from_access&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;a_row&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;access&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a_row&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;timestamp&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]):&lt;/span&gt;
            &lt;span class="n"&gt;from_access&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;a_row&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;mask&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;a_row&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;timestamp&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

    &lt;span class="c"&gt;# Here we select the ips that Mollom reported, if there are multiple&lt;/span&gt;
    &lt;span class="c"&gt;# occurrences of the same ip, we always choose the most recent&lt;/span&gt;
    &lt;span class="c"&gt;# from_mollom = {&amp;#39;ip&amp;#39;:timestamp, ...}&lt;/span&gt;
    &lt;span class="n"&gt;from_mollom&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;m_row&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;mollom&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;m_row&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;mask&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;from_mollom&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;from_mollom&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;m_row&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;mask&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]])&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;m_row&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;timestamp&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]):&lt;/span&gt;
                &lt;span class="n"&gt;from_mollom&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;m_row&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;mask&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;m_row&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;timestamp&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;from_mollom&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;m_row&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;mask&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;m_row&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;timestamp&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

    &lt;span class="c"&gt;# Now, from these ips, select the IPs of spammers that were not already&lt;/span&gt;
    &lt;span class="c"&gt;# banned and generate queries to insert into the &amp;#39;access&amp;#39; table. It&amp;#39;s&lt;/span&gt;
    &lt;span class="c"&gt;# necessary to check if some of ips reported through Mollom didn&amp;#39;t be&lt;/span&gt;
    &lt;span class="c"&gt;# already banned, because of how the Drupal&amp;#39;s event log works. The optional&lt;/span&gt;
    &lt;span class="c"&gt;# core module &amp;quot;Database logging&amp;quot; (which must be enabled to run his script)&lt;/span&gt;
    &lt;span class="c"&gt;# is deleting records by the tail (into the &amp;#39;watchdog&amp;#39; table) on each cron&lt;/span&gt;
    &lt;span class="c"&gt;# run, according to a maximum limit set in the admin menu. This limit may be&lt;/span&gt;
    &lt;span class="c"&gt;# 100, 1000, 10000, 100000, 1000000 records, as determined in the &amp;quot;Loggin&lt;/span&gt;
    &lt;span class="c"&gt;# and alerts -&amp;gt; Database logging&amp;quot; menu. Then depending on the record limit&lt;/span&gt;
    &lt;span class="c"&gt;# set in the &amp;#39;watchdog&amp;#39; table, the frequency with which you run the cron job&lt;/span&gt;
    &lt;span class="c"&gt;# and how often you run this script, it&amp;#39;s  very likely that in the previous&lt;/span&gt;
    &lt;span class="c"&gt;# query we have returned a number of ips that have not yet eliminated from&lt;/span&gt;
    &lt;span class="c"&gt;# the log (&amp;#39;watchdog&amp;#39;), but we have already added to the table of bannedd&lt;/span&gt;
    &lt;span class="c"&gt;# ips (&amp;#39;access&amp;#39;). This will avoid duplicate ips on table &amp;#39;access&amp;#39;&lt;/span&gt;
    &lt;span class="c"&gt;# ins_ips = [&amp;#39;ip0&amp;#39;, &amp;#39;ip1&amp;#39;, ...]&lt;/span&gt;
    &lt;span class="n"&gt;ins_ips&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;f_ip&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;f_ip&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;from_mollom&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;f_ip&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;from_access&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="n"&gt;query_str&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ins_qstr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i_ip&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;from_mollom&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i_ip&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i_ip&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;ins_ips&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c"&gt;# number of banned ips through this script&lt;/span&gt;
    &lt;span class="n"&gt;banned_ips&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;from_access&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ins_ips&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="c"&gt;# number of banned ips through Drupal administration interface&lt;/span&gt;
    &lt;span class="n"&gt;drupal_banned_ips&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;access&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;from_access&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c"&gt;# After a certain number of records in the table &amp;#39;access&amp;#39;, the website&amp;#39;s&lt;/span&gt;
    &lt;span class="c"&gt;# perfomance deteriorates and from an even larger number, the behavior of&lt;/span&gt;
    &lt;span class="c"&gt;# Drupal just become erratic. In the case of the site on which to run this&lt;/span&gt;
    &lt;span class="c"&gt;# script, we see a clear loss of performance from the 3000 records and&lt;/span&gt;
    &lt;span class="c"&gt;# becomes erratic over 5000. To avoid this unpleasant side effect, and&lt;/span&gt;
    &lt;span class="c"&gt;# that cure don&amp;#39;t be worse than the disease, I set a performance threshold&lt;/span&gt;
    &lt;span class="c"&gt;# in 2000 records, from which records were removed from the table. If the&lt;/span&gt;
    &lt;span class="c"&gt;# number of rows is greater than the performance threshold, we proceed to&lt;/span&gt;
    &lt;span class="c"&gt;# calculate the ips to remove, selecting the oldest. The number of ips to&lt;/span&gt;
    &lt;span class="c"&gt;# delete will be at least the 30% of &amp;quot;from_access&amp;quot;. Just delete records&lt;/span&gt;
    &lt;span class="c"&gt;# inserted through this script, never the inserted via Drupal admin&lt;/span&gt;
    &lt;span class="c"&gt;# interface&lt;/span&gt;
    &lt;span class="n"&gt;trigger&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;access&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;threshold&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c"&gt;# perfomance threshold&lt;/span&gt;

    &lt;span class="n"&gt;del_ips&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;latest&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;  &lt;span class="c"&gt;# ips to delete (if trigger) &amp;amp; latest ip&amp;#39;s date&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;trigger&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="c"&gt;# Now we&amp;#39;ll group the ips by date. Use the object collections.defauldict&lt;/span&gt;
        &lt;span class="c"&gt;# to group the ips in a dictionary of lists (values) of ips by date&lt;/span&gt;
        &lt;span class="c"&gt;# (keys)&lt;/span&gt;
        &lt;span class="c"&gt;# ips_by_time = {timestamp:[&amp;#39;ip0&amp;#39;, ..], ...}&lt;/span&gt;
        &lt;span class="n"&gt;ips_by_time&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;collections&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;defaultdict&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;fa_ip&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;from_access&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;ips_by_time&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;from_access&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;fa_ip&lt;/span&gt;&lt;span class="p"&gt;]]&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fa_ip&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="c"&gt;# We selected the oldest ips to have a number of them greater than or&lt;/span&gt;
        &lt;span class="c"&gt;# equal to 30% of blocked by this script&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;ips_date&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;sorted&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ips_by_time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;()):&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;del_ips&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;from_access&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
                &lt;span class="n"&gt;query_str&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;del_qstr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ips_date&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c"&gt;# delete by date, less queries&lt;/span&gt;
                &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;d_ip&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;ips_by_time&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;ips_date&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
                    &lt;span class="n"&gt;del_ips&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;d_ip&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                    &lt;span class="n"&gt;banned_ips&lt;/span&gt; &lt;span class="o"&gt;-=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
                &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ips_date&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;latest&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                    &lt;span class="n"&gt;latest&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ips_date&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="n"&gt;latest&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;strftime&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;%A &lt;/span&gt;&lt;span class="si"&gt;%x&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;localtime&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;latest&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

        &lt;span class="c"&gt;# log spammers&amp;#39; ips deleted from the table&lt;/span&gt;
        &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;Spammers&amp;#39; Ips deleted&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ip_and_country&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;del_ips&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;gip&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;Newest date of deleted IPs&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;Date: {0}&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;latest&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

    &lt;span class="c"&gt;# runs the database query&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;query_str&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;cursor&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;query_str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c"&gt;# close database cursors&lt;/span&gt;
    &lt;span class="n"&gt;cursor&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;close&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;dict_cursor&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;close&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="c"&gt;# log spammers&amp;#39; ips inserted into the table&lt;/span&gt;
    &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;Spammers&amp;#39; IPs inserted&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ip_and_country&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ins_ips&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;gip&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

    &lt;span class="c"&gt;# log total banned ips by origin&lt;/span&gt;
    &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;Banned IPs&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;Mollom: &lt;/span&gt;&lt;span class="si"&gt;%d&lt;/span&gt;&lt;span class="s"&gt; IPs&amp;#39;&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="n"&gt;banned_ips&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                            &lt;span class="s"&gt;&amp;#39;Drupal: &lt;/span&gt;&lt;span class="si"&gt;%d&lt;/span&gt;&lt;span class="s"&gt; IPs&amp;#39;&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="n"&gt;drupal_banned_ips&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
    &lt;span class="c"&gt;# log the end time&lt;/span&gt;
    &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;End Time&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c"&gt;# send the log by email&lt;/span&gt;
    &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;Ban Drupal Spammers. Ins: {0} Del: {1}&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;
             &lt;span class="n"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ins_ips&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;del_ips&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt; &lt;span class="n"&gt;send_from&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;from_addr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
             &lt;span class="n"&gt;dest_to&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;to_addrs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;mail_server&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;smtp_server&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;server_user&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;smtp_user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
             &lt;span class="n"&gt;server_pass&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;smtp_pass&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c"&gt;# write the log to a file&lt;/span&gt;
    &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;__name__&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;__main__&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;br /&gt;

&lt;hr /&gt;
&lt;h2 id="comentarios_realizados_anteriormente_en_drupal"&gt;Comentarios realizados anteriormente en Drupal&lt;/h2&gt;
&lt;div style="float:right; padding:2px; border: 1px solid #ccc; height:28px;"&gt;
&lt;a href="http://inseguridad.org/"&gt;&lt;img src="pictures/avtr_jbone.png" height=28 
width=28 alt="avatar" title="avatar de bjone"/&gt;&lt;/a&gt;&lt;/div&gt;

&lt;h3 id="muy_interesante"&gt;Muy interesante&lt;/h3&gt;
&lt;p&gt;por &lt;a href="http://inseguridad.org/"&gt;bjone&lt;/a&gt; el Jue, 14/10/2010 - 11:40&lt;/p&gt;
&lt;p&gt;muy interesante... voy a probar el mollom... gracias por la información.&lt;/p&gt;
&lt;hr /&gt;
&lt;div style="float:right; padding:2px; border: 1px solid #ccc; height:28px;"&gt;
&lt;img src="pictures/avtr_anonimo.png" height=28 width=28
alt="avatar" title="avatar de anónimo"/&gt;&lt;/div&gt;

&lt;h3 id="preguntita"&gt;Preguntita&lt;/h3&gt;
&lt;p&gt;por Anónimo el Mié, 27/10/2010 - 14:00&lt;/p&gt;
&lt;p&gt;Estoy armando mi sitio, que poseerá foro y tendré lo que entendí tu llamas 
hosting compartido. Es decir, me alquilaran espacio de hosting. La pregunta es: 
¿las precauciones contra el spam en mi sitio deberán correr exclusivamente por 
mi cuenta o parte de la pelea la lleva el administrador de hospedaje?&lt;/p&gt;
&lt;p&gt;Gracias.&lt;/p&gt;
&lt;hr /&gt;
&lt;div style="float:right; padding:2px; border: 1px solid #ccc; height:28px;"&gt;
&lt;img src="pictures/avtr_anonimo.png" height=28 width=28
alt="avatar" title="avatar de anónimo"/&gt;&lt;/div&gt;

&lt;h3 id="gracias_termin+_de_leer_el"&gt;Gracias, terminé de leer el&lt;/h3&gt;
&lt;p&gt;por Anónimo el Mié, 27/10/2010 - 14:03&lt;/p&gt;
&lt;p&gt;Gracias, terminé de leer el artículo y me respondí solo :)&lt;/p&gt;
&lt;p&gt;Si, parece que deberé hacerme cargo activamente :(.&lt;/p&gt;
&lt;hr /&gt;
&lt;div style="float:right; padding:2px; border: 1px solid #ccc; height:28px;"&gt;
&lt;a href="pages/sobre-mi.html"&gt;&lt;img src="pictures/avtr_joedicastro.png" height=28 
width=28 alt="avatar" title="avatar de joedicastro"/&gt;&lt;/a&gt;&lt;/div&gt;

&lt;h3 id="si_efectivamente_as+_es"&gt;Si, efectivamente así es,&lt;/h3&gt;
&lt;p&gt;por &lt;a href="http://joedicastro.com"&gt;joe di castro&lt;/a&gt; el Mié, 27/10/2010 - 20:04&lt;/p&gt;
&lt;p&gt;Si, efectivamente así es, pero además es así también en los servidores 
administrados y en los servidores dedicados.&lt;/p&gt;
&lt;p&gt;A lo sumo se dedican a administrar el hard, el sistema operativo y el sistema 
base para la web (Apache, Mysql/PostgreSQL, NGINX, PHP, ...). Pero en cuanto a 
la aplicación web en si misma y todo lo que a ella atañe, es la parte que te 
toca. Luego dependiendo de según donde acabes teniendo el hosting, en una 
situación determinada -de un ataque a la web por ejemplo- pueden desde echarte 
una mano de buena fe hasta exigirte que lo arregles o te cierran la cuenta (si 
no te la cierran directamente). Depende de con quien des y de la circunstancia 
que se dé, en la mayoría de hostings compartidos no se paran mucho a dar soporte 
a este tipo de situaciones, y si a penalizar a los que no gestionan 
correctamente sus sitios.&lt;/p&gt;
&lt;p&gt;De todos modos, si eliges un buen sistema de foros y aplicas un buen sistema 
antispam, no deberías tener excesivos problemas y acabaras aprendiendo mucho por 
el camino. La mayoría de los problemas vienen por la desidia y la poca 
preocupación de los administradores de webs por estos temas.&lt;/p&gt;
&lt;p&gt;Saludos y suerte con el foro.&lt;/p&gt;
&lt;hr /&gt;
&lt;div style="float:right; padding:2px; border: 1px solid #ccc; height:28px;"&gt;
&lt;a href="http://sigt.net/"&gt;&lt;img src="pictures/avtr_armonth.png" height=28 
width=28 alt="avatar" title="avatar de armonth"/&gt;&lt;/a&gt;&lt;/div&gt;

&lt;h3 id="sobre_el_spamicide"&gt;Sobre el Spamicide&lt;/h3&gt;
&lt;p&gt;por &lt;a href="http://sigt.net/archivo/sistema-antispam-del-campo-oculto-para-wordpress.xhtml"&gt;Armonth&lt;/a&gt; el Sáb, 30/10/2010 - 19:59 &lt;/p&gt;
&lt;p&gt;Buenas, yo ese sistema lo conocía por el de "campo oculto" y lo comenté hace ya 
más de 3 años en SigT (te he enlazado mi nombre al artículo) con la implementación.&lt;/p&gt;
&lt;p&gt;Un detalle que cabe mencionar es que es mucho más efectivo poner como campo "te 
echo atrás por spammer" el campo correspondiente a "nombre" originalmente en la 
implementación de WordPress (author) y poner un nuevo author que no poner un 
campo nuevo a ver si lo rellenan.&lt;/p&gt;
&lt;p&gt;La mayoría de scripts para spamear no están hechos para rellenar todos los 
campos, están hechos para rellenar el nombre (author), email, url y comment... 
ignorando otros campos...&lt;/p&gt;
&lt;hr /&gt;
&lt;div style="float:right; padding:2px; border: 1px solid #ccc; height:28px;"&gt;
&lt;a href="pages/sobre-mi.html"&gt;&lt;img src="pictures/avtr_joedicastro.png" height=28 
width=28 alt="avatar" title="avatar de joedicastro"/&gt;&lt;/a&gt;&lt;/div&gt;

&lt;h3 id="si_desde_luego_es_bastante"&gt;Si, desde luego es bastante&lt;/h3&gt;
&lt;p&gt;por &lt;a href="http://joedicastro.com"&gt;joe di castro&lt;/a&gt; el Sáb, 30/10/2010 - 22:05&lt;/p&gt;
&lt;p&gt;Si, desde luego es bastante más lógico hacerlo de esa manera, engañando 
doblemente a los spammers. De todos modos el modulo Spamicide te deja renombrar 
el campo como quieras y se puede hacer pasar por uno de esos campos sin 
problemas, por lo que se puede hacer lo que comentas.&lt;/p&gt;
&lt;p&gt;Ya conocía el articulo que enlazas, hace mucho tiempo que te sigo :), aunque los 
dos estamos muy inactivos últimamente.&lt;/p&gt;
&lt;p&gt;Saludos&lt;/p&gt;
&lt;hr /&gt;
&lt;div style="float:right; padding:2px; border: 1px solid #ccc; height:28px;"&gt;
&lt;a href="http://sigt.net/"&gt;&lt;img src="pictures/avtr_armonth.png" height=28 
width=28 alt="avatar" title="avatar de armonth"/&gt;&lt;/a&gt;&lt;/div&gt;

&lt;h3 id="bueno"&gt;Bueno&lt;/h3&gt;
&lt;p&gt;por &lt;a href="http://sigt.net/"&gt;Armonth&lt;/a&gt; el Sáb, 30/10/2010 - 22:57 &lt;/p&gt;
&lt;p&gt;Bueno, yo estoy "inactivo" de sigt que no de otro proyecto aún no revelado y que 
dejé los MMO ;P&lt;/p&gt;
&lt;div class="footnote"&gt;
&lt;hr /&gt;
&lt;ol&gt;
&lt;li id="fn:1"&gt;
&lt;p&gt;Las estadísticas de uso emplean los datos de drupal.org a 12 de Octubre 
de 2010&amp;#160;&lt;a href="#fnref:1" rev="footnote" title="Jump back to footnote 1 in the text"&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;</summary><category term="drupal"></category><category term="spam"></category><category term="python"></category><category term="mysql"></category><category term="mollom"></category><category term="script"></category></entry><entry><title>Optimizar imagénes para la web</title><link href="http://joedicastro.com/optimizar-imagenes-para-la-web.html" rel="alternate"></link><updated>2010-09-07T00:00:00+02:00</updated><author><name>joe di castro</name></author><id>http://joedicastro.com/optimizar-imagenes-para-la-web.html</id><summary type="html">&lt;p&gt;&lt;strong&gt;El peso&lt;/strong&gt; (tamaño) &lt;strong&gt;de las imágenes de un sitio web es determinante para&lt;/strong&gt; 
dos aspectos fundamentales: &lt;strong&gt;la velocidad de carga de las páginas y el consumo 
de ancho de banda.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;La velocidad de carga de las páginas es un factor cada día más importante, tanto
 para el posicionamiento de la web, como para la experiencia del usuario, que 
cada vez es más impaciente. El consumo de ancho de banda también es un
factor muy importante, en cuanto a que suele estar directamente relacionado con 
el coste económico del alojamiento del sitio.&lt;/p&gt;
&lt;p&gt;Y aunque generalmente los programas de retoque fotográfico más comunes 
incorporan ya opciones especiales para guardar imágenes para la web, no todos 
exprimen al máximo el potencial de eliminar información superflua de una
imagen (bueno, el EXIF puede no serlo tanto, dependiendo del sitio) sin que se 
vea afectada la calidad de la misma.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Yahoo&lt;/strong&gt;, en sus consejos para optimizar la velocidad de carga de un sitio web,
 &lt;strong&gt;"Yahoo Best Practices for Speeding Up Your Web Site"&lt;/strong&gt;, recomienda una serie 
de pequeñas aplicaciones en su &lt;a href="http://developer.yahoo.com/performance/rules.html#opt_images"&gt;apartado para optimizar las imágenes.&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Son una serie de ordenes de línea comandos que han de aplicarse a las imágenes, 
una por una, algo tedioso y poco optimo si nuestro sitio web tiene cientos o 
miles de imágenes.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Por eso he creado un script en Python,&lt;/strong&gt; &lt;em&gt;img4web.py&lt;/em&gt;, &lt;strong&gt;que automatiza todo 
el proceso y procesa todas las imágenes en formatos .png y .jpg que se 
encuentren en una carpeta.&lt;/strong&gt; &lt;/p&gt;
&lt;p&gt;Es muy sencillo de utilizar y al final del proceso tendremos una nueva carpeta 
"&lt;strong&gt;processed&lt;/strong&gt;" con todas las imágenes y un resumen del proceso como este:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;================================================================================  
                                    Summary                                       
================================================================================  
         Original            Processed           Save

.jpgs:   ( 31)  2.12 MiB     ( 31)  1.82 MiB     301.28 KiB  
.pngs:   ( 10)489.46 KiB     ( 10)368.93 KiB     120.53 KiB  
--------------------------------------------------------------------------------  
Total:   ( 41)  2.60 MiB     ( 41)  2.19 MiB     421.81 KiB
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;El script, las revisiones del código y las instrucciones de como descargarlo y 
usarlo podéis encontrarlas en &lt;a href="https://bitbucket.org/joedicastro/img4web/wiki/Leer_en_espanol"&gt;mi repositorio&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Funciona tanto en Linux como en Windows, no lo he probado en un Mac.&lt;/p&gt;
&lt;p&gt;El código del script, es el siguiente:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="c"&gt;#!/usr/bin/env python&lt;/span&gt;
&lt;span class="c"&gt;# -*- coding: &amp;lt;utf8&amp;gt; -*-&lt;/span&gt;

&lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="sd"&gt;    img4web.py: optimize .jpg and .png images for the web&lt;/span&gt;
&lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;

&lt;span class="c"&gt;#===============================================================================&lt;/span&gt;
&lt;span class="c"&gt;# This Script optimizes .jpg and .png images for the web.&lt;/span&gt;
&lt;span class="c"&gt;#&lt;/span&gt;
&lt;span class="c"&gt;# This follows the &amp;quot;Yahoo Best Practices for Speeding Up Your Web Site&amp;quot; about&lt;/span&gt;
&lt;span class="c"&gt;# optimize images. &lt;/span&gt;
&lt;span class="c"&gt;# http://developer.yahoo.com/performance/rules.html#opt_images&lt;/span&gt;
&lt;span class="c"&gt;# &lt;/span&gt;
&lt;span class="c"&gt;# Uses the program pngcrush and the command jpegtran of the libjpeg library&lt;/span&gt;
&lt;span class="c"&gt;# &lt;/span&gt;
&lt;span class="c"&gt;# pngcrush, http://pmt.sourceforge.net/pngcrush/&lt;/span&gt;
&lt;span class="c"&gt;# libjpg, http://www.ijg.org/&lt;/span&gt;
&lt;span class="c"&gt;#&lt;/span&gt;
&lt;span class="c"&gt;# In Linux they are usually available in the most popular distribution &lt;/span&gt;
&lt;span class="c"&gt;# repositories, e.g.: &lt;/span&gt;
&lt;span class="c"&gt;# In debian, Ubuntu as these packages:&lt;/span&gt;
&lt;span class="c"&gt;# pngcrush&lt;/span&gt;
&lt;span class="c"&gt;# libjpeg-progs&lt;/span&gt;
&lt;span class="c"&gt;#&lt;/span&gt;
&lt;span class="c"&gt;# In Windows pngcrush can be downloaded at &lt;/span&gt;
&lt;span class="c"&gt;# http://sourceforge.net/projects/pmt/files/pngcrush-executables/&lt;/span&gt;
&lt;span class="c"&gt;# and libjpeg can be downloaded (as gnuwin32) at &lt;/span&gt;
&lt;span class="c"&gt;# http://gnuwin32.sourceforge.net/downlinks/jpeg.php&lt;/span&gt;
&lt;span class="c"&gt;#&lt;/span&gt;
&lt;span class="c"&gt;# How it runs?&lt;/span&gt;
&lt;span class="c"&gt;#&lt;/span&gt;
&lt;span class="c"&gt;# Get a list of .jpg and .png images in the working directory (where script &lt;/span&gt;
&lt;span class="c"&gt;# runs) and process all of them one by one. Store the processed images in a new&lt;/span&gt;
&lt;span class="c"&gt;# subdirectory named &amp;#39;processed&amp;#39; (I know, I didn&amp;#39;t killed myself worrying about &lt;/span&gt;
&lt;span class="c"&gt;# the name)&lt;/span&gt;
&lt;span class="c"&gt;#===============================================================================&lt;/span&gt;

&lt;span class="c"&gt;#===============================================================================&lt;/span&gt;
&lt;span class="c"&gt;#    Copyright 2009 joe di castro &amp;lt;joe@joedicastro.com&amp;gt;&lt;/span&gt;
&lt;span class="c"&gt;#       &lt;/span&gt;
&lt;span class="c"&gt;#    This program is free software: you can redistribute it and/or modify&lt;/span&gt;
&lt;span class="c"&gt;#    it under the terms of the GNU General Public License as published by&lt;/span&gt;
&lt;span class="c"&gt;#    the Free Software Foundation, either version 3 of the License, or&lt;/span&gt;
&lt;span class="c"&gt;#    (at your option) any later version.&lt;/span&gt;
&lt;span class="c"&gt;#&lt;/span&gt;
&lt;span class="c"&gt;#    This program is distributed in the hope that it will be useful,&lt;/span&gt;
&lt;span class="c"&gt;#    but WITHOUT ANY WARRANTY; without even the implied warranty of&lt;/span&gt;
&lt;span class="c"&gt;#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the&lt;/span&gt;
&lt;span class="c"&gt;#    GNU General Public License for more details.&lt;/span&gt;
&lt;span class="c"&gt;#&lt;/span&gt;
&lt;span class="c"&gt;#    You should have received a copy of the GNU General Public License&lt;/span&gt;
&lt;span class="c"&gt;#    along with this program.  If not, see &amp;lt;http://www.gnu.org/licenses/&amp;gt;.&lt;/span&gt;
&lt;span class="c"&gt;#&lt;/span&gt;
&lt;span class="c"&gt;#===============================================================================&lt;/span&gt;

&lt;span class="n"&gt;__author__&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;joe di castro - joe@joedicastro.com&amp;quot;&lt;/span&gt;
&lt;span class="n"&gt;__license__&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;GNU General Public License version 2&amp;quot;&lt;/span&gt;
&lt;span class="n"&gt;__date__&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;30/12/2010&amp;quot;&lt;/span&gt;
&lt;span class="n"&gt;__version__&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;0.4&amp;quot;&lt;/span&gt;

&lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;sys&lt;/span&gt;
    &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;glob&lt;/span&gt;
    &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;os&lt;/span&gt;
    &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;platform&lt;/span&gt;
    &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;re&lt;/span&gt;
    &lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;subprocess&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Popen&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;PIPE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;call&lt;/span&gt;
&lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="ne"&gt;ImportError&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="c"&gt;# Checks the installation of the necessary python modules &lt;/span&gt;
    &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;linesep&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;join&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;An error found importing one module:&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;exc_info&lt;/span&gt;&lt;span class="p"&gt;()[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]),&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;You need to install it&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;Stopping...&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;]))&lt;/span&gt;
    &lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;exit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;best_unit_size&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bytes_size&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;Get a size in bytes &amp;amp; convert it to the best IEC prefix for readability.&lt;/span&gt;

&lt;span class="sd"&gt;    Return a dictionary with three pair of keys/values:&lt;/span&gt;

&lt;span class="sd"&gt;    &amp;#39;s&amp;#39; -- (float) Size of path converted to the best unit for easy read&lt;/span&gt;
&lt;span class="sd"&gt;    &amp;#39;u&amp;#39; -- (str) The prefix (IEC) for s (from bytes(2^0) to YiB(2^80))&lt;/span&gt;

&lt;span class="sd"&gt;    &amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;exp&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;90&lt;/span&gt; &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;bu_size&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;abs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bytes_size&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="nb"&gt;pow&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;2.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;exp&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bu_size&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;unit&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;bytes&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;KiB&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;MiB&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;GiB&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;40&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;TiB&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;PiB&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="mi"&gt;60&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;EiB&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;70&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;ZiB&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;80&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;YiB&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;}[&lt;/span&gt;&lt;span class="n"&gt;exp&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
            &lt;span class="k"&gt;break&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;s&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;bu_size&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;u&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;unit&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_size&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;the_path&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;Get size of a directory tree or a file in bytes.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
    &lt;span class="n"&gt;path_size&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;directories&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;files&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;walk&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;the_path&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;filename&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;files&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;path_size&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;lstat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;st_size&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;directory&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;directories&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;path_size&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;lstat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;directory&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;st_size&lt;/span&gt;
    &lt;span class="n"&gt;path_size&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;getsize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;the_path&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;path_size&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;check_execs_posix_win&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;progs&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;Check if the programs are installed.&lt;/span&gt;

&lt;span class="sd"&gt;    Returns two values:&lt;/span&gt;

&lt;span class="sd"&gt;    (dict) windows_paths - a dictionary of executables/paths (keys/values)&lt;/span&gt;
&lt;span class="sd"&gt;    (boolean) is_windows - True it&amp;#39;s a Windows OS, False it&amp;#39;s a *nix OS&lt;/span&gt;

&lt;span class="sd"&gt;    &amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;not_found&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot; If executable is not installed, exit and report.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
        &lt;span class="n"&gt;msg&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;The {0} program is necessary to run this script&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;exit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;windows_paths&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
    &lt;span class="n"&gt;is_windows&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;platform&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;system&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;Windows&amp;#39;&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;
    &lt;span class="c"&gt;# get all the drive unit letters if the OS is Windows&lt;/span&gt;
    &lt;span class="n"&gt;windows_drives&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;re&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;findall&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;r&amp;#39;(\w:)&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                                &lt;span class="n"&gt;Popen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;fsutil fsinfo drives&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;stdout&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;PIPE&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;
                                &lt;span class="n"&gt;communicate&lt;/span&gt;&lt;span class="p"&gt;()[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;is_windows&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;prog&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;progs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;is_windows&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="c"&gt;# Set all commands to search the executable in all drives&lt;/span&gt;
            &lt;span class="n"&gt;win_cmds&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;dir /B /S {0}\*{1}.exe&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;letter&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;prog&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt;
                        &lt;span class="n"&gt;letter&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;windows_drives&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
            &lt;span class="c"&gt;# Get the first location (usually in C:) of the all founded where &lt;/span&gt;
            &lt;span class="c"&gt;# the executable exists&lt;/span&gt;
            &lt;span class="n"&gt;exe_paths&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;join&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="n"&gt;Popen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cmd&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;stdout&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;PIPE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;stderr&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;PIPE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                                        &lt;span class="n"&gt;shell&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;communicate&lt;/span&gt;&lt;span class="p"&gt;()[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt;
                                        &lt;span class="n"&gt;cmd&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;win_cmds&lt;/span&gt;&lt;span class="p"&gt;]))&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;linesep&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
            &lt;span class="c"&gt;# Assign the path to the executable or report not found if empty&lt;/span&gt;
            &lt;span class="n"&gt;windows_paths&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;prog&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;exe_paths&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;exe_paths&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="n"&gt;not_found&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;prog&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="n"&gt;Popen&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="n"&gt;prog&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;--help&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;stdout&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;PIPE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;stderr&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;PIPE&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="ne"&gt;OSError&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="n"&gt;not_found&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;prog&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;windows_paths&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;is_windows&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;execs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;windows&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;Main section.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
    &lt;span class="c"&gt;# Check if exists the subdirectory for store the results, else create it&lt;/span&gt;
    &lt;span class="n"&gt;dest_path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;getcwd&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;processed&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;exists&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dest_path&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mkdir&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dest_path&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c"&gt;# Get the list of all .png an .jpg images in the current folder by type&lt;/span&gt;
    &lt;span class="n"&gt;jpg&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;png&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;glob&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;glob&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;*.jp[e|g]*&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;glob&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;glob&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;*.png&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c"&gt;# Get the original size of the images in bytes by type&lt;/span&gt;
    &lt;span class="n"&gt;org_jpg_sz&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;get_size&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;orig_jpg&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;orig_jpg&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;jpg&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="n"&gt;org_png_sz&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;get_size&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;orig_png&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;orig_png&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;png&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

    &lt;span class="c"&gt;# Get the executable&amp;#39;s names (and path for windows) of the needed programs &lt;/span&gt;
    &lt;span class="n"&gt;jpegtran&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;execs&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;jpegtran&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;windows&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;jpegtran&amp;#39;&lt;/span&gt;
    &lt;span class="n"&gt;pngcrush&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;execs&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;pngcrush&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;windows&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;pngcrush&amp;#39;&lt;/span&gt;

    &lt;span class="c"&gt;# Process all .jpg images&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;jpg_img&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;jpg&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;call&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="n"&gt;jpegtran&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;-copy&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;none&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;-optimize&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;-perfect&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;-outfile&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
              &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dest_path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;jpg_img&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;jpg_img&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;

    &lt;span class="c"&gt;# Process all .png images&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;png_img&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;png&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;call&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="n"&gt;pngcrush&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;-rem&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;alla&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;-reduce&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;-brute&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
              &lt;span class="n"&gt;png_img&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dest_path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;png_img&lt;/span&gt;&lt;span class="p"&gt;)])&lt;/span&gt;

    &lt;span class="c"&gt;# Get the size of the processed images in bytes by type&lt;/span&gt;
    &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;chdir&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dest_path&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;prc_jpg&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;j&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;j&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;glob&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;glob&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;*.jp[e|g]*&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;j&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;jpg&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="n"&gt;prc_png&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;glob&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;glob&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;*.png&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;png&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="n"&gt;prc_jpg_sz&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;get_size&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;new_j&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;new_j&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;prc_jpg&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="n"&gt;prc_png_sz&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;get_size&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;new_p&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;new_p&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;prc_png&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

    &lt;span class="c"&gt;# Get a human readable size&lt;/span&gt;
    &lt;span class="n"&gt;ojs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ops&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;best_unit_size&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;org_jpg_sz&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;best_unit_size&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;org_png_sz&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;pjs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;pps&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;best_unit_size&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;prc_jpg_sz&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;best_unit_size&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;prc_png_sz&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;tot_org&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;best_unit_size&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;org_jpg_sz&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;org_png_sz&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;tot_prc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;best_unit_size&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;prc_jpg_sz&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;prc_png_sz&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;sjs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;best_unit_size&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;org_jpg_sz&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;prc_jpg_sz&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;sps&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;best_unit_size&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;org_png_sz&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;prc_png_sz&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;tts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;best_unit_size&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;org_jpg_sz&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;org_png_sz&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;prc_jpg_sz&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;prc_png_sz&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

    &lt;span class="c"&gt;# print a little report    &lt;/span&gt;
    &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;{0}{1}{0}{2:^80}{0}{1}&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;linesep&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;=&amp;#39;&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;80&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;Summary&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;         Original            Processed           Save&amp;#39;&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;linesep&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;.jpgs:   ({6:3}){0:&amp;gt;6.2f} {1:8}({7:3}){2:&amp;gt;6.2f} {3:8}{4:&amp;gt;6.2f} {5}&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;
          &lt;span class="n"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ojs&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;s&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;ojs&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;u&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;pjs&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;s&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;pjs&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;u&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;sjs&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;s&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;sjs&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;u&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
                 &lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;jpg&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;prc_jpg&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
    &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;.pngs:   ({6:3}){0:&amp;gt;6.2f} {1:8}({7:3}){2:&amp;gt;6.2f} {3:8}{4:&amp;gt;6.2f} {5}&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;
          &lt;span class="n"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ops&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;s&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;ops&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;u&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;pps&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;s&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;pps&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;u&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;sps&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;s&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;sps&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;u&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
                 &lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;png&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;prc_png&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
    &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;-&amp;#39;&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;80&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;Total:   ({6:3}){0:&amp;gt;6.2f} {1:8}({7:3}){2:&amp;gt;6.2f} {3:8}{4:&amp;gt;6.2f} {5}&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;
          &lt;span class="n"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tot_org&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;s&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;tot_org&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;u&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;tot_prc&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;s&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;tot_prc&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;u&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
                 &lt;span class="n"&gt;tts&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;s&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;tts&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;u&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
                 &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;jpg&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;png&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;prc_jpg&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;prc_png&lt;/span&gt;&lt;span class="p"&gt;))))&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;__name__&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;__main__&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;WIN_EXECS&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;WIN_OS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;check_execs_posix_win&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;jpegtran&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;pngcrush&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;WIN_EXECS&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;WIN_OS&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Para una versión actualizada del código ir a &lt;a href="https://bitbucket.org/joedicastro/img4web/src/tip/src/img4web.py"&gt;el fichero fuente.&lt;/a&gt;&lt;/p&gt;</summary><category term="python"></category><category term="script"></category><category term="imagenes"></category><category term="yahoo best practices"></category><category term="pngcrush"></category><category term="jpegtran"></category></entry><entry><title>TED Talks, descargar videos y subtitulos de las charlas</title><link href="http://joedicastro.com/ted-talks-descargar-videos-y-subtitulos-de-las-charlas.html" rel="alternate"></link><updated>2010-07-29T22:01:00+02:00</updated><author><name>joe di castro</name></author><id>http://joedicastro.com/ted-talks-descargar-videos-y-subtitulos-de-las-charlas.html</id><summary type="html">&lt;p&gt;&lt;img alt="TED logo" src="http://joedicastro.com/pictures/ted_logo.png" title="TED logo" /&gt;&lt;/p&gt;
&lt;p&gt;Como introducción, para aquellos que aun no conozcan &lt;a href="http://www.ted.com/"&gt;TED&lt;/a&gt; &lt;strong&gt;(Technology, 
Enternaiment, Design)&lt;/strong&gt; y sus famosas charlas, &lt;strong&gt;TED Talks&lt;/strong&gt;, podría resumirlo 
como algo así como la versión en vídeo de las revistas de "divulgación 
científica y tecnológica" (acogiendo bajo esta denominación desde las más 
banales a las mas prestigiosas) que todos conocemos como &lt;a href="http://www.muyinteresante.es/"&gt;Muy Interesante&lt;/a&gt;, 
&lt;a href="http://www.investigacionyciencia.es/"&gt;Investigación y Ciencia&lt;/a&gt;, &lt;a href="http://www.mundo-geo.es/"&gt;Geo&lt;/a&gt;, &lt;a href="http://www.quo.es/"&gt;Quo&lt;/a&gt;, &lt;a href="http://www.nationalgeographic.com.es/"&gt;National Geographic&lt;/a&gt;, 
&lt;a href="http://www.popsci.com/"&gt;Popular Science&lt;/a&gt;, &lt;a href="http://www.nature.com/"&gt;Nature&lt;/a&gt;, &lt;a href="http://www.sciencemag.org/"&gt;Science&lt;/a&gt;, &lt;a href="http://www.thelancet.com/"&gt;Lancet&lt;/a&gt;,  etc... con 
la ventaja de la escasa distorsión del mensaje y lo conciso del mismo. Esto se 
debe a que no tenemos que pasar por el filtro del periodista (cuando no es un 
&lt;a href="http://es.wikipedia.org/wiki/Art%C3%ADculo_cient%C3%ADfico"&gt;paper&lt;/a&gt;), &lt;strong&gt;es el propio interesado&lt;/strong&gt; quien nos &lt;strong&gt;relata sus ideas 
personalmente&lt;/strong&gt; y que la gran mayoría de las charlas esta sometida a un 
&lt;strong&gt;tiempo limite de 18 minutos&lt;/strong&gt;, ya que es el tiempo máximo que se ha calculado 
que podemos estar centrados en un solo asunto sin perder el interés o la 
atención. Evidentemente no son comparables a un articulo amplio, detallado y 
bien redactado o a un buen documental, ni tampoco lo pretenden.
No se trata de profundizar en los temas, &lt;strong&gt;se trata de que&lt;/strong&gt; el ponente 
&lt;strong&gt;consiga transmitirte sus ideas, su entusiasmo y la trascendencia de las 
mismas&lt;/strong&gt;, y en la mayoría de los casos &lt;strong&gt;lo consiguen&lt;/strong&gt;, y algunas de estas 
charlas son de una calidad y de una trascendencia tales, que deberían tener 
lugar también en los medios tradicionales (TV). La calidad de estas charlas es 
tal que es difícil no quedarte impresionado (cuando no deslumbrado) al final de 
las mismas y contagiado del entusiasmo de los ponentes, la apertura de miras, 
conocimiento y humildad que te proporcionan (amen de ayudarte a creer un poquito 
más en la humanidad) no tiene parangón actualmente. Desde la serie &lt;a href="http://es.wikipedia.org/wiki/Cosmos:_un_viaje_personal"&gt;Cosmos&lt;/a&gt; 
de &lt;a href="http://es.wikipedia.org/wiki/Carl_Sagan"&gt;Carl Sagan&lt;/a&gt;, no he encontrado nada parecido que me invitara tanto a 
pensar y me abriera tanto la mente. Lo único que se acerca, entre la telebasura 
reinante, en el mundo audiovisual actual, es el programa
&lt;a href="http://www.rtve.es/television/redes/"&gt;Redes&lt;/a&gt; de &lt;a href="http://es.wikipedia.org/wiki/Eduardo_Punset"&gt;Eduardo Punset&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Para una definición más precisa, un extracto del &lt;a href="http://es.wikipedia.org/wiki/TED"&gt;articulo de la Wikipedia&lt;/a&gt;:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;TED&lt;/strong&gt; (&lt;strong&gt;Tecnología, Entretenimiento, Diseño&lt;/strong&gt;, del inglés: &lt;em&gt;Technology, 
Entertainment, Design&lt;/em&gt;) es una organización sin ánimo de lucro dedicada a las 
"ideas que vale la pena difundir" (del inglés: &lt;em&gt;Ideas worth spreading&lt;/em&gt;). TED es 
ampliamente conocida por su conferencia anual (&lt;strong&gt;TED Conference&lt;/strong&gt;) y sus charlas 
(&lt;strong&gt;TED Talks&lt;/strong&gt;) que cubren un amplio espectro de temas que incluyen &lt;strong&gt;ciencias, 
arte y diseño, política, educación, cultura, negocios, asuntos globales, 
tecnología y desarrollo, y entretenimiento&lt;/strong&gt;. Los conferenciantes han incluido 
a personas como el ex-Presidente de los Estados Unidos &lt;strong&gt;Bill Clinton&lt;/strong&gt;, los 
laureados con el Premio&lt;strong&gt; Nobel James D. Watson&lt;/strong&gt;, &lt;strong&gt;Murray Gell-Mann&lt;/strong&gt;, y Al 
Gore, el co-fundador de Microsoft, &lt;strong&gt;Bill Gates&lt;/strong&gt;, los co-fundadores de Google 
&lt;strong&gt;Sergey Brin&lt;/strong&gt; y &lt;strong&gt;Larry Page&lt;/strong&gt;, y &lt;strong&gt;Billy Graham&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Estas charlas se publican en su pagina web actualmente a un ritmo aproximado de 
una por día de semana (esto es, lunes a viernes). &lt;strong&gt;Actualmente (julio 2010) 
hay más de 700 charlas disponibles en su página y se han visionado más de 
290.000.000 de veces (julio 2010)&lt;/strong&gt;, para que os hagáis una idea de la 
repercusión y trascendencia de las mismas.&lt;/p&gt;
&lt;p&gt;El caso es que yo las sigo habitualmente, vamos que veo todas las que publican. 
Al principio me limitaba a enterarme de la publicación de las mismas por mi 
lector de fuentes RSS, veía el asunto de las mismas y si me parecía interesante, 
las veía directamente a través de su página web. Luego, cansado de los 
inconvenientes de verlas a través de un navegador web a pantalla completa, en 
flash desde Linux, dí un paso más y me pase a un programa como
&lt;strong&gt;&lt;a href="http://www.getmiro.com/"&gt;Miro&lt;/a&gt;&lt;/strong&gt;, de hecho hay una versión del mismo para &lt;strong&gt;TED&lt;/strong&gt;. Para el que no 
lo conozca, &lt;strong&gt;Miro&lt;/strong&gt; es un reproductor multimedia open source multiplataforma 
que cuenta con un agregador de fuentes RSS y torrents para podcasts de audio y 
vídeo. Así que él automáticamente se baja las charlas a medida que se van 
publicando y tu solo tienes que visionarlas desde el mismo, decidiendo después 
si las eliminas o las conservas.&lt;/p&gt;
&lt;p style="text-align: center;"&gt;&lt;img src="/pictures/miro_TED.jpg" 
alt="Miro TED Talks" title="Miro TED Talks" height="327" width="540"/&gt;&lt;/p&gt;

&lt;p&gt;Pero &lt;strong&gt;Miro&lt;/strong&gt; para mi tenia dos grandes problemas, el primero que no descarga 
los subtitulos, y el segundo que la visualización de estos es bastante pobre 
(al menos comparado con la configuración que tengo en mi &lt;a href="http://smplayer.sourceforge.net/"&gt;&lt;strong&gt;SMPlayer&lt;/strong&gt;&lt;/a&gt;). 
No es que los subtitulos me sean esenciales (todas las charlas son en ingles) 
pero si que ayudan mucho para el contexto de aquellas donde uno ni de lejos 
domina los tecnicismos de algunos campos, o porque no decirlo, cuando el
acento o la pronunciación de algunos de los ponentes me hace imposible 
seguirlos sin los mismos. Siempre veo los subtitulos en el idioma original, 
inglés, pero siempre descargo también la versión en español porque me es muy
útil en algunos momentos, como por ejemplo cuando se refieren a especies 
animales o vegetales de los que no conozco la traducción al español, y en lugar 
de tirar de diccionario continuamente, gano tiempo visionandolos en
castellano.&lt;/p&gt;
&lt;p&gt;Como uno ha de reconocer humildemente, rara es la vez que a uno no se le presente 
un problema que no se le haya presentado antes a otro y no haya dado con una 
solución valida. Luego en vez de reinventar la rueda (aunque finalmente es lo 
que haya acabado haciendo) me dirigí a San Google para encontrar una solución 
para descargar los subtitulos de las &lt;strong&gt;TED Talks&lt;/strong&gt;, y he aquí, que doy con 
&lt;a href="http://fci-h.blogspot.com/2010/05/python-script-to-download-ted-talks.html"&gt;este script&lt;/a&gt; en &lt;a href="http://python.org/"&gt;&lt;strong&gt;Python&lt;/strong&gt;&lt;/a&gt; (mi lenguaje de programación favorito). 
Lo empleé durante unos días (tres para ser exactos), pero no me acaba de 
convencer y me puse a modificarlo, y cuando estaba en ello, me dije: Que 
demonios! si al final no te convence &lt;strong&gt;Miro&lt;/strong&gt; para visionar los vídeos con 
subtitulos, porque no descargar también los mismos y utilizar el &lt;strong&gt;SMPlayer&lt;/strong&gt; 
para visionarlos? Así que me puse manos a la obra, y esto es lo que salio y 
actualmente empleo.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;El script hace uso de la fuente RSS de las TED Talks en calidad HD para 
conocer cuando se publica una nueva charla en la pagina&lt;/strong&gt;. Comprueba si no esta 
ya descargado, si no es así, lo descarga y luego comprueba la disponibilidad de 
los subtitulos (español e ingles) para las charlas ya descargadas que se 
encuentren en la carpeta. Si encuentra alguno disponible y no descargado 
anteriormente, lo convierte en un nuevo fichero .srt en la carpeta. Finalmente, 
si se ha descargado algo (charlas o subs), manda un correo a mi usuario local 
notificándomelo. Esta script lo tengo automatizado mediante una tarea cron en 
un pequeño servidor que tengo y que se ejecuta todas las mañanas a eso de las 
9:30 y que normalmente me descarga la charla del día anterior y los subtitulos 
en ingles de la misma y los de español de dos días antes. Esto depende mucho de 
la velocidad de traducción de los colaboradores, ya que a veces, cuando una 
charla es muy interesante, es posible que a las pocas horas dispongamos de los 
subtitulos en los dos idiomas.&lt;/p&gt;
&lt;p&gt;La parte fundamental del código del &lt;a href="https://bitbucket.org/joedicastro/ted-talks-download/src/tip/src/TEDTalks.py" title="TEDTalks.py"&gt;script&lt;/a&gt;, es la siguiente:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_sub&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tt_id&lt;/span&gt; &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tt_intro&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sub&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;Get TED Subtitle in JSON format &amp;amp; convert it to SRT Subtitle&lt;/span&gt;
&lt;span class="sd"&gt;    Obtiene el subtitulo de TED en formato JSON y lo convierte al formato SRT&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;srt_time&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tst&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;Format Time from TED Subtitles format to SRT time Format&lt;/span&gt;
&lt;span class="sd"&gt;        Convierte el formato de tiempo del subtitulo TED al formato de SRT&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
        &lt;span class="n"&gt;secs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;mins&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;hours&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;tst&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="mi"&gt;60&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tst&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;60000&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tst&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;3600000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;right_srt_time&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;{0:02d}:{1:02d}:{2:02d},000&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;hours&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;mins&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;secs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;right_srt_time&lt;/span&gt;

    &lt;span class="n"&gt;srt_content&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;&amp;#39;&lt;/span&gt;
    &lt;span class="n"&gt;sub_log&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;&amp;#39;&lt;/span&gt;
    &lt;span class="n"&gt;tt_url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;http://www.ted.com/talks&amp;#39;&lt;/span&gt;
    &lt;span class="n"&gt;sub_url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;{0}/subtitles/id/{1}/lang/{2}&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tt_url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tt_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sub&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
    &lt;span class="c"&gt;## Get JSON sub&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;FOUND&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;json_file&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Popen&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;wget&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;-q&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;-O&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;-&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sub_url&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
                          &lt;span class="n"&gt;stdout&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;PIPE&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;stdout&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;readlines&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;json_file&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;json_file&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;captions&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;status&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                    &lt;span class="n"&gt;json_file&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;remove&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;line&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;sub_log&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;Subtitle &amp;#39;{0}&amp;#39; not found.{1}&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sub&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;linesep&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;json_file&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;urllib2&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;urlopen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sub_url&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;readlines&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;json_object&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;loads&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;json_file&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;captions&amp;#39;&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;json_object&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;caption_idx&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;json_object&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;captions&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
                &lt;span class="n"&gt;sub_log&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;Subtitle &amp;#39;{0}&amp;#39; not available.{1}&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;
                            &lt;span class="n"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sub&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;linesep&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
            &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;caption&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;json_object&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;captions&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="n"&gt;start&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;tt_intro&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;caption&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;startTime&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
                &lt;span class="n"&gt;end&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;start&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;caption&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;duration&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
                &lt;span class="n"&gt;idx_line&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;{0}&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;caption_idx&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="n"&gt;time_line&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;{0} --&amp;gt; {1}&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;srt_time&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;start&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;srt_time&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;end&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
                &lt;span class="n"&gt;text_line&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;{0}&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;caption&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;content&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;utf-8&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
                &lt;span class="n"&gt;srt_content&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;join&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="n"&gt;idx_line&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;time_line&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;text_line&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
                &lt;span class="n"&gt;caption_idx&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
        &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;status&amp;#39;&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;json_object&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;sub_log&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;This is an error message returned by TED:{0}{0} - {1}&amp;quot;&lt;/span&gt;
                        &lt;span class="s"&gt;&amp;quot;{0}{0}Probably because the subtitle &amp;#39;{2}&amp;#39; is not &amp;quot;&lt;/span&gt;
                        &lt;span class="s"&gt;&amp;quot;available.{0}{0}{0}&amp;quot;&lt;/span&gt;
                        &lt;span class="s"&gt;&amp;quot;&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;linesep&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;json_object&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;status&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;message&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
                                  &lt;span class="n"&gt;sub&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

    &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="ne"&gt;ValueError&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;sub_log&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;Subtitle &amp;#39;{0}&amp;#39; it&amp;#39;s a malformed json file.{1}&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;
                    &lt;span class="n"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sub&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;linesep&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;srt_content&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sub_log&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;check_subs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ttalk&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;v_name&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;Check if the subtitles for the talk are downloaded, if not try to get&lt;/span&gt;
&lt;span class="sd"&gt;    them. Checks it for english and spanish languages&lt;/span&gt;
&lt;span class="sd"&gt;    Comprueba si los subtitulos para la charla estan descargados, si no, intenta&lt;/span&gt;
&lt;span class="sd"&gt;    obtenerlos. Lo comprueba para los idiomas español e ingles&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
    &lt;span class="c"&gt;## Get the names for the subtitles (for english and spanish languages) only&lt;/span&gt;
    &lt;span class="c"&gt;# if they not are already downloaded&lt;/span&gt;
    &lt;span class="n"&gt;subs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s_name&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;s_name&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt;
            &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;{0}.{1}.srt&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;v_name&lt;/span&gt;&lt;span class="p"&gt;[:&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;lang&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;lang&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;eng&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;spa&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;s_name&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;glob&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;glob&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;*.srt&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="n"&gt;s_log&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;&amp;#39;&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;sub&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;subs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="c"&gt;## Reads the talk web page, to search the talk&amp;#39;s intro duration&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;FOUND&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;tt_webpage&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Popen&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;wget&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;-q&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;-O&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;-&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                                &lt;span class="n"&gt;ttalk&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;feedburner_origlink&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
                                &lt;span class="n"&gt;stdout&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;PIPE&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;stdout&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;read&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;tt_webpage&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;urllib2&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;urlopen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ttalk&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;feedburner_origlink&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;read&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="n"&gt;tt_intro&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;search&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;introDuration:(\d+),&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tt_webpage&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;group&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="n"&gt;subtitle&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;get_log&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;get_sub&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ttalk&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;:&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;tt_intro&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sub&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;s_log&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;get_log&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;subtitle&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nb"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sub&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;w&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;srt_file&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="n"&gt;srt_file&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;subtitle&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;s_log&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;{0}{1} downloaded.{0}&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;linesep&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sub&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;s_log&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_video&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ttk&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;vid_url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;vid_name&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;Gets the TED Talk video&lt;/span&gt;
&lt;span class="sd"&gt;    Obtiene el video de la TED Talk&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;FOUND&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;Popen&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;wget&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;-q&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;-O&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;vid_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;vid_url&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;stdout&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;PIPE&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;stdout&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;read&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;urllib&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;urlretrieve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;vid_url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;vid_name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;v_log&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;{0} ({1})&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ttk&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;subtitle&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ttk&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;itunes_duration&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;v_log&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;{0}&lt;/span&gt;&lt;span class="se"&gt;\n\n&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;=&amp;#39;&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ttk&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;subtitle&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;11&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="n"&gt;v_log&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;{0}&lt;/span&gt;&lt;span class="se"&gt;\n\n&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ttk&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;feedburner_origlink&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;v_log&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;{0}&lt;/span&gt;&lt;span class="se"&gt;\n\n&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ttk&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;v_log&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;file://{0}&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;getcwd&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;vid_name&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="n"&gt;vid_size&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;best_unit_size&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;get_size&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;vid_name&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="n"&gt;v_log&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;{0:.2f} {1}&lt;/span&gt;&lt;span class="se"&gt;\n\n&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;vid_size&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;s&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;vid_size&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;u&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;v_log&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;main section&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;

&lt;span class="c"&gt;#===============================================================================&lt;/span&gt;
&lt;span class="c"&gt;# SCRIPT PARAMETERS&lt;/span&gt;
&lt;span class="c"&gt;#===============================================================================&lt;/span&gt;

    &lt;span class="c"&gt;## The directory to store the videos and subs.&lt;/span&gt;
    &lt;span class="c"&gt;# For Windows change the character &amp;#39;\&amp;#39; for the character &amp;#39;/&amp;#39;, I know is&lt;/span&gt;
    &lt;span class="c"&gt;# akward but is because how escape strings python&lt;/span&gt;
    &lt;span class="n"&gt;ttalk_vid_dir&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;/your/path/to/TED/Talks/Videos&amp;#39;&lt;/span&gt;

&lt;span class="c"&gt;#===============================================================================&lt;/span&gt;
&lt;span class="c"&gt;# END PARAMETERS&lt;/span&gt;
&lt;span class="c"&gt;#===============================================================================&lt;/span&gt;

    &lt;span class="c"&gt;# initalize the log&lt;/span&gt;
    &lt;span class="n"&gt;log&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Logger&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="c"&gt;# log the header&lt;/span&gt;
    &lt;span class="n"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;http://code.joedicastro.com/ted-talks-download&amp;#39;&lt;/span&gt;
    &lt;span class="n"&gt;msg&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;Download TED Talks from HD RSS Feed&amp;#39;&lt;/span&gt;
    &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;header&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c"&gt;# log the start time&lt;/span&gt;
    &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;Start Time&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;chdir&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;normpath&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ttalk_vid_dir&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

    &lt;span class="c"&gt;## Get a list of the current TED Talks downloaded in the dir&lt;/span&gt;
    &lt;span class="n"&gt;videos&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;glob&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;glob&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;*.mp4&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c"&gt;## Get the last download Talk video date&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nb"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;.data.pkl&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;rb&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;pkl_file&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;last&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pickle&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;load&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pkl_file&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ne"&gt;EOFError&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ne"&gt;IOError&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;pickle&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PickleError&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;last&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;localtime&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;86400&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;video_dates&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;

    &lt;span class="c"&gt;## The TED Talks HD RSS feed&lt;/span&gt;
    &lt;span class="n"&gt;ttalk_feed_url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;http://feeds.feedburner.com/tedtalksHD&amp;#39;&lt;/span&gt;
    &lt;span class="n"&gt;ttalk_feed&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;feedparser&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ttalk_feed_url&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c"&gt;## If the feed is erroneous or occurs a http or network error, log and exit!&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;ttalk_feed&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;bozo&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;An error occurred&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ttalk_feed&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;bozo_exception&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;WIN_OS&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;Download TED Talks&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;exit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c"&gt;## If correct, process the feed entries&lt;/span&gt;
    &lt;span class="n"&gt;vids_log&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;subs_log&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;&amp;#39;&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;ttalk_entrie&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;ttalk_feed&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;entries&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="c"&gt;## Get The video url and name&lt;/span&gt;
        &lt;span class="n"&gt;tt_vid_url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ttalk_entrie&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;enclosures&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;href&lt;/span&gt;
        &lt;span class="n"&gt;tt_vid_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;tt_vid_url&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;/&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="c"&gt;## If the video is new, download it!&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;ttalk_entrie&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;updated_parsed&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;last&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;tt_vid_name&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;videos&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;vids_log&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;get_video&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ttalk_entrie&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tt_vid_url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tt_vid_name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;videos&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tt_vid_name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;video_dates&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ttalk_entrie&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;updated_parsed&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="c"&gt;## If video is already downloaded, check if subs exists, if not, get it!&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;tt_vid_name&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;videos&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;subs_log&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;check_subs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ttalk_entrie&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tt_vid_name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;Talks downloaded&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;vids_log&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;Subs downloaded&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;subs_log&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;

    &lt;span class="c"&gt;## Set the last download video date&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;video_dates&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;last&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;max&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;video_dates&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nb"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;.data.pkl&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;wb&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;output&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;pickle&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dump&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;last&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;output&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;End time&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="c"&gt;## If logs any activity, sends the information mail&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;WIN_OS&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;Download TED Talks&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;__name__&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;__main__&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;WIN_OS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;platform&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;system&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;Windows&amp;#39;&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;WIN_OS&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;FOUND&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;check_exec_posix&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;wget&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Y un ejemplo de un correo enviado por el mismo quedaría así:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;De:     youruser@yourcomputer
Para:     youruser@yourcomputer
Asunto:     Download TED Talks - Thursday 07/29/10, 17:04:52
Fecha:     Thu, 29 Jul 2010 17:04:52 +0200

New TED Talk downloaded!
========================
http://www.ted.com/talks/view/id/927

Laurie Santos: A monkey economy as irrational as ours (00:19:45)

Laurie Santos looks for the roots of human irrationality by watching the way
our primate relatives make decisions. A clever series of experiments in
&amp;quot;monkeynomics&amp;quot; shows that some of the silly choices we make, monkeys make too.

file:///your/path/to/TEDTalks/LaurieSantos_2010G_480.mp4
240.7 MiB

SusanShaw_2010X_480.eng.srt downloaded
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Pudiendo acceder al vídeo directamente desde el correo, y si se encontraran los 
subtítulos para el mismo ya disponibles, visualizarlos automáticamente.&lt;/p&gt;
&lt;p&gt;El que el script esté fundamentalmente en ingles (la mayoría de comentarios y 
los nombres de las variables)  es que me siento más cómodo de esta manera y 
ayuda a que un espectro más amplio de personas sea capaz de comprenderlo. Si 
este script le sirve a alguien más o le inspira para crear el suyo propio, pues 
habrá valido la pena el publicarlo. El resto del script, &lt;strong&gt;TEDTalks.py&lt;/strong&gt; está 
disponible en &lt;a href="https://bitbucket.org/joedicastro/ted-talks-download/wiki/Leer_en_espanol"&gt;mi repositorio de bickbucket&lt;/a&gt;, al igual que instrucciones 
ampliadas de su uso.&lt;/p&gt;
&lt;p&gt;Finalmente como ejemplo, publico una de las charlas más impactantes este año y 
ganadora del &lt;strong&gt;TED Prize&lt;/strong&gt;, &lt;a href="http://www.ted.com/talks/lang/spa/jamie_oliver.html"&gt;Jamie Oliver: Enseñarle a todos los niños acerca de 
la comida&lt;/a&gt;&lt;/p&gt;
&lt;div&gt;&lt;p style="text-align: center;"&gt;&lt;object data="http://video.ted.com/assets/player/swf/EmbedPlayer.swf" type="application/x-shockwave-flash" height="326" width="446"&gt;&lt;param name="data" value="http://video.ted.com/assets/player/swf/EmbedPlayer.swf"&gt;&lt;param name="allowFullScreen" value="true"&gt;&lt;param name="allowScriptAccess" value="always"&gt;&lt;param name="wmode" value="transparent"&gt;&lt;param name="bgColor" value="#ffffff"&gt;&lt;param name="flashvars" value="vu=http://video.ted.com/talks/dynamic/JamieOliver_2010-medium.mp4&amp;amp;su=http://images.ted.com/images/ted/tedindex/embed-posters/JamieOliver-2010.embed_thumbnail.jpg&amp;amp;vw=432&amp;amp;vh=240&amp;amp;ap=0&amp;amp;ti=765&amp;amp;introDuration=15330&amp;amp;adDuration=4000&amp;amp;postAdDuration=830&amp;amp;adKeys=talk=jamie_oliver;year=2010;theme=a_taste_of_ted2010;theme=ted_prize_winners;event=TED2010;&amp;amp;preAdTag=tconf.ted/embed;tile=1;sz=512x288;"&gt;&lt;param name="src" value="http://video.ted.com/assets/player/swf/EmbedPlayer.swf"&gt;&lt;param name="bgcolor" value="#ffffff"&gt;&lt;param name="allowfullscreen" value="true"&gt;&lt;/object&gt;&lt;/p&gt;&lt;/div&gt;

&lt;br /&gt;

&lt;hr /&gt;
&lt;h2 id="comentarios_realizados_anteriormente_en_drupal"&gt;Comentarios realizados anteriormente en Drupal&lt;/h2&gt;
&lt;div style="float:right; padding:2px; border: 1px solid #ccc; height:28px;"&gt;
&lt;a href="http://www.elnaranjal.com.ve/"&gt;&lt;img src="pictures/avtr_dennis.png" 
height=28 width=28 alt="avatar" title="avatar de Dennis"/&gt;&lt;/a&gt;&lt;/div&gt;

&lt;h3 id="excelente"&gt;Excelente!&lt;/h3&gt;
&lt;p&gt;por &lt;a href="http://www.elnaranjal.com.ve/"&gt;Dennis&lt;/a&gt; el Sáb, 31/07/2010 - 21:12 &lt;/p&gt;
&lt;p&gt;Amigo, he leído con atención su articulo acerca de los subtítulos. El nivel de 
automatización alcanzado resulta sorprendente, sin embargo, para aquellos que no 
contamos con el hardware necesario se torna complicada la cosa.&lt;/p&gt;
&lt;p&gt;En mi caso, me interesa descargar algunas (no todas) las charlas. De hecho, 
quisiera unas pocas, cuidadosamente seleccionadas, para presentarlas en el 
colegio en el cual doy clases a mis alumnos de secundaria.&lt;/p&gt;
&lt;p&gt;¿Conoce Ud. de alguna forma de descargar en un formato legible por VLC (.SUB o 
.SRT, por ejemplo) los subtítulos en español de charlas seleccionadas? He estado 
tratando de conseguir algunos en forma infructuosa desde hace algún tiempo, 
aunque he conseguido algunos en formato JSON.&lt;/p&gt;
&lt;p&gt;Gracias mil por la ayuda con este tema.&lt;/p&gt;
&lt;hr /&gt;
&lt;div style="float:right; padding:2px; border: 1px solid #ccc; height:28px;"&gt;
&lt;a href="pages/sobre-mi.html"&gt;&lt;img src="pictures/avtr_joedicastro.png" height=28 
width=28 alt="avatar" title="avatar de joedicastro"/&gt;&lt;/a&gt;&lt;/div&gt;

&lt;h3 id="script_para_subtitulos_de_una_charla"&gt;Script para subtitulos de una charla&lt;/h3&gt;
&lt;p&gt;por &lt;a href="http://joedicastro.com"&gt;joe di castro&lt;/a&gt; el Dom, 01/08/2010 - 01:38&lt;/p&gt;
&lt;p&gt;Bueno, para conseguir eso que me comentas, he modificado el script anterior un 
poco y he creado este &lt;a href="https://bitbucket.org/joedicastro/ted-talks-download/src/tip/src/TEDSubs.py"&gt;&lt;strong&gt;nuevo script&lt;/strong&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;La forma de utilizarlo es muy sencilla, por ejemplo para bajarse la charla de 
Jamie Oliver, simplemente en la linea de comandos escribes&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="go"&gt;python TEDSubs.py http://www.ted.com/talks/lang/eng/jamie_oliver.html&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Y el te baja tanto los subtítulos como el vídeo. Si quisieras bajarte solo los 
subtítulos, bastaría con que añadieras la opción &lt;code&gt;-s&lt;/code&gt; ó &lt;code&gt;--only_subs&lt;/code&gt;, quedaría 
así:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="go"&gt;python TEDSubs.py -s  http://www.ted.com/talks/lang/eng/jamie_oliver.html&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Como ves es muy sencillo, solo tendrías que sustituir la dirección de la charla 
de Oliver por la que deseas bajar.&lt;/p&gt;
&lt;p&gt;Espero que te sirva.&lt;/p&gt;
&lt;p&gt;Saludos&lt;/p&gt;
&lt;hr /&gt;
&lt;div style="float:right; padding:2px; border: 1px solid #ccc; height:28px;"&gt;
&lt;img src="pictures/avtr_ignacio.png" height=28 width=28
alt="avatar" title="avatar de Ignacio"/&gt;&lt;/div&gt;

&lt;h3 id="ted"&gt;TED&lt;/h3&gt;
&lt;p&gt;por Ignacio el Mié, 04/08/2010 - 01:02 &lt;/p&gt;
&lt;p&gt;Estimado muy bueno lo que haces, me parecen excelentes los vídeos y me gustaría 
contar con ellos. ¿Donde corro el script?¿podrías subírmelos con subtítulos a 
algún gestor o ftp?&lt;/p&gt;
&lt;p&gt;Muchas gracias&lt;/p&gt;
&lt;hr /&gt;
&lt;div style="float:right; padding:2px; border: 1px solid #ccc; height:28px;"&gt;
&lt;a href="pages/sobre-mi.html"&gt;&lt;img src="pictures/avtr_joedicastro.png" height=28 
width=28 alt="avatar" title="avatar de joedicastro"/&gt;&lt;/a&gt;&lt;/div&gt;

&lt;h3 id="vamos_por_partes"&gt;Vamos por partes...&lt;/h3&gt;
&lt;p&gt;por &lt;a href="http://joedicastro.com"&gt;joe di castro&lt;/a&gt; el Jue, 05/08/2010 - 19:47&lt;/p&gt;
&lt;p&gt;Vamos por partes...&lt;/p&gt;
&lt;p&gt;Los scripts los ejecutas en tu ordenador, lo único necesario es el lenguaje de 
programación &lt;strong&gt;Python&lt;/strong&gt; y una conexión a Internet. Si tu maquina corre en linux, 
el python lo tienes instalado por defecto en casi todas las distribuciones. Si 
corre en Windows o Mac, lo puedes descargar en &lt;a href="http://www.python.org/download/"&gt;&lt;strong&gt;Python.org&lt;/strong&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Para correr los scripts, en la linea de comandos solo necesitas ejecutar estas 
lineas de comando:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="go"&gt;python TEDTalks.py&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;para el primer script o las lineas de comando ya comentadas anteriormente para 
el segundo script.&lt;/p&gt;
&lt;p&gt;Para descargarlos, puedes o bien copiar y pegar el código tal y como aparece en 
el articulo, o bien descargarlos:&lt;/p&gt;
&lt;p&gt;&lt;a href="https://bitbucket.org/joedicastro/ted-talks-download/get/tip.zip"&gt;&lt;strong&gt;TEDTalks.py &amp;amp; TEDSubs.py&lt;/strong&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;o bien vas directamente al mi &lt;strong&gt;&lt;a href="https://bitbucket.org/joedicastro/ted-talks-download"&gt;repositorio en bitbucket&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Eso si, hay que tener en cuenta que en el caso del primer script, este solo se 
baja el vídeo del dia anterior en su primera ejecución o bien el más reciente no 
descargado en caso de ejecuciones sucesivas. Y como la gente de &lt;strong&gt;TED&lt;/strong&gt; parece 
que se ha tomado un descanso en Agosto (el último vídeo es del 30 de Julio), 
este no descargara nada, porque de momento no hay nada que descargar. Hasta que 
no retornen la publicación de nuevas charlas, el primer script no bajara nuevos 
contenidos, igual que los tradicionales lectores RSS o el mismo Miro.&lt;/p&gt;
&lt;p&gt;Saludos&lt;/p&gt;
&lt;hr /&gt;
&lt;div style="float:right; padding:2px; border: 1px solid #ccc; height:28px;"&gt;
&lt;img src="pictures/avtr_daniel.png" height=28 width=28
alt="avatar" title="avatar de Daniel"/&gt;&lt;/div&gt;

&lt;h3 id="gracias"&gt;Gracias&lt;/h3&gt;
&lt;p&gt;por Daniel el Mié, 22/09/2010 - 04:38 &lt;/p&gt;
&lt;p&gt;Hermano, muchas gracias.. excelente script.. ya te imaginaras lo importante que 
es que para muchos, poder adquirir estas excelente conferencias. Gracias de 
nuevo&lt;/p&gt;
&lt;hr /&gt;
&lt;div style="float:right; padding:2px; border: 1px solid #ccc; height:28px;"&gt;
&lt;img src="pictures/avtr_xavier.png" height=28 width=28
alt="avatar" title="avatar de Xavier"/&gt;&lt;/div&gt;

&lt;h3 id="funcionan_los_subtitulos"&gt;Funcionan los Subtitulos?&lt;/h3&gt;
&lt;p&gt;por Xavier el Mié, 24/11/2010 - 00:35 &lt;/p&gt;
&lt;p&gt;Hola,&lt;/p&gt;
&lt;p&gt;muchas gracias por disponibilizar los scripts.&lt;/p&gt;
&lt;p&gt;Estoy intentando usar el segundo script (para bajar vídeos concretos) pero solo 
me baja el vídeo. Si lo uso con -s no me baja nada (tampoco devuelve ningún 
mensaje de error.&lt;/p&gt;
&lt;p&gt;Podría ser que hayan cambiado la manera de bajar los subtítulos?&lt;/p&gt;
&lt;p&gt;gracias por adelantado&lt;/p&gt;
&lt;p&gt;Xavier&lt;/p&gt;
&lt;hr /&gt;
&lt;div style="float:right; padding:2px; border: 1px solid #ccc; height:28px;"&gt;
&lt;a href="pages/sobre-mi.html"&gt;&lt;img src="pictures/avtr_joedicastro.png" height=28 
width=28 alt="avatar" title="avatar de joedicastro"/&gt;&lt;/a&gt;&lt;/div&gt;

&lt;h3 id="si_funcionan_pero"&gt;Si, funcionan, pero...&lt;/h3&gt;
&lt;p&gt;por &lt;a href="http://joedicastro.com"&gt;joe di castro&lt;/a&gt; el Mié, 24/11/2010 - 09:28&lt;/p&gt;
&lt;p&gt;Si, funcionan, pero a veces los subtítulos aún no están disponibles, o peor, el 
fichero original en formato json es defectuoso. Lo de que el formato json sea 
defectuoso suele pasar de vez en cuando y generalmente acaban
solucionándolo al cabo de unos días. Debe de ser algún defecto en la traducción 
o al subir el fichero.&lt;/p&gt;
&lt;p&gt;De todos modos ya he modificado el script para que te muestre un mensaje de 
error en caso de que no esté disponible o este defectuoso, e indicado si es el 
subtitulo en español o ingles. Puedes descargarlo de aquí o del repositorio. 
Gracias por informarme del error, se me había escapado el proporcionar esta 
información.&lt;/p&gt;
&lt;p&gt;Y voy a mirar la manera de convertir igualmente el subtitulo de json a srt 
aunque el json esté malformado siempre que sea posible. No será muy difícil.&lt;/p&gt;
&lt;p&gt;Saludos&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Actualización&lt;/strong&gt;: Bueno, he encontrado un fallo, a veces cuando la página 
termina un cierto tiempo en responder, no cargaba algunos ficheros json de 
subtítulos completos, y lo interpretaba como un fichero json defectuoso. Ya he 
corregido el error, substituyendo la función que empleaba para la descarga por 
otra que siempre descarga el fichero completo.&lt;/p&gt;
&lt;hr /&gt;
&lt;div style="float:right; padding:2px; border: 1px solid #ccc; height:28px;"&gt;
&lt;a href="pages/sobre-mi.html"&gt;&lt;img src="pictures/avtr_joedicastro.png" height=28 
width=28 alt="avatar" title="avatar de joedicastro"/&gt;&lt;/a&gt;&lt;/div&gt;

&lt;h3 id="bueno_ya_estan_incorporadas"&gt;Bueno, ya estan incorporadas&lt;/h3&gt;
&lt;p&gt;por &lt;a href="http://joedicastro.com"&gt;joe di castro&lt;/a&gt; el Mié, 24/11/2010 - 23:26&lt;/p&gt;
&lt;p&gt;Bueno, ya están incorporadas las mejoras también al otro script. En principio no 
veo necesario el crear el método para los ficheros JSON defectuosos, en parte 
porque sería replicar más o menos lo que ya hace el modulo json y en parte 
porque los JSON defectuosos provenían del empleo de la librería de python urllib 
para descargarlos, lo que a veces por timeouts no conseguía hacerlo del todo. El 
emplear la librería urllib2 soluciona este problema puesto que si los descarga 
al completo.&lt;/p&gt;
&lt;p&gt;De todos modos ahora se reportan todos los códigos de error que aparezcan, tanto 
los que genero yo en el script, como los originales generados por TED (en 
realidad dotSUB, que es el que proporciona los subtítulos para TED) y les haré 
un seguimiento. Porque puede que si estén cambiando algo, dados los errores que 
reportan algunos de los últimos vídeos.&lt;/p&gt;
&lt;p&gt;Saludos y gracias por reportarme el fallo, Xavier.&lt;/p&gt;
&lt;hr /&gt;
&lt;div style="float:right; padding:2px; border: 1px solid #ccc; height:28px;"&gt;
&lt;img src="pictures/avtr_xavier.png" height=28 width=28
alt="avatar" title="avatar de Xavier"/&gt;&lt;/div&gt;

&lt;h3 id="aun_tengo_algun_problemilla"&gt;Aun tengo algun problemilla&lt;/h3&gt;
&lt;p&gt;por Xavier el Jue, 25/11/2010 - 00:40&lt;/p&gt;
&lt;p&gt;Muchas gracias por tan rápida respuesta. Me he copiado el código para bajar los 
vídeos individuales, pero aun tengo problemas. Esta vez parece como si me 
faltara algún modulo de python, aunque no se ni cual ni como debo actualizarlo. 
Tengo el siguiente error:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="gt"&gt;Traceback (most recent call last):&lt;/span&gt;
  File &lt;span class="nb"&gt;&amp;quot;./TEDVideos.py&amp;quot;&lt;/span&gt;, line &lt;span class="m"&gt;184&lt;/span&gt;, in &lt;span class="n"&gt;&amp;lt;module&amp;gt;&lt;/span&gt;
    &lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  File &lt;span class="nb"&gt;&amp;quot;./TEDVideos.py&amp;quot;&lt;/span&gt;, line &lt;span class="m"&gt;172&lt;/span&gt;, in &lt;span class="n"&gt;main&lt;/span&gt;
    &lt;span class="n"&gt;ttalk_webpage&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;group&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; 
&lt;span class="gr"&gt;AttributeError&lt;/span&gt;: &lt;span class="n"&gt;&amp;#39;NoneType&amp;#39; object has no attribute &amp;#39;group&amp;#39;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;(El programa TEDVideos.py es el nombre que le he dado yo a tu código)&lt;/p&gt;
&lt;p&gt;muchas gracias de antemano&lt;/p&gt;
&lt;hr /&gt;
&lt;div style="float:right; padding:2px; border: 1px solid #ccc; height:28px;"&gt;
&lt;a href="pages/sobre-mi.html"&gt;&lt;img src="pictures/avtr_joedicastro.png" height=28 
width=28 alt="avatar" title="avatar de joedicastro"/&gt;&lt;/a&gt;&lt;/div&gt;

&lt;h3 id="no_en_este_caso_no_te_falta"&gt;No, en este caso no te falta&lt;/h3&gt;
&lt;p&gt;por &lt;a href="http://joedicastro.com"&gt;joe di castro&lt;/a&gt; el Jue, 25/11/2010 - 10:12&lt;/p&gt;
&lt;p&gt;No, en este caso no te falta ningún modulo para ejecutar el script, eso es algo 
que ya tengo contemplado y es lo primero que compruebo, y lanzo un aviso del 
modulo que es necesario para ejecutarlo si no se encuentra.&lt;/p&gt;
&lt;p&gt;El error que me muestras es porque intento seleccionar uno de entre los 
resultados de una búsqueda de datos dentro de una página web, y al ser el 
resultado de la búsqueda vacío arroja ese error. Es un error que tampoco tenía
contemplado porque asumía que en un principio las direcciones web introducidas 
serían correctas, algo que voy a contemplar a partir de ahora, para avisar al 
usuario.&lt;/p&gt;
&lt;p&gt;Ese error te lo da porque no encuentra lo que busca, el identificador único de 
la charla, en la página web que le proporcionas. Lo raro es que si fuera una 
dirección web incorrecta, el error tendría que producirse antes, cuando busca 
la duración de la introducción previa al vídeo. Lo he probado con unas 25 
charlas diferentes y no he conseguido reproducir el error. La mejora forma de 
entender lo que ha pasado es que me proporciones la dirección web que te da el 
error para comprobar lo que ocurre.&lt;/p&gt;
&lt;p&gt;Saludos&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Actualización&lt;/strong&gt;: He añadido mensajes de error para contemplar estas 
posibilidades, así será más fácil diagnosticar los fallos.&lt;/p&gt;
&lt;hr /&gt;
&lt;div style="float:right; padding:2px; border: 1px solid #ccc; height:28px;"&gt;
&lt;img src="pictures/avtr_xavier.png" height=28 width=28
alt="avatar" title="avatar de Xavier"/&gt;&lt;/div&gt;

&lt;h3 id="en_efecto_no_encuentra_la_p+gina"&gt;En efecto, no encuentra la página&lt;/h3&gt;
&lt;p&gt;por Xavier el Jue, 25/11/2010 - 12:01 &lt;/p&gt;
&lt;p&gt;Tal y como predices, no parece encontrar la pagina buscada. Ahora me lanza este 
error:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;Some data not found in this URL:

http://www.ted.com/talks/lang/eng/jamie_oliver.html

Please report this error and provides the URL to check at:

http://code.joedicastro.com/ted-talks-download/issues/new

Thanks for helping to fix errors.
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Lo curioso es que estoy probándolo con el ejemplo que tu nos proporcionas:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="go"&gt;TEDSubs.py -s  http://www.ted.com/talks/lang/eng/jamie_oliver.html&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Para asegurarme que tengo conectividad desde mi ordenador he intentado ir a la 
pagina web en un browser (sale ok) y he intentado bajarla con wget (baja ok).&lt;/p&gt;
&lt;p&gt;No creo que afecte, pero estoy usando OS X 10.6&lt;/p&gt;
&lt;p&gt;gracias&lt;/p&gt;
&lt;p&gt;Xavier&lt;/p&gt;
&lt;hr /&gt;
&lt;div style="float:right; padding:2px; border: 1px solid #ccc; height:28px;"&gt;
&lt;a href="pages/sobre-mi.html"&gt;&lt;img src="pictures/avtr_joedicastro.png" height=28 
width=28 alt="avatar" title="avatar de joedicastro"/&gt;&lt;/a&gt;&lt;/div&gt;

&lt;h3 id="bueno_teniendo_en_cuenta_los"&gt;Bueno, teniendo en cuenta los&lt;/h3&gt;
&lt;p&gt;por &lt;a href="http://joedicastro.com"&gt;joe di castro&lt;/a&gt; el Jue, 25/11/2010 - 12:33&lt;/p&gt;
&lt;p&gt;Bueno, teniendo en cuenta los errores que te da ahora y que al principio si 
podías bajar el vídeo, el problema está en el cambio que hice de urllib a 
urllib2, pero si el segundo te da este error, el primero no te bajaba los 
subtítulos. He mirado googleando un poco y si parece haber algunos problemas 
con estas librerías y MAC OSX, es algo que no puedo corroborar directamente 
puesto que no dispongo de este SO para probarlo.&lt;/p&gt;
&lt;p&gt;Así que voy a ir por terreno seguro y aprovechando el feedback que me das, voy 
a prepararte una versión que emplee la herramienta externa wget para coger los 
datos de la página, los subtítulos y el vídeo. De este modo nos aseguramos de 
sortear este problema.&lt;/p&gt;
&lt;p&gt;De nada, la pena es no tener un Mac, si no, no te hacia dar tantas vueltas.&lt;/p&gt;
&lt;p&gt;Saludos&lt;/p&gt;
&lt;hr /&gt;
&lt;div style="float:right; padding:2px; border: 1px solid #ccc; height:28px;"&gt;
&lt;a href="pages/sobre-mi.html"&gt;&lt;img src="pictures/avtr_joedicastro.png" height=28 
width=28 alt="avatar" title="avatar de joedicastro"/&gt;&lt;/a&gt;&lt;/div&gt;

&lt;h3 id="ya_tienes_la_nueva_versi+n"&gt;Ya tienes la nueva versión&lt;/h3&gt;
&lt;p&gt;por &lt;a href="http://joedicastro.com"&gt;joe di castro&lt;/a&gt; el Jue, 25/11/2010 - 14:00&lt;/p&gt;
&lt;p&gt;Ya tienes la nueva versión con wget, esta no debería darte esos problemas. Ya me 
dirás el resultado.&lt;/p&gt;
&lt;p&gt;Saludos&lt;/p&gt;
&lt;hr /&gt;
&lt;div style="float:right; padding:2px; border: 1px solid #ccc; height:28px;"&gt;
&lt;img src="pictures/avtr_xavier.png" height=28 width=28
alt="avatar" title="avatar de Xavier"/&gt;&lt;/div&gt;

&lt;h3 id="fant+stico"&gt;fantástico!&lt;/h3&gt;
&lt;p&gt;por Xavier el Jue, 25/11/2010 - 14:43 &lt;/p&gt;
&lt;p&gt;Ahora ya funciona perfectamente!&lt;/p&gt;
&lt;p&gt;muchas gracias por añadir estas funcionalidades y por los scripts. Van a serme 
de grande utilidad.&lt;/p&gt;
&lt;p&gt;un saludo,&lt;/p&gt;
&lt;p&gt;Xavier&lt;/p&gt;
&lt;hr /&gt;
&lt;div style="float:right; padding:2px; border: 1px solid #ccc; height:28px;"&gt;
&lt;a href="pages/sobre-mi.html"&gt;&lt;img src="pictures/avtr_joedicastro.png" height=28 
width=28 alt="avatar" title="avatar de joedicastro"/&gt;&lt;/a&gt;&lt;/div&gt;

&lt;h3 id="de_nada_gracias_a_ti_por_el"&gt;De nada, gracias a ti por el&lt;/h3&gt;
&lt;p&gt;por &lt;a href="http://joedicastro.com"&gt;joe di castro&lt;/a&gt; el Jue, 25/11/2010 - 14:47&lt;/p&gt;
&lt;p&gt;De nada, gracias a ti por el feedback, le servirá a futuros usuarios.&lt;/p&gt;
&lt;p&gt;Luego modifico también el otro script para emplear wget.&lt;/p&gt;
&lt;p&gt;Un saludo&lt;/p&gt;
&lt;p&gt;Joe&lt;/p&gt;
&lt;hr /&gt;
&lt;div style="float:right; padding:2px; border: 1px solid #ccc; height:28px;"&gt;
&lt;a href="pages/sobre-mi.html"&gt;&lt;img src="pictures/avtr_joedicastro.png" height=28 
width=28 alt="avatar" title="avatar de joedicastro"/&gt;&lt;/a&gt;&lt;/div&gt;

&lt;h3 id="nuevas_versiones_mejoradas_de"&gt;Nuevas versiones mejoradas de&lt;/h3&gt;
&lt;p&gt;por &lt;a href="http://joedicastro.com"&gt;joe di castro&lt;/a&gt; el Mié, 01/12/2010 - 23:51&lt;/p&gt;
&lt;p&gt;Nuevas versiones mejoradas de los scripts (versión 1.3) y probadas en las 831 
charlas disponibles hasta hoy, contemplando todos los casos diferentes.&lt;/p&gt;
&lt;p&gt;Saludos&lt;/p&gt;</summary><category term="python"></category><category term="script"></category><category term="ted"></category><category term="ted talks"></category><category term="video"></category><category term="subtitulos"></category></entry></feed>
