<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:georss='http://www.georss.org/georss' xmlns:gd='http://schemas.google.com/g/2005' xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-15310795</id><updated>2012-02-16T05:46:05.371-03:00</updated><category term='linux'/><category term='Python'/><category term='sunos'/><category term='Notes About...'/><category term='regnum'/><category term='java'/><category term='Hacks'/><category term='manuales'/><category term='Galaktia'/><category term='lestat'/><category term='juego'/><category term='experiments'/><category term='comic'/><category term='Python Memory Tools'/><category term='ANSI C'/><category term='todos mueren'/><category term='projects'/><category term='siramek'/><category term='bash'/><category term='electricidad'/><category term='ikariam'/><category term='Pygame'/><category term='Code'/><category term='Syntax'/><category term='nuclear'/><category term='GitHub'/><category term='Mathematics'/><category term='Esteban'/><category term='etch'/><category term='software'/><category term='PyPI'/><category term='rolgps'/><category term='palm'/><category term='debian'/><category term='guerra'/><category term='pymemtools'/><category term='defcon'/><category term='LaTeX'/><category term='nvidia'/><category term='Pyglet'/><category term='DBus'/><category term='Ordano'/><title type='text'>/* comment */</title><subtitle type='html'></subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://pcostesi.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/15310795/posts/default?max-results=100'/><link rel='alternate' type='text/html' href='http://pcostesi.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><author><name>Pablo Alejandro Costesich</name><uri>https://profiles.google.com/109197123025560796978</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh3.googleusercontent.com/-RiwlUyLCcO4/AAAAAAAAAAI/AAAAAAAAAkU/uNS7Zn7RHxI/s512-c/photo.jpg'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>25</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>100</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-15310795.post-8694971862083497395</id><published>2011-10-10T02:24:00.001-03:00</published><updated>2011-10-10T02:25:00.490-03:00</updated><title type='text'>Lenguaje S (Lógica Computacional)</title><content type='html'>En el libro &lt;i&gt;Computability, Complexity and Languages &lt;/i&gt;de Davis y Weyuker se describe un lenguaje de programación &lt;i&gt;Y &lt;/i&gt;(S para algunos). Dicho lenguaje, sencillo, cumple algunas propiedades teóricas interesantes y es empleado en cátedras de Lógica Computacional.&lt;br /&gt;&lt;br /&gt;El código de un programa en S es algo como:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;[A1] IF X1 != 0 GOTO B1&lt;br /&gt;     Z1 &amp;lt;- Z1 + 1&lt;br /&gt;     IF Z1 != 0 GOTO E1&lt;br /&gt;[B1] X1 &amp;lt;- X1 - 1&lt;br /&gt;     Y  &amp;lt;- Y  + 1&lt;br /&gt;     Z1 &amp;lt;- Z1 + 1&lt;br /&gt;     IF Z1 != 0 GOTO A&lt;/pre&gt;&lt;pre&gt;&lt;/pre&gt;(Función Identidad, Página 17 de &lt;i&gt;Computability, Complexity and Languages&lt;/i&gt;)&lt;br /&gt;&lt;br /&gt;Es un lenguaje simple, con tres instrucciones: incrementar o decrementar una variable. y saltar a una etiqueta si una variable no es cero.&lt;br /&gt;&lt;br /&gt;El rango de las variables es [0, +inf) y toman como valor inicial cero. Además, decrementar una que está en cero no cambia su valor.&lt;br /&gt;&lt;br /&gt;También están las etiquetas, que permiten nombrar instrucciones y saltar a ellas mediante IF. Si se salta a una etiqueta inexistente el programa termina. Una extensión al lenguaje es la existencia de macros, similares a las de C.&lt;br /&gt;&lt;pre&gt;&lt;/pre&gt;En el Lenguaje S es conveniente probar los edge-cases antes de empezar a probar formalmente el programa, o tener una ayuda para hacer un seguimiento de la ejecución del mismo.&lt;br /&gt;&lt;br /&gt;Como el lenguaje es simple decidí implementar un &lt;a href="https://gist.github.com/1263655"&gt;intérprete&lt;/a&gt; y un compilador con una reducida máquina virtual (por el momento sólo en Python). Están bastante incompletos (pero funcionan) y necesitan un lexer-parser real. Aclaro que fue una solución &lt;i&gt;Quick n' Dirty.&lt;/i&gt;&lt;br /&gt;&lt;br /&gt;El &lt;a href="https://github.com/pcostesi/spy"&gt;código&lt;/a&gt; está en GitHub y bajo licencia NewBSD. Cualquier sugerencia constructiva es bien recibida, por lo que agradezco mucho cualquier fork y pull-request, bug report o comentario.&lt;div class="blogger-post-footer"&gt;&lt;a href="&lt;$BlogSiteFeedUrl$&gt;" title="Atom feed"&gt;Site Feed&lt;/a&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/15310795-8694971862083497395?l=pcostesi.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pcostesi.blogspot.com/feeds/8694971862083497395/comments/default' title='Comentarios de la entrada'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=15310795&amp;postID=8694971862083497395' title='0 Comentarios'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/15310795/posts/default/8694971862083497395'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/15310795/posts/default/8694971862083497395'/><link rel='alternate' type='text/html' href='http://pcostesi.blogspot.com/2011/10/lenguaje-s-logica-computacional.html' title='Lenguaje S (Lógica Computacional)'/><author><name>Pablo Alejandro Costesich</name><uri>https://profiles.google.com/109197123025560796978</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh3.googleusercontent.com/-RiwlUyLCcO4/AAAAAAAAAAI/AAAAAAAAAkU/uNS7Zn7RHxI/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-15310795.post-6180555426135086656</id><published>2011-09-16T15:26:00.002-03:00</published><updated>2011-09-16T15:26:40.005-03:00</updated><title type='text'>Datastore en App Engine</title><content type='html'>En el post anterior traté las dificultades que nos trajo App Engine, y en particular su Datastore, con la forma en la que diseñamos nuestra aplicación.&lt;br /&gt;&lt;br /&gt;Después de correr unas cuantas pruebas, descubrimos que la mayoría de los problemas de performance no provenían del particionamiento espacial (como erróneamente habíamos considerado), sino que de un &lt;i&gt;derivado&lt;/i&gt;&amp;nbsp;de las consultas: la cantidad de geoceldas que empleábamos por consulta. Teníamos, entonces, dos formas de solucionarlo: eliminar celdas, o bien encontrar el motivo por el cual la consulta era lenta.&lt;br /&gt;&lt;br /&gt;Las consultas con IN en Datastore son compuestas. Se realiza una consulta por cada elemento en la lista del filtro IN en paralelo, y luego se juntan los resultados para no devolver duplicados. Por este motivo, mientras más consultas con filtros IN tendremos mayor consumo de API, y por ende mayor latencia. En una aplicación que depende de respuestas casi instantáneas eso juega en detrimento de nuestra experiencia de usuario.&lt;br /&gt;&lt;br /&gt;¿Cómo podemos resolverlo? ¿O al menos optimizarlo?&lt;br /&gt;&lt;br /&gt;Como asumimos que los índices ya están bien armados por el desarrollador (no lo vamos a tratar en esta entrada), lo primero que podemos hacer es reordenar la forma en la que consultamos por datos. Un objeto Query en AppEngine tiene varias etapas, que son: construcción, ejecución y obtención. Si necesitamos ejecutar distintas consultas sin relación entre sí (como en un foro querríamos obtener la lista de usuarios activos, comentarios recientes y nuevos posts) conviene que lo hagan en paralelo. Para eso tenemos el método run(), que corre las consultas asincrónicamente. Podríamos generar nuestras consultas al principio, correrlas en paralelo y levantar el resultado recién cuando sea necesario sin tener que bloquear por cada una.&lt;br /&gt;&lt;br /&gt;Vuelta a las celdas, esta técnica no nos ayuda en nada pues las consultas IN ya corren en simultáneo. Podemos optar por una optimización básica: reducir el número de celdas. Menos consultas implica que obtener los datos (previo merging) sea un tanto más rápido, en especial si además filtramos por otras propiedades con IN. Sin embargo, en el post anterior vimos que no siempre es posible y que de todas formas tendremos como resultado pérdida de precisión y mayor cantidad de falsos positivos (elementos fuera de nuestro viewport, inútiles en nuestro caso). Terminaríamos aumentando la cantidad de elementos necesarios por consulta a un número poco práctico.&lt;br /&gt;&lt;br /&gt;Otra opción es obtener sólo objetos Key. La gran ventaja es que AppEngine no levantará las entidades y tardará menos en filtrar, pero nos dará la información relevante a nuestra consulta: &lt;i&gt;qué markers se encuentran en nuestro viewport actual&lt;/i&gt;, y no &lt;i&gt;la información de los que se encuentran allí&lt;/i&gt;. La desventaja es que ahora requerimos dos pasos para mostrar datos al usuario. ¿Pero entonces de qué nos sirve? Podemos aprovechar el poder de los navegadores modernos para continuar nuestras optimizaciones allí (tema que no trataremos en este post).&lt;br /&gt;Ahora debemos implementar get_multi, de forma tal que podamos levantar los markers de manera eficiente (en grupos) y una sola vez.&lt;br /&gt;&lt;br /&gt;La última optimización que podemos hacer es subdividir las entidades en fragmentos más pequeños. No es evidente, o al menos no es la opción que primero se nos ocurre, pero también debemos considerar el costo de serialización y el peso que conlleva crear objetos con datos que no vamos a aprovechar. En una aplicación que debe presentar feedback rápido al usuario separaríamos nuestra entidad en información de &lt;i&gt;primer nivel&lt;/i&gt;&amp;nbsp;(que efectivamente necesitamos) y &lt;i&gt;secundaria &lt;/i&gt;(que sólo aprovechamos cuando el usuario está interesado en ese objetivo).&lt;br /&gt;&lt;br /&gt;Como dije en un post anterior, desarrollar en AppEngine requiere pensar en un paradigma distinto y presenta desafíos interesantes.&lt;div class="blogger-post-footer"&gt;&lt;a href="&lt;$BlogSiteFeedUrl$&gt;" title="Atom feed"&gt;Site Feed&lt;/a&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/15310795-6180555426135086656?l=pcostesi.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pcostesi.blogspot.com/feeds/6180555426135086656/comments/default' title='Comentarios de la entrada'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=15310795&amp;postID=6180555426135086656' title='0 Comentarios'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/15310795/posts/default/6180555426135086656'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/15310795/posts/default/6180555426135086656'/><link rel='alternate' type='text/html' href='http://pcostesi.blogspot.com/2011/09/datastore-en-app-engine.html' title='Datastore en App Engine'/><author><name>Pablo Alejandro Costesich</name><uri>https://profiles.google.com/109197123025560796978</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh3.googleusercontent.com/-RiwlUyLCcO4/AAAAAAAAAAI/AAAAAAAAAkU/uNS7Zn7RHxI/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-15310795.post-8632935115851722126</id><published>2011-09-04T02:06:00.002-03:00</published><updated>2011-09-04T02:06:39.750-03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Python'/><category scheme='http://www.blogger.com/atom/ns#' term='Code'/><title type='text'>Problemas con consultas geoespaciales en Google AppEngine</title><content type='html'>&lt;br /&gt;En uno de los proyectos donde estoy trabajando la principal funcionalidad depende de búsqueda de puntos en un mapa. La aplicación debe responder de manera rápida, estable y devolver datos según diversos filtros para luego ser procesados del lado cliente (web).&lt;br /&gt;&lt;br /&gt;El stack de AppEngine presentó unos cuantos desafíos, particularmente en Datastore y CPU. Si bien la aplicación es relativamente sencilla, la gran cantidad de puntos y los filtros demandan una forma distinta de pensar.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Datastore&lt;/b&gt;&lt;br /&gt;Como el Datastore de AppEngine está basado en BigTable, las consultas sobre el sistema se limitan a desigualdades sobre un solo campo. Esto elimina búsquedas donde encerramos entre rangos de latitud/longitud, y además evita el ordenamiento de los datos.&lt;br /&gt;&lt;br /&gt;La solución es usar una especie de función hash para almacenar la posición aproximada de los puntos. La idea es particionar el mapa en una cantidad arbitraria de celdas y utilizar el operador IN de AppEngine. &amp;nbsp;Esto nos ahorra la búsqueda por desigualdad a costo de incrementar el número de consultas (y CPU).&lt;br /&gt;&lt;br /&gt;&lt;a href="http://code.google.com/p/geomodel/"&gt;Geomodel&lt;/a&gt; es una librería que implementa este sistema. Provee un modelo del que heredamos y los métodos necesarios para generar y consultar las geoceldas. Es totalmente transparente al usuario y sólo requiere que llamemos a un método para actualizar la información de las celdas. Sin embargo, es necesario conocer la teoría para hacer uso correcto de los recursos y no llevarnos sorpresas desagradables.&lt;br /&gt;&lt;br /&gt;(Nota: omito el chequeo de errores para ser breve)&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Teoría&lt;/b&gt;&lt;br /&gt;¿Qué es una geocelda?&lt;br /&gt;Podemos pensar al mapa como un rectángulo. La idea de las geoceldas es partir el mapa en secciones&lt;br /&gt;&amp;nbsp;más pequeños, seleccionar la celda donde se encuentra nuestro punto y reiterar el proceso hasta que logremos una celda arbitrariamente pequeña (como un &lt;a href="http://en.wikipedia.org/wiki/Quadtree"&gt;quadtree&lt;/a&gt;). Lo bueno de este sistema es que como trabajamos sobre el par latitud/longitud es independiente a la proyección.&lt;br /&gt;&lt;br /&gt;(Nota: Geomodel implementa los métodos de forma distinta y más eficiente, pero la esencia es la misma)&lt;br /&gt;&lt;br /&gt;Ejemplo:&lt;br /&gt;Supongamos que tenemos un sistema que va desde 0 a 100 metros para la latitud y la longitud, y queremos celdas de 10 metros de precisión. Para facilitar el ejemplo, sólo partiremos el mapa en 4 celdas por vez, a las que llamaremos Q, W, A y S (por la posición en el teclado).&lt;br /&gt;&lt;br /&gt;Para ubicar el punto 2-32:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;class Map(object):&lt;br /&gt;    def __init__(self, x, y, cell_size):&lt;br /&gt;        self.x = x&lt;br /&gt;        self.y = y&lt;br /&gt;        self.cell_size = cell_size&lt;br /&gt;&lt;br /&gt;    def locate(self, x, y):&lt;br /&gt;        cell = ""&lt;br /&gt;        middle_x, middle_y = self.x / 2, self.y / 2&lt;br /&gt;        size = max(self.x, self.y)&lt;br /&gt;        while size &amp;gt; self.cell_size:&lt;br /&gt;            left, right = int(x &amp;gt; middle_x), int(y &amp;gt; middle_y) &lt;br /&gt;            cell += ("QW", "AS")[left][right]&lt;br /&gt;            middle_x *= 0.5 + left * 0.25&lt;br /&gt;            middle_y *= 0.5 + right * 0.25&lt;br /&gt;            size /= 2&lt;br /&gt;        return cell&lt;br /&gt;            &lt;br /&gt;m = Map(100, 100, 10)&lt;br /&gt;print m.locate(2, 32)&lt;/pre&gt;&lt;br /&gt;Vemos que calcular el hash tiene costo Tita(log(max(x, y) / cell_size)). Una propiedad importante es que si dos puntos tienen el mismo prefijo entonces son vecinos, mientras que la inversa no es cierta para todos los casos (ejemplo: 51-50 y 50-51). Esto dificulta la búsqueda de proximidad (pues debemos generar las ocho celdas vecinas), y aumenta el uso de CPU.&lt;br /&gt;&lt;br /&gt;Por el tipo de arquitectura de BigTable, para almacenar los registros conviene precomputar una lista de todas las celdas a las que un punto pertenece y almacenarlas en una ListProperty. De esta manera, el costo de una consulta por celda será proporcional a la precisión (número de elementos).&lt;br /&gt;&lt;br /&gt;La búsqueda por caja (bounding box) es la que más utilizamos. Para ello se computa una serie de celdas a partir de una vista (viewport) a una resolución determinada, y con esas celdas se consulta a la base de datos. Geomodel filtra los resultados para eliminar aquellos que quedan fuera de la vista, pero eso incrementa el uso de CPU.&lt;br /&gt;&lt;br /&gt;Para calcular el viewport necesitamos un par de funciones para obtener los vecinos (izquierdo e inferior) de una celda. Eso lo podemos lograr así:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;class Map(Map):&lt;br /&gt;&lt;br /&gt;    ...&lt;br /&gt;&lt;br /&gt;    @staticmethod&lt;br /&gt;    def right(cell):&lt;br /&gt;        if cell[-1] == "Q":&lt;br /&gt;            return cell[:-1] + "W"&lt;br /&gt;        elif cell[-1] == "A":&lt;br /&gt;            return cell[:-1] + "S"&lt;br /&gt;        elif cell[-1] == "W":&lt;br /&gt;            return Map.right(cell[:-1]) + "Q"&lt;br /&gt;        elif cell[-1] == "S":&lt;br /&gt;            return Map.right(cell[:-1]) + "A"&lt;br /&gt;            &lt;br /&gt;    @staticmethod&lt;br /&gt;    def down(cell):&lt;br /&gt;        if cell[-1] == "Q":&lt;br /&gt;            return cell[:-1] + "A"&lt;br /&gt;        elif cell[-1] == "W":&lt;br /&gt;            return cell[:-1] + "S"&lt;br /&gt;        elif cell[-1] == "A":&lt;br /&gt;            return Map.down(cell[:-1]) + "Q"&lt;br /&gt;        elif cell[-1] == "S":&lt;br /&gt;            return Map.down(cell[:-1]) + "W"&lt;br /&gt;&lt;br /&gt;       &lt;br /&gt;&lt;/pre&gt;El costo de calcular el vecino es relativo a la precisión, pero en la mayoría de los casos requiere sólo un par de cambios. Con estas funciones sólo resta calcular el viewport, para lo cual calculamos los cuatro extremos de nuestra caja y generamos a partir de ellos el resto:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;class Map(Map):&lt;br /&gt;&lt;br /&gt;    ...&lt;br /&gt;&lt;br /&gt;    @classmethod&lt;br /&gt;    def row(cls, cell, limit):&lt;br /&gt;        yield cell&lt;br /&gt;        while cell != limit:&lt;br /&gt;            cell = cls.right(cell)&lt;br /&gt;            yield cell&lt;br /&gt;&lt;br /&gt;    @classmethod&lt;br /&gt;    def col(cls, cell, limit):&lt;br /&gt;        yield cell&lt;br /&gt;        while cell != limit:&lt;br /&gt;            cell = cls.down(cell)&lt;br /&gt;            yield cell&lt;br /&gt;&lt;br /&gt;    def viewport(self, n, e, s, w):&lt;br /&gt;        left_col = self.col(self.locate(n, e), self.locate(s, e))&lt;br /&gt;        right_col = self.col(self.locate(n, w), self.locate(s, w))&lt;br /&gt;        for start, stop in zip(left_col, right_col):&lt;br /&gt;            for cell in self.row(start, stop):&lt;br /&gt;                yield cell &lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre&gt;&lt;br /&gt;&lt;/pre&gt;Para nuestro mapa, el uso sería:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;for cell in m.viewport(0, 0, 20, 20):&lt;br /&gt;    print cell&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Vemos que la búsqueda por viewport es simple, pero que requiere una cantidad de recursos importante si se quiere un buen nivel de precisión. En particular, generar las celdas es proporcional a la resolución y tamaño de la caja, por lo que un buen algoritmo debería ser capaz de detectar el tamaño óptimo de las celdas para reducir la cantidad de consultas (por prefijo común, por ejemplo) y no superar el límite práctico de AppEngine.&lt;br /&gt;&lt;br /&gt;Existen otros esquemas de partición, pero es aconsejable escoger uno que nos permita ordenar los resultados de forma coherente. Para los interesados, Geomodel particiona en 16 subceldas y asigna un orden similar a un código Morton (&lt;a href="http://en.wikipedia.org/wiki/Z-order_(curve)"&gt;Z-Order Curve&lt;/a&gt;).&lt;br /&gt;&lt;br /&gt;No voy a tratar el análisis de búsqueda por proximidad, al menos no en este post. ¿Por qué? Una idea: el método proximity_fetch en Geomodel tiene +150 líneas. Se puede obtener un resultado similar para celdas pequeñas generando un viewport centrado en el objetivo.&lt;div class="blogger-post-footer"&gt;&lt;a href="&lt;$BlogSiteFeedUrl$&gt;" title="Atom feed"&gt;Site Feed&lt;/a&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/15310795-8632935115851722126?l=pcostesi.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pcostesi.blogspot.com/feeds/8632935115851722126/comments/default' title='Comentarios de la entrada'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=15310795&amp;postID=8632935115851722126' title='0 Comentarios'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/15310795/posts/default/8632935115851722126'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/15310795/posts/default/8632935115851722126'/><link rel='alternate' type='text/html' href='http://pcostesi.blogspot.com/2011/09/problemas-con-consultas-geoespaciales.html' title='Problemas con consultas geoespaciales en Google AppEngine'/><author><name>Pablo Alejandro Costesich</name><uri>https://profiles.google.com/109197123025560796978</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh3.googleusercontent.com/-RiwlUyLCcO4/AAAAAAAAAAI/AAAAAAAAAkU/uNS7Zn7RHxI/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-15310795.post-5089539983343924496</id><published>2010-04-20T20:31:00.005-03:00</published><updated>2010-04-30T12:21:12.617-03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Mathematics'/><category scheme='http://www.blogger.com/atom/ns#' term='ANSI C'/><category scheme='http://www.blogger.com/atom/ns#' term='experiments'/><title type='text'>Game of Life (Reprise)</title><content type='html'>Sólo por el placer de probarme, (re)escribí una versión de Conway's Game of Life en Ansi C con un par de particularidades. El algoritmo usa bit shifting, por lo que es relativamente difícil de leer. ¿Por qué hacer esto, entonces? Puro ejercicio mental.&lt;br /&gt;&lt;br /&gt;En el algoritmo entran en juego dos conceptos básicos: máquinas de estado y operaciones de bit-a-bit. Hay mucho material dando vueltas por ahí como para explicarlo, pero son dos herramientas que encajan perfecto con este problema.&lt;br /&gt;&lt;br /&gt;El código tiene menos de 70 líneas en total, y si bien no es "edgeless" como el ejemplo del &lt;a href="http://pcostesi.blogspot.com/2009/12/conways-game-of-life.html"&gt;Game of Life anterior&lt;/a&gt;, cumple a la perfección su cometido. El tiempo de ejecución es tan corto que ni siquiera puedo medirlo con time. El código en assembler (gcc archivo.c -s) tiene 391 líneas invariablemente del flag de optimización. Supongo que eso ocurre por el uso de instrucciones de comparación bit-a-bit.&lt;br /&gt;&lt;br /&gt;Un posible cambio para eliminar la suma es realizar un mapa de todas las combinaciones de celdas de 3x3, con lo que el input y el estado serían lo mismo. El mapa devolvería el estado de la célula, exactamente igual que en el ejemplo. No lo considero una optimización, más bien lo contrario.&lt;br /&gt;&lt;br /&gt;El código está en &lt;strike&gt;&lt;a href="http://codepad.org/ovElmmuM"&gt;codepad&lt;/a&gt;&lt;/strike&gt; (UPDATE: &lt;a href="http://codepad.org/hSXM88EI"&gt;acá&lt;/a&gt;) y a continuación:&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: c"&gt;#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;&lt;br /&gt;#define COUNT_ALIVE(a, b, c, offset)    (((a &gt;&gt; (offset - 1)) &amp; 1) + \&lt;br /&gt;                                        ((a &gt;&gt; offset) &amp; 1) + \&lt;br /&gt;                                        ((a &gt;&gt; (offset + 1)) &amp; 1) + \&lt;br /&gt;                                        ((b &gt;&gt; (offset - 1)) &amp; 1) + \&lt;br /&gt;                                        ((b &gt;&gt; (offset + 1)) &amp; 1) + \&lt;br /&gt;                                        ((c &gt;&gt; (offset - 1)) &amp; 1) + \&lt;br /&gt;                                        ((c &gt;&gt; offset) &amp; 1) + \&lt;br /&gt;                                        ((c &gt;&gt; (offset + 1)) &amp; 1))&lt;br /&gt;&lt;br /&gt;#define IS_ALIVE(a, b)                  ((a &gt;&gt; b) &amp; 1)&lt;br /&gt;&lt;br /&gt;char status[2][9] =    {&lt;br /&gt;                    /*   0, 1, 2, 3, 4, 5, 6, 7, 8*/&lt;br /&gt;                        {0, 0, 0, 1, 0, 0, 0, 0, 0},    /* Dead cell */&lt;br /&gt;                        {0, 0, 1, 1, 0, 0, 0, 0, 0}     /* alive cell */&lt;br /&gt;                        };&lt;br /&gt;&lt;br /&gt;void print(unsigned long int [], int, int);&lt;br /&gt;void evolve(unsigned long int [], int, int);&lt;br /&gt;&lt;br /&gt;int&lt;br /&gt;main(int argc, char** argv)&lt;br /&gt;{&lt;br /&gt;    unsigned long int m[20];&lt;br /&gt;    int i;&lt;br /&gt;    for (i = 0; i &lt; 20; i++)&lt;br /&gt;        m[i] = 0;&lt;br /&gt;    m[3] = 1 &lt;&lt; 8;&lt;br /&gt;    m[4] = 1 &lt;&lt; 7;&lt;br /&gt;    m[5] = 7 &lt;&lt; 7;&lt;br /&gt;    for (i = 0; i &lt; 32; i++)&lt;br /&gt;    {&lt;br /&gt;        printf("= GENERATION %d =\n", i + 1);&lt;br /&gt;        print(m, 20, 20);&lt;br /&gt;        evolve(m, 20, 20);&lt;br /&gt;    }&lt;br /&gt;    return EXIT_SUCCESS;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;void&lt;br /&gt;print(unsigned long int m[], int x, int y)&lt;br /&gt;{&lt;br /&gt;    int i, j;&lt;br /&gt;    for(i = 0; i &lt; y; i++)&lt;br /&gt;    {&lt;br /&gt;        for(j = 1; j &lt;= x; j++)&lt;br /&gt;            printf("%c", (m[i] &gt;&gt; (x - j)) &amp; 1 ? '#' : ' ' );&lt;br /&gt;        printf("\n");&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;void evolve(unsigned long int m[], int x, int y)&lt;br /&gt;{&lt;br /&gt;    unsigned long int *m2 = calloc(y, sizeof(unsigned long int));&lt;br /&gt;    int itrX, itrY;&lt;br /&gt;    for (itrY = 1; itrY &lt; y - 1; itrY++)&lt;br /&gt;        for (itrX = 1; itrX &lt; x - 1; itrX++)&lt;br /&gt;            m2[itrY] |= (status[IS_ALIVE(m[itrY], itrX)][COUNT_ALIVE(\&lt;br /&gt;                        m[itrY - 1], m[itrY], m[itrY + 1], itrX)] &lt;&lt; itrX);&lt;br /&gt;    for (itrY = 0; itrY &lt; y; itrY++)&lt;br /&gt;        m[itrY] = m2[itrY];&lt;br /&gt;    free(m2);&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;EDIT: Me olvidé hacer la respectiva llamada a free, y calloc inicializa a cero.&lt;div class="blogger-post-footer"&gt;&lt;a href="&lt;$BlogSiteFeedUrl$&gt;" title="Atom feed"&gt;Site Feed&lt;/a&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/15310795-5089539983343924496?l=pcostesi.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pcostesi.blogspot.com/feeds/5089539983343924496/comments/default' title='Comentarios de la entrada'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=15310795&amp;postID=5089539983343924496' title='0 Comentarios'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/15310795/posts/default/5089539983343924496'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/15310795/posts/default/5089539983343924496'/><link rel='alternate' type='text/html' href='http://pcostesi.blogspot.com/2010/04/game-of-life-reprise.html' title='Game of Life (Reprise)'/><author><name>Pablo Alejandro Costesich</name><uri>https://profiles.google.com/109197123025560796978</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh3.googleusercontent.com/-RiwlUyLCcO4/AAAAAAAAAAI/AAAAAAAAAkU/uNS7Zn7RHxI/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-15310795.post-1898445877941961117</id><published>2010-04-03T20:12:00.003-03:00</published><updated>2010-04-03T20:15:02.326-03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Python'/><title type='text'>Quick Pastebin</title><content type='html'>Hoy necesitaba pasar unos archivos de una máquina que corre Linux a otra con el mismo sistema operativo. El problema: rsync y scp no funcionan, y no sé el motivo. Tampoco tenía tiempo para andar averiguando, así que en menos de 10 minutos codeé esto:&lt;br /&gt;&lt;br /&gt;&lt;pre class='brush: python'&gt;#!/usr/bin/env python&lt;br /&gt;# -*- coding: utf-8 -*-&lt;br /&gt;#&lt;br /&gt;#       pastebin.py&lt;br /&gt;#       &lt;br /&gt;#       Copyright 2010 Pablo Alejandro Costesich&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;import urllib2&lt;br /&gt;import urllib&lt;br /&gt;import sys&lt;br /&gt;&lt;br /&gt;def paste(text, **kwargs):&lt;br /&gt;    urlapi ='http://pastebin.com/api_public.php'&lt;br /&gt;    values = {'paste_code': text}&lt;br /&gt;    values.update(kwargs)&lt;br /&gt;    data = urllib.urlencode(values)&lt;br /&gt;    req = urllib2.Request(urlapi, data)&lt;br /&gt;    response = urllib2.urlopen(req)&lt;br /&gt;    return response.read()&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;def main(*args):&lt;br /&gt;    if len(args) == 1:&lt;br /&gt;        print paste(''.join(sys.stdin.readlines()))&lt;br /&gt;    else:&lt;br /&gt;        for filename in args[1:]:&lt;br /&gt;            f = open(filename)&lt;br /&gt;            print paste(''.join(f.readlines()))&lt;br /&gt;    return 0&lt;br /&gt;&lt;br /&gt;if __name__ == '__main__':&lt;br /&gt;    main(*sys.argv)&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;a href="&lt;$BlogSiteFeedUrl$&gt;" title="Atom feed"&gt;Site Feed&lt;/a&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/15310795-1898445877941961117?l=pcostesi.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pcostesi.blogspot.com/feeds/1898445877941961117/comments/default' title='Comentarios de la entrada'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=15310795&amp;postID=1898445877941961117' title='0 Comentarios'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/15310795/posts/default/1898445877941961117'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/15310795/posts/default/1898445877941961117'/><link rel='alternate' type='text/html' href='http://pcostesi.blogspot.com/2010/04/quick-pastebin.html' title='Quick Pastebin'/><author><name>Pablo Alejandro Costesich</name><uri>https://profiles.google.com/109197123025560796978</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh3.googleusercontent.com/-RiwlUyLCcO4/AAAAAAAAAAI/AAAAAAAAAkU/uNS7Zn7RHxI/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-15310795.post-8986972830047360903</id><published>2010-03-25T12:51:00.000-03:00</published><updated>2010-03-25T12:51:45.715-03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='GitHub'/><category scheme='http://www.blogger.com/atom/ns#' term='PyPI'/><category scheme='http://www.blogger.com/atom/ns#' term='Python Memory Tools'/><category scheme='http://www.blogger.com/atom/ns#' term='Galaktia'/><category scheme='http://www.blogger.com/atom/ns#' term='Python'/><category scheme='http://www.blogger.com/atom/ns#' term='pymemtools'/><title type='text'>Python Memory Tools</title><content type='html'>Ayer publiqué por primera vez una herramienta sobre la que estuve trabajando para resolver un par de problemas en Galaktia. Se trata de un conjunto de funciones decoradoras y clases para implementar el &lt;i&gt;Memory Pattern&lt;/i&gt; en &lt;i&gt;callables&lt;/i&gt; y brindarles una pseudo transparencia referencial (porque el valor debería depender sólo de los parámetros y no causar efectos colaterales). Es útil para funciones computacionalmente caras e inmutables (o que la actualización de sus valores de retorno tenga un tiempo de expiración largo).&lt;br /&gt;&lt;br /&gt;Es una librería muy sencilla y fácil de entender, aunque seguro implementada miles de veces y de distintas maneras. Mi objetivo fue que sea fácil de usar (para mí), donde cada storage se asemeje lo más posible a un diccionario. Esto hace que pueda reemplazar muchas partes de código viejo sin romperlo.&lt;br /&gt;&lt;br /&gt;Un ejemplo muy simple de la librería:&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: python"&gt;from memtools.storages import RedisMemory, MemcacheMemory, Alzheimer&lt;br /&gt;mem = RedisMemory(debug=True)&lt;br /&gt;mem2 = MemcacheMemory(debug=True) # podría usarlo del mismo modo&lt;br /&gt;mem3 = Alzheimer(True) # easter egg, pierde valores a lo random.&lt;br /&gt;@mem&lt;br /&gt;def fib(n):&lt;br /&gt;    if n in (0, 1):&lt;br /&gt;        return 1&lt;br /&gt;    else:&lt;br /&gt;        return fib(n - 1) + fib(n - 2)&lt;br /&gt;&lt;br /&gt;print fib(2)&lt;br /&gt;print fib(5)&lt;br /&gt;print fib(3)&lt;br /&gt;&lt;br /&gt;mem["asd"] = 1&lt;br /&gt;print mem["asd"]&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;La biblioteca está bajo licencia BSD y fue escrita enteramente por mí. En una época fue parte del proyecto Galaktia (@galaktia), pero gracias a Manuel Aráoz (@maraoz) y el resto del equipo pude separarla, así que ahora está disponible para todos. Agradezco cualquier modificación, sugerencia o cambio que pueda hacerse para mejorarla (son libres de hacerlo o no). Un fork&amp;merge sería un halago.&lt;br /&gt;&lt;br /&gt;Pueden encontrar el código en &lt;a href="http://github.com/pcostesi/pymemtools"&gt;GitHub&lt;/a&gt; o en &lt;a href="http://pypi.python.org/pypi/pymemtools"&gt;PyPI&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Puede ser que en un futuro le agregue soporte para memcache en Google App Engine y &lt;i&gt;on-demand value updates&lt;/i&gt; (recalcular los valores almacenados para una clave cuando se cumpla una condición).&lt;div class="blogger-post-footer"&gt;&lt;a href="&lt;$BlogSiteFeedUrl$&gt;" title="Atom feed"&gt;Site Feed&lt;/a&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/15310795-8986972830047360903?l=pcostesi.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pcostesi.blogspot.com/feeds/8986972830047360903/comments/default' title='Comentarios de la entrada'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=15310795&amp;postID=8986972830047360903' title='0 Comentarios'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/15310795/posts/default/8986972830047360903'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/15310795/posts/default/8986972830047360903'/><link rel='alternate' type='text/html' href='http://pcostesi.blogspot.com/2010/03/python-memory-tools.html' title='Python Memory Tools'/><author><name>Pablo Alejandro Costesich</name><uri>https://profiles.google.com/109197123025560796978</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh3.googleusercontent.com/-RiwlUyLCcO4/AAAAAAAAAAI/AAAAAAAAAkU/uNS7Zn7RHxI/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-15310795.post-6863780324375050643</id><published>2010-02-22T19:11:00.000-03:00</published><updated>2010-02-22T19:11:17.510-03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='linux'/><category scheme='http://www.blogger.com/atom/ns#' term='Python'/><category scheme='http://www.blogger.com/atom/ns#' term='experiments'/><title type='text'>Eerie ping</title><content type='html'>Ayer a la noche hablando con un amigo de la facultad e intercambiando ideas en base a una conversación con raíces en la &lt;a href="http://xkcd.com/705/"&gt;última edición&lt;/a&gt; de la &lt;a href="http://xkcd.com"&gt;tira electrónica xkcd&lt;/a&gt;, salió como desafío mandar un mensaje entre equipos de una red usando la opción "-p" mediante ping.&lt;br /&gt;&lt;br /&gt;El código, horrible pero legible, a continuación:&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: python"&gt;import sys, os&lt;br /&gt;addr = sys.argv[1]&lt;br /&gt;msg = ' '.join(sys.argv[2:])&lt;br /&gt;&lt;br /&gt;for i in xrange(0, len(msg), 2):&lt;br /&gt;    a, b = msg[i], (msg[i+1] if i+1 &lt; len(msg) else '\0')&lt;br /&gt;    os.popen("ping -c 1 -p %02X%02X %s" % (ord(a), ord(b), addr))&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;a href="&lt;$BlogSiteFeedUrl$&gt;" title="Atom feed"&gt;Site Feed&lt;/a&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/15310795-6863780324375050643?l=pcostesi.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pcostesi.blogspot.com/feeds/6863780324375050643/comments/default' title='Comentarios de la entrada'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=15310795&amp;postID=6863780324375050643' title='0 Comentarios'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/15310795/posts/default/6863780324375050643'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/15310795/posts/default/6863780324375050643'/><link rel='alternate' type='text/html' href='http://pcostesi.blogspot.com/2010/02/eerie-ping.html' title='Eerie ping'/><author><name>Pablo Alejandro Costesich</name><uri>https://profiles.google.com/109197123025560796978</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh3.googleusercontent.com/-RiwlUyLCcO4/AAAAAAAAAAI/AAAAAAAAAkU/uNS7Zn7RHxI/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-15310795.post-4903207255719573946</id><published>2009-12-16T14:35:00.002-03:00</published><updated>2010-04-20T00:52:08.171-03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='projects'/><category scheme='http://www.blogger.com/atom/ns#' term='Notes About...'/><category scheme='http://www.blogger.com/atom/ns#' term='software'/><category scheme='http://www.blogger.com/atom/ns#' term='DBus'/><category scheme='http://www.blogger.com/atom/ns#' term='Python'/><category scheme='http://www.blogger.com/atom/ns#' term='experiments'/><title type='text'>Experimentos con DBus en Python: Nombre Conocido, Exportando Objetos y Lanzando Señales</title><content type='html'>&lt;b&gt;Nombre Conocido&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;En el tutorial oficial no está documentado, pero tampoco es complejo. Sólo se necesita usar el método dbus.service.BusName para reclamar un nombre y pasarle el bus al que estará asociado.&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: python"&gt;import gobject&lt;br /&gt;from dbus.service import BusName&lt;br /&gt;from dbus import SessionBus&lt;br /&gt;from dbus.mainloop.glib import DBusGMainLoop&lt;br /&gt;&lt;br /&gt;dbus_loop = DBusGMainLoop()&lt;br /&gt;session = SessionBus(mainloop=dbus_loop)&lt;br /&gt;&lt;br /&gt;name = BusName('com.blogger.pcostesi.Test', session)&lt;br /&gt;&lt;br /&gt;loop = gobject.MainLoop()&lt;br /&gt;gobject.threads_init()&lt;br /&gt;loop.run()&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Lo anterior es inseguro, ya que no realizamos ninguna verificación sobre la existencia previa de un servicio del mismo modo. Lo correcto sería envolver el llamado &lt;a href="http://lateral.netmanagers.com.ar/tr/es/weblog/2009/12/11.html?utm_source=feedburner&amp;utm_medium=feed&amp;utm_campaign=Feed%3A+LateralOpinionEsp+%28Lateral+Opinion+En+Espa%C3%B1ol%29#BB854"&gt;en un bloque try/except, como hace Roberto Alsina&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Una vez que tenemos el BusName reservado para nuestro servicio, podemos exponer objetos.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Exportando Objetos&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;Para publicar un servicio, es necesario encapsularlo en una clase que herede de dbus.service.Object, y los métodos que quisiéramos que sean accesibles desde el bus deben ser decorados con dbus.service.method. Object tiene que ser inicializado en __init__ para que funcione, pasándole un Nombre Conocido o un Bus y el path del objeto.&lt;br /&gt;&lt;br /&gt;Un servicio ejemplo sería:&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: python"&gt;import gobject&lt;br /&gt;from dbus.service import BusName, Object, method&lt;br /&gt;from dbus import SessionBus&lt;br /&gt;from dbus.mainloop.glib import DBusGMainLoop&lt;br /&gt;&lt;br /&gt;dbus_loop = DBusGMainLoop()&lt;br /&gt;session = SessionBus(mainloop=dbus_loop)&lt;br /&gt;&lt;br /&gt;class Ejemplo(Object):&lt;br /&gt;    instancias = 0&lt;br /&gt;    def __init__(self, bus, loop):&lt;br /&gt;        # Nombre Conocido de la aplicación&lt;br /&gt;        name = BusName('com.blogger.pcostesi.Test', bus)&lt;br /&gt;        Ejemplo.instancias += 1&lt;br /&gt;        self.loop = loop&lt;br /&gt;        self.path = '/com/blogger/pcostesi/Ejemplo/%s' % Ejemplo.instancias&lt;br /&gt;        super(Ejemplo, self).__init__(name, self.path)&lt;br /&gt;        print self.path, "listo."&lt;br /&gt;    &lt;br /&gt;    @method(dbus_interface='com.blogger.pcostesi.Ejemplo',&lt;br /&gt;            in_signature='s', out_signature='', sender_keyword='sender',&lt;br /&gt;            destination_keyword='dest')&lt;br /&gt;    def saludo(self, saludo, sender='alguien', dest='mi'):&lt;br /&gt;        print "%s dijo a %s: %s" % (sender, dest, saludo)&lt;br /&gt;    &lt;br /&gt;    @method(dbus_interface='com.blogger.pcostesi.CerrarEjemplo')&lt;br /&gt;    def salir(self):&lt;br /&gt;        self.loop.quit()&lt;br /&gt;&lt;br /&gt;loop = gobject.MainLoop()&lt;br /&gt;gobject.threads_init()&lt;br /&gt;&lt;br /&gt;ejemplo1 = Ejemplo(session, loop)&lt;br /&gt;ejemplo2 = Ejemplo(session, loop)&lt;br /&gt;&lt;br /&gt;loop.run()&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Y un cliente sólo necesitaría ser así:&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: python"&gt;import dbus&lt;br /&gt;session = dbus.SessionBus()&lt;br /&gt;&lt;br /&gt;ejemplo1 = session.get_object('com.blogger.pcostesi.Test',&lt;br /&gt;        '/com/blogger/pcostesi/Ejemplo/1')&lt;br /&gt;ejemplo2 = session.get_object('com.blogger.pcostesi.Test',&lt;br /&gt;        '/com/blogger/pcostesi/Ejemplo/2')&lt;br /&gt;&lt;br /&gt;ej1_saludo = dbus.Interface(ejemplo1,&lt;br /&gt;        dbus_interface='com.blogger.pcostesi.Ejemplo')&lt;br /&gt;&lt;br /&gt;ej2_saludo = dbus.Interface(ejemplo2,&lt;br /&gt;        dbus_interface='com.blogger.pcostesi.Ejemplo')&lt;br /&gt;&lt;br /&gt;ej1_cerrar = dbus.Interface(ejemplo1,&lt;br /&gt;        dbus_interface='com.blogger.pcostesi.CerrarEjemplo')&lt;br /&gt;&lt;br /&gt;ej2_cerrar = dbus.Interface(ejemplo2,&lt;br /&gt;        dbus_interface='com.blogger.pcostesi.CerrarEjemplo')&lt;br /&gt;&lt;br /&gt;ej1_saludo.saludo('Hola')&lt;br /&gt;ej2_saludo.saludo('Hello')&lt;br /&gt;ej1_cerrar.salir()&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Notarán la gran cantidad de parámetros que recibe el decorador. No todos son necesarios, sólo la interfaz. Recomiendo llenar in_signature y out_signature para despejar dudas.&lt;br /&gt;&lt;br /&gt;Los keywords que admite son:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;&lt;i&gt;dbus_interface&lt;/i&gt;: la interfaz a la que el método pertenecerá&lt;br /&gt;&lt;/li&gt;&lt;li&gt;&lt;i&gt;in_signature&lt;/i&gt;: la firma de entrada de la función, según los Tipos de Datos.&lt;br /&gt;&lt;/li&gt;&lt;li&gt;&lt;i&gt;out_signature&lt;/i&gt;: la firma de salida de la función, según los Tipos de Datos.&lt;/li&gt;&lt;li&gt;&lt;i&gt;async_callbacks&lt;/i&gt;: una tupla que contiene dos strings, cada uno con los nombres de keyword en el método que es decorado (de éxito y de error respectivamente) para cuando son llamados asincrónicamente. &lt;br /&gt;&lt;/li&gt;&lt;ul&gt;&lt;/ul&gt;&lt;li&gt;&lt;i&gt;sender_keyword&lt;/i&gt; &lt;/li&gt;&lt;li&gt;&lt;i&gt;path_keyword&lt;/i&gt;: keyword a la dirección del objeto&lt;br /&gt;&lt;/li&gt;&lt;li&gt;&lt;i&gt;rel_path_keyword&lt;/i&gt;: similar al anterior, pero el objeto recibirá un path relativo.&lt;br /&gt;&lt;/li&gt;&lt;li&gt;&lt;i&gt;destination_keyword&lt;/i&gt;&lt;br /&gt;&lt;/li&gt;&lt;li&gt;&lt;i&gt;message_keyword&lt;/i&gt;&lt;/li&gt;&lt;li&gt;&lt;i&gt;connection_keyword&lt;/i&gt;: keyword para pasar un objeto dbus.connection.Connection. Útil por si este objeto está en más de una conexión.&lt;/li&gt;&lt;li&gt;&lt;i&gt;utf8_strings&lt;/i&gt;&lt;/li&gt;&lt;li&gt;&lt;i&gt;byte_arrays&lt;/i&gt;&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;&lt;b&gt;Lanzando Señales&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;A diferencia de los métodos exportados, las señales trabajan sólo con los parámetros de entrada y tienen el mismo nombre que la función a la que se llama. Para crear una señal, lo único que hay que hacer es aplicar el decorador dbus.service.signal.&lt;br /&gt;&lt;br /&gt;Agreguemos a nuestro ejemplo anterior una señal para cuando llamemos al método saludo: &lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: python"&gt;import gobject&lt;br /&gt;from dbus.service import BusName, Object, method, signal&lt;br /&gt;from dbus import SessionBus&lt;br /&gt;from dbus.mainloop.glib import DBusGMainLoop&lt;br /&gt;&lt;br /&gt;dbus_loop = DBusGMainLoop()&lt;br /&gt;session = SessionBus(mainloop=dbus_loop)&lt;br /&gt;&lt;br /&gt;class Ejemplo(Object):&lt;br /&gt;    instancias = 0&lt;br /&gt;    def __init__(self, bus, loop):&lt;br /&gt;        # Nombre Conocido de la aplicación&lt;br /&gt;        name = BusName('com.blogger.pcostesi.Test', bus)&lt;br /&gt;        Ejemplo.instancias += 1&lt;br /&gt;        self.loop = loop&lt;br /&gt;        self.path = '/com/blogger/pcostesi/Ejemplo/%s' % Ejemplo.instancias&lt;br /&gt;        super(Ejemplo, self).__init__(name, self.path)&lt;br /&gt;        print self.path, "listo."&lt;br /&gt;    &lt;br /&gt;    @method(dbus_interface='com.blogger.pcostesi.Ejemplo',&lt;br /&gt;            in_signature='s', out_signature='', sender_keyword='sender',&lt;br /&gt;            destination_keyword='dest')&lt;br /&gt;    def saludo(self, saludo, sender='alguien', dest='mi'):&lt;br /&gt;        print u"%s dijo a %s: %s" % (sender, dest, saludo)&lt;br /&gt;        self.dicho(sender, dest, saludo)&lt;br /&gt;    &lt;br /&gt;    @method(dbus_interface='com.blogger.pcostesi.CerrarEjemplo')&lt;br /&gt;    def salir(self):&lt;br /&gt;        self.loop.quit()&lt;br /&gt;    &lt;br /&gt;    @signal(dbus_interface='com.blogger.pcostesi.Ejemplo', signature='sss')&lt;br /&gt;    def dicho(self, s, d, saludo):&lt;br /&gt;        print 'Notificando: dicho(%s, %s, %s)' % (s, d, saludo)&lt;br /&gt;&lt;br /&gt;loop = gobject.MainLoop()&lt;br /&gt;gobject.threads_init()&lt;br /&gt;&lt;br /&gt;ejemplo3 = Ejemplo(session, loop)&lt;br /&gt;loop.run()&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;i&gt;dbus.service.signal&lt;/i&gt; recibe como parámetros:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;&lt;i&gt;dbus_interface&lt;/i&gt;: interfaz que lanzará la señal&lt;/li&gt;&lt;li&gt;&lt;i&gt;signature&lt;/i&gt;: firma que tiene la señal&lt;/li&gt;&lt;li&gt;&lt;i&gt;path_keyword&lt;/i&gt; (no puede pasarse como posicional, sólo como keyword)&lt;br /&gt;&lt;/li&gt;&lt;li&gt;&lt;i&gt;rel_path_keyword&lt;/i&gt;&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;&lt;br /&gt;Y eso es básicamente todo lo necesario de dbus.&lt;div class="blogger-post-footer"&gt;&lt;a href="&lt;$BlogSiteFeedUrl$&gt;" title="Atom feed"&gt;Site Feed&lt;/a&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/15310795-4903207255719573946?l=pcostesi.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pcostesi.blogspot.com/feeds/4903207255719573946/comments/default' title='Comentarios de la entrada'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=15310795&amp;postID=4903207255719573946' title='0 Comentarios'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/15310795/posts/default/4903207255719573946'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/15310795/posts/default/4903207255719573946'/><link rel='alternate' type='text/html' href='http://pcostesi.blogspot.com/2009/12/experimentos-con-dbus-en-python-nombre.html' title='Experimentos con DBus en Python: Nombre Conocido, Exportando Objetos y Lanzando Señales'/><author><name>Pablo Alejandro Costesich</name><uri>https://profiles.google.com/109197123025560796978</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh3.googleusercontent.com/-RiwlUyLCcO4/AAAAAAAAAAI/AAAAAAAAAkU/uNS7Zn7RHxI/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-15310795.post-3816203619655513654</id><published>2009-12-16T11:56:00.000-03:00</published><updated>2009-12-16T11:56:44.011-03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='projects'/><category scheme='http://www.blogger.com/atom/ns#' term='Notes About...'/><category scheme='http://www.blogger.com/atom/ns#' term='software'/><category scheme='http://www.blogger.com/atom/ns#' term='DBus'/><category scheme='http://www.blogger.com/atom/ns#' term='Python'/><category scheme='http://www.blogger.com/atom/ns#' term='experiments'/><title type='text'>Experimentos con DBus en Python: Escuchando Señales</title><content type='html'>&lt;b&gt;Señales&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;Si corren el último ejemplo del post anterior con dbus-monitor --session (para ver los mensajes en el bus), verán que se envían mensajes que no son "llamados" a ninguna función, sino que son emitidas en eventos. Esos mensajes se llaman &lt;i&gt;señales&lt;/i&gt;.&lt;br /&gt;&lt;br /&gt;Existen dos formas de capturar señales: una es escuchando un &lt;i&gt;bus&lt;/i&gt;, mientras que la otra es escuchando una &lt;i&gt;interfaz de un objeto&lt;/i&gt;. En un principio pueden sonar similares, pero el propósito de las mismas es radicalmente distinto. Es necesario en ambos casos disponer de un event loop.&lt;br /&gt;&lt;br /&gt;En el caso de escuchar un bus tenemos el método add_signal_receiver. Como parámetros, admite:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;&lt;i&gt;handler_function&lt;/i&gt;, una función (callable) a la que se llamará cuando se reciba la señal. Recibirá como parámetros los argumentos de la señal.&lt;/li&gt;&lt;li&gt;&lt;i&gt;signal_name&lt;/i&gt;, el nombre de la señal.&lt;/li&gt;&lt;li&gt;&lt;i&gt;dbus_interface&lt;/i&gt;, la interfaz asociada al emisor de la señal. Escucha interfaces y no objetos.&lt;/li&gt;&lt;li&gt;&lt;i&gt;bus_name&lt;/i&gt;, el Nombre Conocido de la aplicación.&lt;/li&gt;&lt;li&gt;&lt;i&gt;path&lt;/i&gt;, la dirección de un Objeto.&lt;/li&gt;&lt;li&gt;&lt;i&gt;keywords&lt;/i&gt; (ver más adelante).&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;En todas (excepto &lt;i&gt;handler_function&lt;/i&gt;) el default es &lt;i&gt;None&lt;/i&gt;, que escucha todas. Devuelve un objeto &lt;i&gt;SignalMatch&lt;/i&gt;, que tiene un método &lt;i&gt;remove()&lt;/i&gt; para eliminar la conexión. Los keywords &lt;i&gt;utf8_strings&lt;/i&gt; (booleana) y &lt;i&gt;byte_arrays&lt;/i&gt; (booleana) afectan los tipos al llamar el handler.&lt;br /&gt;&lt;br /&gt;Un ejemplo de una función catch-all sería:&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: python"&gt;from dbus import SystemBus&lt;br /&gt;from dbus.mainloop.glib import DBusGMainLoop&lt;br /&gt;import gobject&lt;br /&gt;&lt;br /&gt;def handler(*args, **kwargs):&lt;br /&gt;    print '='*10&lt;br /&gt;    print 'args:', args&lt;br /&gt;    print 'kwargs', kwargs&lt;br /&gt;&lt;br /&gt;dbus_loop = DBusGMainLoop()&lt;br /&gt;session = SystemBus(mainloop=dbus_loop)&lt;br /&gt;&lt;br /&gt;session.add_signal_receiver(handler)&lt;br /&gt;&lt;br /&gt;loop = gobject.MainLoop()&lt;br /&gt;gobject.threads_init()&lt;br /&gt;loop.run()&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Si tienen un teclado multimedia, prueben presionando play o subiendo y bajando el volumen. Podríamos refinar lo anterior para escuchar sólo elementos con la interfaz &lt;i&gt;org.freedesktop.Hal.Device&lt;/i&gt; de este modo:&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: python"&gt;session.add_signal_receiver(handler,&lt;br /&gt;        dbus_interface='org.freedesktop.Hal.Device',&lt;br /&gt;        sender_keyword='sender')&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Para escuchar señales de una interfaz asociada a un objeto, disponemos de &lt;i&gt;connect_to_signal&lt;/i&gt;. Los parámetros son:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;&lt;i&gt;handler_function&lt;/i&gt;, una función (callable) a la que se llamará cuando se reciba la señal. Recibirá como parámetros los argumentos de la señal.&lt;/li&gt;&lt;li&gt;&lt;i&gt;dbus_interface&lt;/i&gt;, la interfaz asociada al emisor de la señal.&lt;/li&gt;&lt;li&gt;&lt;i&gt;keywords&lt;/i&gt; (ver más adelante).&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;También cuenta con &lt;i&gt;utf8_strings&lt;/i&gt; y &lt;i&gt;byte_arrays&lt;/i&gt;, como &lt;i&gt;add_signal_receiver&lt;/i&gt;.&lt;br /&gt;&lt;br /&gt;Un ejemplo trivial:&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: python"&gt;from dbus import Interface&lt;br /&gt;&lt;br /&gt;class Notifier(object):&lt;br /&gt;&lt;br /&gt;    def __init__(self, bus, replace=False, app_name='', loop=None):&lt;br /&gt;        self.proxy = bus.get_object('org.freedesktop.Notifications',&lt;br /&gt;                '/org/freedesktop/Notifications')&lt;br /&gt;        self.interface = Interface(self.proxy,&lt;br /&gt;                dbus_interface='org.freedesktop.Notifications')&lt;br /&gt;        self.app_name = app_name&lt;br /&gt;        self.prev = 0&lt;br /&gt;        self.loop = loop&lt;br /&gt;        self.replace = replace&lt;br /&gt;        self.r = {1: 'expiró', 2: 'fue cerrada por el usuario',&lt;br /&gt;                3: 'se llamó a CloseNotification',&lt;br /&gt;                4: 'pasó algo no esperado'}&lt;br /&gt;        # Conectamos la señal&lt;br /&gt;        self.interface.connect_to_signal('NotificationClosed',&lt;br /&gt;                self.available)&lt;br /&gt;    &lt;br /&gt;    def notify(self, message, timeout=-1):&lt;br /&gt;        self.prev = self.interface.Notify('System', self.prev, '',&lt;br /&gt;                self.app_name, message, [], {}, timeout)&lt;br /&gt;    &lt;br /&gt;    # La señal envía como parámetros id y reason.&lt;br /&gt;    def available(self, id, reason):&lt;br /&gt;        print "La notificación %i se cerró debido a que %s." % (id,&lt;br /&gt;                self.r[reason])&lt;br /&gt;        self.loop.quit()&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Y se implementa así:&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: python"&gt;from dbus import SessionBus&lt;br /&gt;from dbus.mainloop.glib import DBusGMainLoop&lt;br /&gt;import gobject, dbus&lt;br /&gt;&lt;br /&gt;dbus_loop = DBusGMainLoop()&lt;br /&gt;session = dbus.SessionBus(mainloop=dbus_loop)&lt;br /&gt;&lt;br /&gt;loop = gobject.MainLoop()&lt;br /&gt;gobject.threads_init()&lt;br /&gt;&lt;br /&gt;notifier = Notifier(session, app_name='Prueba', loop=loop)&lt;br /&gt;def notify():&lt;br /&gt;    notifier.notify('Hola', 3*1000)&lt;br /&gt;&lt;br /&gt;gobject.timeout_add(1000, notify)&lt;br /&gt;loop.run()&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Keywords&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;Tanto add_signal_receiver como su contraparte connect_to_signal en una interfaz o proxy admiten keywords que son utilizadas para pasar información extra a nuestros handlers bajo el nombre que le asignemos. Las más usadas son:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;&lt;i&gt;sender_keyword&lt;/i&gt;: El nombre único del emisor de la señal. &lt;/li&gt;&lt;li&gt;&lt;i&gt;destination_keyword&lt;/i&gt;: El destino de la señal. &lt;/li&gt;&lt;li&gt;&lt;i&gt;interface_keyword&lt;/i&gt;: La interfaz asociada que lanza la señal.&lt;br /&gt;&lt;/li&gt;&lt;li&gt;&lt;i&gt;member_keyword&lt;/i&gt;: El nombre de la señal. &lt;/li&gt;&lt;li&gt;&lt;i&gt;path_keyword&lt;/i&gt;: El path o dirección del objeto emisor.&lt;br /&gt;&lt;/li&gt;&lt;li&gt;&lt;i&gt;message_keyword&lt;/i&gt;: el mensaje, como dbus.lowlevel.SignalMessage&lt;/li&gt;&lt;/ul&gt;Si no son especificadas, no se pasan.&lt;div class="blogger-post-footer"&gt;&lt;a href="&lt;$BlogSiteFeedUrl$&gt;" title="Atom feed"&gt;Site Feed&lt;/a&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/15310795-3816203619655513654?l=pcostesi.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pcostesi.blogspot.com/feeds/3816203619655513654/comments/default' title='Comentarios de la entrada'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=15310795&amp;postID=3816203619655513654' title='0 Comentarios'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/15310795/posts/default/3816203619655513654'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/15310795/posts/default/3816203619655513654'/><link rel='alternate' type='text/html' href='http://pcostesi.blogspot.com/2009/12/experimentos-con-dbus-en-python.html' title='Experimentos con DBus en Python: Escuchando Señales'/><author><name>Pablo Alejandro Costesich</name><uri>https://profiles.google.com/109197123025560796978</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh3.googleusercontent.com/-RiwlUyLCcO4/AAAAAAAAAAI/AAAAAAAAAkU/uNS7Zn7RHxI/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-15310795.post-1827837154081418650</id><published>2009-12-16T11:24:00.001-03:00</published><updated>2010-04-20T00:58:56.386-03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='projects'/><category scheme='http://www.blogger.com/atom/ns#' term='Notes About...'/><category scheme='http://www.blogger.com/atom/ns#' term='software'/><category scheme='http://www.blogger.com/atom/ns#' term='DBus'/><category scheme='http://www.blogger.com/atom/ns#' term='Python'/><category scheme='http://www.blogger.com/atom/ns#' term='experiments'/><title type='text'>Experimentos con DBus en Python: Loop de Eventos y Llamados Asincrónicos</title><content type='html'>&lt;b&gt;Llamadas Asincrónicas&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;Ahora, nuestros programas cuentan con la capacidad de realizar llamados por el bus a otros objetos, pero éstos son bloqueantes -- el programa se quedará esperando una respuesta antes de seguir. A menudo, esto es el comportamiento que deseamos y muchas veces nos es suficiente, pero dado que algunos servicios pueden tardar un tiempo largo (como descubrir aparatos bluetooth en el entorno), a veces queremos procesar otras cosas.&lt;br /&gt;&lt;br /&gt;Primero, y para que esto sea posible, necesitamos crear un &lt;i&gt;Event Loop&lt;/i&gt;. Es un método al que llamaremos y se encargará de esperar eventos para despacharlos a nuestras funciones.&lt;br /&gt;&lt;br /&gt;Hay dos loops disponibles: uno para Qt y otro para Gtk. Yo prefiero el de Gtk, principalmente porque tengo varias aplicaciones que usan pygtk y ninguna de pyqt4. Eso es cuestión del lector.&lt;br /&gt;&lt;br /&gt;Nota importante: se debe instanciar el loop antes de conectar el bus.&lt;br /&gt;&lt;br /&gt;En gtk, haríamos:&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: python"&gt;from dbus import SessionBus&lt;br /&gt;from dbus import SessionBus&lt;br /&gt;from dbus.mainloop.glib import DBusGMainLoop&lt;br /&gt;import gobject&lt;br /&gt;&lt;br /&gt;dbus_loop = DBusGMainLoop()&lt;br /&gt;session = dbus.SessionBus(mainloop=dbus_loop)&lt;br /&gt;&lt;br /&gt;# Colocar señales y llamados asincrónicos aquí&lt;br /&gt;&lt;br /&gt;loop = gobject.MainLoop()&lt;br /&gt;gobject.threads_init()&lt;br /&gt;loop.run()&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;O sino, también se puede definir un dbus_loop global de este modo:&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: python"&gt;from dbus import SessionBus&lt;br /&gt;from dbus import SessionBus&lt;br /&gt;from dbus.mainloop.glib import DBusGMainLoop&lt;br /&gt;import gobject&lt;br /&gt;&lt;br /&gt;DBusGMainLoop(set_as_default=True)&lt;br /&gt;session = dbus.SessionBus()&lt;br /&gt;&lt;br /&gt;# Colocar señales y llamados asincrónicos aquí&lt;br /&gt;&lt;br /&gt;loop = gobject.MainLoop()&lt;br /&gt;# threads_init activa/desactiva threads cuando llama métodos en c&lt;br /&gt;gobject.threads_init()&lt;br /&gt;loop.run()&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Prefiero el primero, por una cuestión de ser explícito el dbus_loop que elijo.&lt;br /&gt;&lt;br /&gt;Nótese, sin embargo, que ese no es el loop general. Para ello usamos gobject.MainLoop y su método run.&lt;br /&gt;&lt;br /&gt;Para PyQT4, tenemos:&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: python"&gt;# Basado en listing4.py de Roberto Alsina&lt;br /&gt;import sys&lt;br /&gt;import dbus&lt;br /&gt;from dbus import SessionBus&lt;br /&gt;from dbus.mainloop.qt import DBusQtMainLoop&lt;br /&gt;from PyQt4.QtCore import QCoreApplication&lt;br /&gt;&lt;br /&gt;app = QCoreApplication(sys.argv)&lt;br /&gt;mainloop = DBusQtMainLoop(set_as_default=True)&lt;br /&gt;dbus.set_default_main_loop(mainloop)&lt;br /&gt;&lt;br /&gt;# Colocar señales y llamados asincrónicos aquí&lt;br /&gt;&lt;br /&gt;app.exec_()&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;El código extra es para iniciar QT (framework que todavía no usé en python).&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Una vez que sabemos crear un main loop y usarlo, podemos llamar funciones asincrónicamente. Para ello, sólo tenemos que llamar al método que queremos con dos keywords:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;&lt;i&gt;reply_handler&lt;/i&gt; - Función (callable) a la que se pasa como parámetros los resultados del método remoto&lt;/li&gt;&lt;li&gt;&lt;i&gt;error_handler&lt;/i&gt; - Función (callable) que recibe un solo argumento de la clase &lt;i&gt;DBusException&lt;/i&gt;&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;A modo ilustrativo (porque es bastante fácil), una versión modificada del Notifier del post pasado:&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: python"&gt;from dbus import Interface&lt;br /&gt;&lt;br /&gt;class Notifier(object):&lt;br /&gt;&lt;br /&gt;    def __init__(self, bus, replace=False, app_name=''):&lt;br /&gt;        self.proxy = bus.get_object('org.freedesktop.Notifications',&lt;br /&gt;                '/org/freedesktop/Notifications')&lt;br /&gt;        self.interface = Interface(self.proxy,&lt;br /&gt;                dbus_interface='org.freedesktop.Notifications')&lt;br /&gt;        self.app_name = app_name&lt;br /&gt;        self.prev = 0&lt;br /&gt;        self.replace = replace&lt;br /&gt;    &lt;br /&gt;    def notify(self, message, timeout=-1):&lt;br /&gt;        self.interface.Notify('System', self.prev, '',&lt;br /&gt;                self.app_name, message, [], {}, timeout,&lt;br /&gt;                reply_handler=self.reply, error_handler=self.error)&lt;br /&gt;    &lt;br /&gt;    def reply(self, n):&lt;br /&gt;        self.prev = n&lt;br /&gt;    &lt;br /&gt;    def error(self, exception):&lt;br /&gt;        print exception&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Y para implementarlo:&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: python"&gt;from dbus import SessionBus&lt;br /&gt;from dbus.mainloop.glib import DBusGMainLoop&lt;br /&gt;import gobject, dbus&lt;br /&gt;&lt;br /&gt;# Creamos el loop de dbus y la sesión&lt;br /&gt;dbus_loop = DBusGMainLoop()&lt;br /&gt;session = dbus.SessionBus(mainloop=dbus_loop)&lt;br /&gt;&lt;br /&gt;# Creamos el loop de eventos&lt;br /&gt;loop = gobject.MainLoop()&lt;br /&gt;gobject.threads_init()&lt;br /&gt;&lt;br /&gt;notifier = Notifier(session, app_name='Prueba')&lt;br /&gt;&lt;br /&gt;def notify():&lt;br /&gt;    notifier.notify('Hola', 15*1000)&lt;br /&gt;    loop.quit()&lt;br /&gt;&lt;br /&gt;# Llama a los cinco segundos el método notify. Esto lo hacemos&lt;br /&gt;# porque no estamos dentro de una aplicación de pygtk, por lo&lt;br /&gt;# que gobject no puede "escuchar" nuestros eventos sin modificar&lt;br /&gt;# el loop.&lt;br /&gt;gobject.timeout_add(5000, notify)&lt;br /&gt;&lt;br /&gt;loop.run()&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Probablemente, si la aplicación es sólo-consola, quieran usar threads para manejar el event-loop sin bloquear otras tareas. Una posible solución puede encontrarse &lt;a href="http://www.jejik.com/articles/2007/01/python-gstreamer_threading_and_the_main_loop/"&gt;aquí&lt;/a&gt;, aunque prefiero usar threads.&lt;div class="blogger-post-footer"&gt;&lt;a href="&lt;$BlogSiteFeedUrl$&gt;" title="Atom feed"&gt;Site Feed&lt;/a&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/15310795-1827837154081418650?l=pcostesi.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pcostesi.blogspot.com/feeds/1827837154081418650/comments/default' title='Comentarios de la entrada'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=15310795&amp;postID=1827837154081418650' title='0 Comentarios'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/15310795/posts/default/1827837154081418650'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/15310795/posts/default/1827837154081418650'/><link rel='alternate' type='text/html' href='http://pcostesi.blogspot.com/2009/12/experimentos-con-dbus-en-python-loop-de.html' title='Experimentos con DBus en Python: Loop de Eventos y Llamados Asincrónicos'/><author><name>Pablo Alejandro Costesich</name><uri>https://profiles.google.com/109197123025560796978</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh3.googleusercontent.com/-RiwlUyLCcO4/AAAAAAAAAAI/AAAAAAAAAkU/uNS7Zn7RHxI/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-15310795.post-8360233651864342285</id><published>2009-12-15T18:29:00.002-03:00</published><updated>2010-04-30T12:21:48.899-03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Syntax'/><category scheme='http://www.blogger.com/atom/ns#' term='Hacks'/><category scheme='http://www.blogger.com/atom/ns#' term='Mathematics'/><category scheme='http://www.blogger.com/atom/ns#' term='LaTeX'/><category scheme='http://www.blogger.com/atom/ns#' term='Code'/><title type='text'>Syntax Highlighting &amp; LaTeX en Blogger</title><content type='html'>Como estudiante de Ing. en Informática, a veces siento la necesidad de publicar código y fórmulas... El problema es que por los medios habituales se complica, y en Blogger no hay ninguna herramienta por defecto para solucionarlo. ¿Qué hacer? O nos pasamos a Wordpress, o usamos &lt;a href="http://blog.cartercole.com/2009/10/awesome-syntax-highlighting-made-easy.html"&gt;un poco de magia en javascript&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;La instalación de &lt;a href="http://alexgorbatchev.com/wiki/SyntaxHighlighter"&gt;SyntaxHighlighter&lt;/a&gt; es realmente sencilla, sólo cortar y pegar.&lt;br /&gt;&lt;br /&gt;¿Y para LaTeX? &lt;a href="http://watchmath.com/vlog/?p=438"&gt;Todavía más sencillo&lt;/a&gt;, ya que es integrable con el sistema de blogger para gadgets.&lt;br /&gt;&lt;br /&gt;La potencia de estos sistemas es realmente interesante. Uno puede escribir cosas como:&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: python"&gt;import this&lt;br /&gt;&lt;br /&gt;for i in xrange(10):&lt;br /&gt;    print i&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;class Test(object):&lt;br /&gt;&lt;br /&gt;    def __init__(self):&lt;br /&gt;        self.message = 'hello!'&lt;br /&gt;&lt;br /&gt;    def say_hello(self):&lt;br /&gt;        print self.message&lt;br /&gt;        return self.message&lt;br /&gt;&lt;br /&gt;    def less_than_five(self, n):&lt;br /&gt;        if 5 &amp;lt; n:&lt;br /&gt;            print '%i is greater than five'&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;O...&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;$\setlength{\unitlength}{1mm}\begin{picture}(60, 40)\put(30, 20){\vector(1, 0){30}}\put(30, 20){\vector(4, 1){20}}\put(30, 20){\vector(3, 1){25}}\put(30, 20){\vector(2, 1){30}}\put(30, 20){\vector(1, 2){10}}\thicklines\put(30, 20){\vector(-4, 1){30}}\put(30, 20){\vector(-1, 4){5}}\thinlines\put(30, 20){\vector(-1, -1){5}}\put(30, 20){\vector(-1, -4){5}}\end{picture}$&lt;br /&gt;&lt;br /&gt;(&lt;a href="http://www.watchmath.blogspot.com/"&gt;Ejemplo de Watchmath&lt;/a&gt;)&lt;div class="blogger-post-footer"&gt;&lt;a href="&lt;$BlogSiteFeedUrl$&gt;" title="Atom feed"&gt;Site Feed&lt;/a&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/15310795-8360233651864342285?l=pcostesi.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pcostesi.blogspot.com/feeds/8360233651864342285/comments/default' title='Comentarios de la entrada'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=15310795&amp;postID=8360233651864342285' title='2 Comentarios'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/15310795/posts/default/8360233651864342285'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/15310795/posts/default/8360233651864342285'/><link rel='alternate' type='text/html' href='http://pcostesi.blogspot.com/2009/12/syntax-highlighting-latex-en-blogger.html' title='Syntax Highlighting &amp; LaTeX en Blogger'/><author><name>Pablo Alejandro Costesich</name><uri>https://profiles.google.com/109197123025560796978</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh3.googleusercontent.com/-RiwlUyLCcO4/AAAAAAAAAAI/AAAAAAAAAkU/uNS7Zn7RHxI/s512-c/photo.jpg'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-15310795.post-6400565774327177141</id><published>2009-12-15T17:41:00.129-03:00</published><updated>2009-12-16T14:40:03.872-03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='projects'/><category scheme='http://www.blogger.com/atom/ns#' term='Notes About...'/><category scheme='http://www.blogger.com/atom/ns#' term='software'/><category scheme='http://www.blogger.com/atom/ns#' term='DBus'/><category scheme='http://www.blogger.com/atom/ns#' term='Python'/><category scheme='http://www.blogger.com/atom/ns#' term='experiments'/><title type='text'>Experimentos con DBus en Python: Buses, Proxies, Interfaces y Tipos de datos</title><content type='html'>Voy a seguir el orden del &lt;a href="http://dbus.freedesktop.org/doc/dbus-python/doc/tutorial.html"&gt;tutorial oficial&lt;/a&gt;, por si algo no queda claro.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Buses&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;En DBus existen dos clases de buses de comunicación: el bus de sistema (dbus.SystemBus) y el bus de sesión (dbus.SessionBus). Ambos se manejan más o menos del mismo modo, pero sus roles son radicalmente distintos.&lt;br /&gt;&lt;br /&gt;El bus de sistema sirve, principalmente, para la comunicación entre daemons y otros servicios centrales. El bus de sesión existe por cada usuario, y es responsable de comunicar servicios de modo más "local" y no system-wide.&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: python"&gt;import dbus&lt;br /&gt;&lt;br /&gt;system = dbus.SystemBus()&lt;br /&gt;session = dbus.SessionBus()&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;En DBus, cada aplicación puede referirse a sí misma con un "&lt;i&gt;Nombre Conocido&lt;/i&gt;". Generalmente, tiene la forma de una URI invertida, como puede ser &lt;i&gt;org.freedesktop.Hal&lt;/i&gt;. Adicionalmente, cada aplicación puede exponer varios objetos, tales como un Manager (&lt;i&gt;org.freedesktop.Hal.Manager&lt;/i&gt;) o, si hablamos de un procesador de texto, podría exponer los archivos abiertos.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Proxies&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;Entonces, por ahora sólo tenemos los buses. ¿Qué podemos hacer con ellos? Obtener &lt;i&gt;proxies&lt;/i&gt; del bus. DBus trabaja con un sistema exponiendo una interfaz y traduciendo los llamados, y para ello se debe crear una clase de objeto que se llama &lt;i&gt;proxy&lt;/i&gt;. Éste actúa como intermediario al que se le pueden aplicar métodos, y que automáticamente ejecutará el envío del mensaje, y es el equivalente a un objeto de una aplicación.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Interfaces&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;Una vez hecho esto, podemos llamar algunos métodos sobre nuestro proxy. Para hacer esto, vamos a adquirir una &lt;i&gt;Interfaz&lt;/i&gt;. Una interfaz, como seguramente saben, es un conjunto de funciones que se asocian a un objeto y le dan una capacidad determinada. Lo bueno de este sistema es que un objeto puede tener más de una interfaz disponible, con lo que podríamos aplicar sólo el subconjunto de acciones que necesitamos.&lt;br /&gt;&lt;br /&gt;Vamos a ver un ejemplo simple de llamar al sistema de Notificaciones en Linux:&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: python"&gt;import dbus&lt;br /&gt;&lt;br /&gt;# No es necesario el SystemBus, sólo el SessionBus&lt;br /&gt;system = dbus.SystemBus()&lt;br /&gt;session = dbus.SessionBus()&lt;br /&gt;&lt;br /&gt;# Obtenemos el proxy con get_object&lt;br /&gt;# NOTA: get_object admite como parámetros:&lt;br /&gt;#             * El Nombre Conocido de la aplicación&lt;br /&gt;#             * El nombre del Objeto al que queremos acceder&lt;br /&gt;&lt;br /&gt;proxy = session.get_object('org.freedesktop.Notifications',&lt;br /&gt;        '/org/freedesktop/Notifications')&lt;br /&gt;&lt;br /&gt;# Y le asociamos una interfaz. En nuestro caso, la interfaz&lt;br /&gt;# de Notify daemon se llama igual que el Nombre Conocido&lt;br /&gt;# de la aplicación que expone el servicio.&lt;br /&gt;&lt;br /&gt;interface = dbus.Interface(proxy,&lt;br /&gt;                dbus_interface='org.freedesktop.Notifications')&lt;br /&gt;&lt;br /&gt;# Llamamos al método Notify en la interfaz para mostrar un&lt;br /&gt;# cartel con un saludo&lt;br /&gt;&lt;br /&gt;interface.Notify('System', 0, '', 'Title', 'Message', [], {}, -1)&lt;br /&gt;&lt;br /&gt;# Los detalles del método Notify pueden encontrarse en&lt;br /&gt;# http://www.galago-project.org/specs/notification/0.9/x408.html&lt;br /&gt;# Pero lo anterior cubre el 99% de los usos&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Notarán, sin embargo, que los métodos exportados de DBus en los proxies no son lo más Pythónico que existe. Podemos arreglar eso y sumar un par de features aprovechando la programación orientada a objetos:&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: python"&gt;from dbus import Interface&lt;br /&gt;&lt;br /&gt;class Notifier(object):&lt;br /&gt;&lt;br /&gt;    def __init__(self, bus, replace=False, app_name=''):&lt;br /&gt;        self.proxy = bus.get_object('org.freedesktop.Notifications',&lt;br /&gt;                '/org/freedesktop/Notifications')&lt;br /&gt;        self.interface = Interface(self.proxy,&lt;br /&gt;                dbus_interface='org.freedesktop.Notifications')&lt;br /&gt;        self.app_name = app_name&lt;br /&gt;        self.prev = 0&lt;br /&gt;        self.replace = replace&lt;br /&gt;    &lt;br /&gt;    def notify(self, message, timeout=-1):&lt;br /&gt;        self.prev = self.interface.Notify('System',&lt;br /&gt;                self.prev, '', self.app_name, message, [], {}, timeout)&lt;br /&gt;        if self.replace:&lt;br /&gt;            self.prev = 0&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Y si queremos implementarla, simplemente haríamos:&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: python"&gt;from dbus import SessionBus&lt;br /&gt;&lt;br /&gt;session = SessionBus()&lt;br /&gt;notifier = Notifier(session, app_name='Prueba')&lt;br /&gt;notifier.notify('Hola', 15*1000) # Tiempo en ms.&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Entonces, generalmente haremos lo siguiente:&lt;br /&gt;Instanciar un bus --&gt; Obtener un proxy --&gt; Obtener una interfaz asociada --&gt; Llamar métodos en la interfaz.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Tipos de Datos&lt;/b&gt; &lt;br /&gt;&lt;br /&gt;Si ya estuvieron metiendo mano a los ejemplos anteriores, notarán que no pueden enviar cualquier clase de datos. Esto pasa porque DBus es un sistema &lt;i&gt;strongly typed&lt;/i&gt; (como Python) y &lt;i&gt;static typed&lt;/i&gt;. Los parámetros tienen un tipo específico de datos, y cada método tiene una &lt;i&gt;firma&lt;/i&gt;. No me voy a detener mucho en esto, ya que la &lt;a href="http://dbus.freedesktop.org/doc/dbus-python/doc/tutorial.html#data-types"&gt;documentación oficial&lt;/a&gt; es clara en ello.&lt;div class="blogger-post-footer"&gt;&lt;a href="&lt;$BlogSiteFeedUrl$&gt;" title="Atom feed"&gt;Site Feed&lt;/a&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/15310795-6400565774327177141?l=pcostesi.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pcostesi.blogspot.com/feeds/6400565774327177141/comments/default' title='Comentarios de la entrada'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=15310795&amp;postID=6400565774327177141' title='0 Comentarios'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/15310795/posts/default/6400565774327177141'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/15310795/posts/default/6400565774327177141'/><link rel='alternate' type='text/html' href='http://pcostesi.blogspot.com/2009/12/experimentos-con-dbus-en-python-lo.html' title='Experimentos con DBus en Python: Buses, Proxies, Interfaces y Tipos de datos'/><author><name>Pablo Alejandro Costesich</name><uri>https://profiles.google.com/109197123025560796978</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh3.googleusercontent.com/-RiwlUyLCcO4/AAAAAAAAAAI/AAAAAAAAAkU/uNS7Zn7RHxI/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-15310795.post-2391474448928783940</id><published>2009-12-15T15:58:00.009-03:00</published><updated>2011-06-16T16:32:39.195-03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='projects'/><category scheme='http://www.blogger.com/atom/ns#' term='Notes About...'/><category scheme='http://www.blogger.com/atom/ns#' term='software'/><category scheme='http://www.blogger.com/atom/ns#' term='DBus'/><category scheme='http://www.blogger.com/atom/ns#' term='Python'/><category scheme='http://www.blogger.com/atom/ns#' term='experiments'/><title type='text'>Experimentos con DBus en Python: Intro</title><content type='html'>Hace unos días, Roberto Alsina expuso en &lt;a href="http://lateral.netmanagers.com.ar/stories/BBS54.html"&gt;esta entrada&lt;/a&gt; la necesidad que tenemos muchos de generar una respuesta visual ante ciertas acciones de nuestros equipos, más concretamente, eventos del teclado y ACPI. En particular, esta era una necesidad evidente para mí, que uso un entorno de escritorio poco común y muy minimalista (Openbox + LXDE). Fue por eso que decidí hacer un fork a &lt;a href="http://github.com/ralsina/dbus-reactor"&gt;su proyecto&lt;/a&gt; en github y probar las posibilidades en &lt;a href="http://github.com/pcostesi/dbus-reactor"&gt;mi rama&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Considero mi trabajo en el código del mismo un borrador, no más que una mera investigación antes del trabajo real. Como la API de DBus en Python es nada menos que caótica y áspera, trataré de documentar mis progresos a modo de anotación.&lt;br /&gt;&lt;br /&gt;Publiqué &lt;strike&gt;Voy a ir publicando (si el tiempo me lo permite)&lt;/strike&gt; una serie de posts (&lt;a href="http://pcostesi.blogspot.com/2009/12/experimentos-con-dbus-en-python-lo.html"&gt;parte 1&lt;/a&gt;, &lt;a href="http://pcostesi.blogspot.com/2009/12/experimentos-con-dbus-en-python-loop-de.html"&gt;parte 2&lt;/a&gt;, &lt;a href="http://pcostesi.blogspot.com/2009/12/experimentos-con-dbus-en-python.html"&gt;parte 3&lt;/a&gt; y &lt;a href="http://pcostesi.blogspot.com/2009/12/experimentos-con-dbus-en-python-nombre.html"&gt;parte 4&lt;/a&gt;), &lt;strike&gt;posiblemente breves&lt;/strike&gt;, sobre dbus y su uso en Python.&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: python"&gt;import dbus&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;a href="&lt;$BlogSiteFeedUrl$&gt;" title="Atom feed"&gt;Site Feed&lt;/a&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/15310795-2391474448928783940?l=pcostesi.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pcostesi.blogspot.com/feeds/2391474448928783940/comments/default' title='Comentarios de la entrada'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=15310795&amp;postID=2391474448928783940' title='0 Comentarios'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/15310795/posts/default/2391474448928783940'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/15310795/posts/default/2391474448928783940'/><link rel='alternate' type='text/html' href='http://pcostesi.blogspot.com/2009/12/experimentos-con-dbus-en-python-intro.html' title='Experimentos con DBus en Python: Intro'/><author><name>Pablo Alejandro Costesich</name><uri>https://profiles.google.com/109197123025560796978</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh3.googleusercontent.com/-RiwlUyLCcO4/AAAAAAAAAAI/AAAAAAAAAkU/uNS7Zn7RHxI/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-15310795.post-4773878352015903470</id><published>2009-12-10T00:04:00.002-03:00</published><updated>2009-12-16T15:09:56.722-03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Pygame'/><category scheme='http://www.blogger.com/atom/ns#' term='Ordano'/><category scheme='http://www.blogger.com/atom/ns#' term='Pyglet'/><category scheme='http://www.blogger.com/atom/ns#' term='Esteban'/><category scheme='http://www.blogger.com/atom/ns#' term='Python'/><title type='text'>Conway's Game of Life</title><content type='html'>&lt;a href="http://python.pastebin.com/f487eba21" target="_blank"&gt;Programé&lt;/a&gt; un &lt;a href="http://en.wikipedia.org/wiki/Conway%27s_Game_of_Life" target="_blank"&gt;juego de la vida&lt;/a&gt;. Está en &lt;a href="http://python.org/" target="_blank"&gt;Python&lt;/a&gt;, el algoritmo es &lt;b&gt;horrible&lt;/b&gt; y salió en menos de una hora (el bugfixing tomó un poco más). Necesita &lt;a href="http://www.pyglet.org/" target="_blank"&gt;Pyglet&lt;/a&gt;, porque fue un &lt;a href="http://estebanordano.com.ar/flamewar-pyglet-vs-pygame/" target="_blank"&gt;desafío de un amigo&lt;/a&gt; para probar ese &lt;a href="http://es.wikipedia.org/wiki/Framework" target="_blank"&gt;framework&lt;/a&gt;. Me había olvidado de lo gratificante que puede ser competir. :P&lt;br /&gt;&lt;br /&gt;EDITADO:&lt;br /&gt;&lt;br /&gt;El código, por si algo le pasa en pastebin:&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: python"&gt;#!/usr/bin/env python&lt;br /&gt;# -*- coding: utf-8 -*-&lt;br /&gt;&lt;br /&gt;from pyglet.window import Window, mouse, key&lt;br /&gt;from pyglet.app import run, exit&lt;br /&gt;from pyglet.clock import schedule_interval, unschedule&lt;br /&gt;from pyglet.image import load&lt;br /&gt;from pyglet.gl import glColor3f, glRectf, gl&lt;br /&gt;&lt;br /&gt;class World(Window):&lt;br /&gt;    """ The World! """&lt;br /&gt;&lt;br /&gt;    def __init__(self, x=20, y=20, speed=1, wrap=False):&lt;br /&gt;        super(World, self).__init__(500, 500)&lt;br /&gt;        self.speed = speed&lt;br /&gt;        self.wrap = wrap&lt;br /&gt;        self.x = range(x)&lt;br /&gt;        self.y = range(y)&lt;br /&gt;        self.x_size = x&lt;br /&gt;        self.y_size = y&lt;br /&gt;        self.grid = [[0 for v in xrange(x)] for i in xrange(y)]&lt;br /&gt;        self.alive_matrix = [[(0, 0) for v in xrange(x)] for i in xrange(y)]&lt;br /&gt;        self.__running = False&lt;br /&gt;&lt;br /&gt;    def start(self):&lt;br /&gt;        if self.__running:&lt;br /&gt;            unschedule(self.update)&lt;br /&gt;            self.__running = False&lt;br /&gt;        else:&lt;br /&gt;            schedule_interval(self.update, self.speed)&lt;br /&gt;            self.__running = True&lt;br /&gt;&lt;br /&gt;    def set_alive(self, x, y, state):&lt;br /&gt;        if self.__running:&lt;br /&gt;            raise Exception("The Game is running!")&lt;br /&gt;        elif state:&lt;br /&gt;            self.grid[y][x] = 1&lt;br /&gt;        else:&lt;br /&gt;            self.grid[y][x] = 0&lt;br /&gt;&lt;br /&gt;    def __query_neighbours(self, x, y):&lt;br /&gt;        alive = 0&lt;br /&gt;        for v, w in [(a, b) for b in xrange(y - 1, y + 2) for a in \&lt;br /&gt;                xrange(x - 1, x + 2) if (a, b) != (x, y)]:&lt;br /&gt;            if self.wrap:&lt;br /&gt;                w %= self.y_size&lt;br /&gt;                v %= self.x_size&lt;br /&gt;            elif w == self.y_size or v == self.x_size or w &lt; 0 or v &lt; 0:&lt;br /&gt;                alive += 1&lt;br /&gt;            if self.grid[w][v] == 1:&lt;br /&gt;                alive += 1&lt;br /&gt;        return alive&lt;br /&gt;&lt;br /&gt;    def evolve(self):&lt;br /&gt;#       Generate Alive Matrix&lt;br /&gt;        for x, y in ((a, b) for b in self.y for a in self.x):&lt;br /&gt;            alive_count = self.__query_neighbours(x, y)&lt;br /&gt;            cell_status = self.grid[y][x]&lt;br /&gt;            self.alive_matrix[y][x] = (cell_status, alive_count)&lt;br /&gt;&lt;br /&gt;        for x, y in ((a, b) for b in self.y for a in self.x):&lt;br /&gt;            cell, alive = self.alive_matrix[y][x]&lt;br /&gt;            if (cell == 0 and alive == 3) or (cell == 1 and alive in (2, 3)):&lt;br /&gt;                self.grid[y][x] = 1&lt;br /&gt;            else:&lt;br /&gt;                self.grid[y][x] = 0&lt;br /&gt;&lt;br /&gt;    def update(self, dt):&lt;br /&gt;        self.evolve()&lt;br /&gt;&lt;br /&gt;    def on_key_press(self, k, modifiers):&lt;br /&gt;        if key.SPACE == k:&lt;br /&gt;            self.start()&lt;br /&gt;        elif key.ESCAPE == k:&lt;br /&gt;            exit()&lt;br /&gt;&lt;br /&gt;    def on_mouse_press(self, x, y, button, modifiers):&lt;br /&gt;        if button &amp; mouse.LEFT and self.__running == False:&lt;br /&gt;            idx_x = int((self.x_size/500.0)*x)&lt;br /&gt;            idx_y = int((self.y_size/500.0)*y)&lt;br /&gt;            state = self.grid[idx_y][idx_x]&lt;br /&gt;            if state:&lt;br /&gt;                state = False&lt;br /&gt;            else:&lt;br /&gt;                state = True&lt;br /&gt;            self.set_alive(idx_x, idx_y, state)&lt;br /&gt;&lt;br /&gt;    def on_draw(self):&lt;br /&gt;        self.clear()&lt;br /&gt;        for x, y in ((a, b) for b in self.y for a in self.x):&lt;br /&gt;            if self.grid[y][x] == 1:&lt;br /&gt;                win_x_unit = 500/self.x_size&lt;br /&gt;                win_y_unit = 500/self.y_size&lt;br /&gt;                glColor3f(1, 1, 1)&lt;br /&gt;                x1 = win_x_unit * x&lt;br /&gt;                x2 = win_x_unit * (x + 1)&lt;br /&gt;                y1 = win_y_unit * y&lt;br /&gt;                y2 = win_y_unit * (y + 1)&lt;br /&gt;                glRectf(x1, y1, x2, y2)&lt;br /&gt;&lt;br /&gt;if __name__ == '__main__':&lt;br /&gt;    world = World(15, 15, 1, True)&lt;br /&gt;    run()&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;a href="&lt;$BlogSiteFeedUrl$&gt;" title="Atom feed"&gt;Site Feed&lt;/a&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/15310795-4773878352015903470?l=pcostesi.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='related' href='http://estebanordano.com.ar/flamewar-pyglet-vs-pygame/' title='Conway&apos;s Game of Life'/><link rel='replies' type='application/atom+xml' href='http://pcostesi.blogspot.com/feeds/4773878352015903470/comments/default' title='Comentarios de la entrada'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=15310795&amp;postID=4773878352015903470' title='1 Comentarios'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/15310795/posts/default/4773878352015903470'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/15310795/posts/default/4773878352015903470'/><link rel='alternate' type='text/html' href='http://pcostesi.blogspot.com/2009/12/conways-game-of-life.html' title='Conway&apos;s Game of Life'/><author><name>Pablo Alejandro Costesich</name><uri>https://profiles.google.com/109197123025560796978</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh3.googleusercontent.com/-RiwlUyLCcO4/AAAAAAAAAAI/AAAAAAAAAkU/uNS7Zn7RHxI/s512-c/photo.jpg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-15310795.post-4493713975883543234</id><published>2008-03-05T23:16:00.002-02:00</published><updated>2008-03-05T23:45:22.393-02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ikariam'/><category scheme='http://www.blogger.com/atom/ns#' term='juego'/><category scheme='http://www.blogger.com/atom/ns#' term='guerra'/><category scheme='http://www.blogger.com/atom/ns#' term='siramek'/><title type='text'>ikariam</title><content type='html'>Hace ya unos cuantos días que no había escrito nada, y menos algo relacionado con los juegos. Este blog solía tratarse sobre tecnología, pero después cambié para incluir más contenido... Así que acá vamos.&lt;br /&gt;&lt;br /&gt;Hoy voy a tratar el tema de &lt;a href="http://ikariam.es"&gt;Ikariam,&lt;/a&gt; un juego online al estilo del archiconocido -y por mí aburrido- Ogame. Al principio, no le encontraba la vuelta de lo interesante que podía ser tener uno de estos mundos y competir entre usuarios por recursos, pero no fue hasta este juego que me volví adicto al género.&lt;br /&gt;&lt;br /&gt;Paso a explicar las características del juego de forma resumida:&lt;br /&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Es un mundo persistente, que transcurre a tiempo real.&lt;/li&gt;&lt;li&gt;Obviamente, es multiplayer.&lt;/li&gt;&lt;li&gt;Se comparte una isla y los recursos en ella junto a otros jugadores, a los que se los puede atacar o aliarse a ellos.&lt;/li&gt;&lt;li&gt;El juego se basa más que nada en los recursos y economía de compra-venta. Mientras más oro, madera, mármol, vino o azufre se tenga, más poder hay.&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Como el juego corre en tiempo real y NO necesita un cliente dedicado (se puede jugar desde cualquier navegador web actual), uno puede irse a dormir, cenar tranquilo y conectarse desde un Debian o un FreeBSD sin importar el equipo.&lt;/li&gt;&lt;li&gt;Hay que tener habilidades comerciales y diplomáticas. Eso ayuda un montón.&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;Por otra parte, a pesar de que parece tan difícil de entender, no es así. Uno tiene tiempo para tomar sus decisiones ya que todo transcurre en lapsos de horas o minutos, incluso días enteros.&lt;br /&gt;&lt;br /&gt;El juego es gratis, y tiene varios servidores. Uno empieza en una isla con un solo edificio, que es el centro cívico de donde se maneja casi todo lo referente a la diplomacia y prosperidad de tu ciudad (o Polis). A medida que avanza el tiempo, uno va recolectando recursos del área y puede construir, entre otras cosas, puertos y mercados, academias y campamentos militares, murallas y castillos.&lt;br /&gt;&lt;br /&gt;Claro, mientras más pasa el tiempo, más aldeanos tendremos que tener, y más recursos gastaremos. Llegará el momento en donde tengamos que crear otros edificios para garantizar el ocio (después de todo, el ocio y placer es el principal motor de la actividad humana en el caso de tener las necesidades básicas cubiertas).&lt;br /&gt;&lt;br /&gt;Al momento de redactar estas líneas, mi pequeño imperio ha ido en franco desarrollo y ha ampliado sus fronteras comerciales con uno de los vecinos de la isla, sin contar a Siramek, quien me llevó al juego y a quien me debo como un aliado de por vida.&lt;br /&gt;&lt;br /&gt;Cabe hacer una aclaración, todavía no entré al foro del juego, cosa que suelo hacer en otros lados.&lt;div class="blogger-post-footer"&gt;&lt;a href="&lt;$BlogSiteFeedUrl$&gt;" title="Atom feed"&gt;Site Feed&lt;/a&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/15310795-4493713975883543234?l=pcostesi.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pcostesi.blogspot.com/feeds/4493713975883543234/comments/default' title='Comentarios de la entrada'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=15310795&amp;postID=4493713975883543234' title='4 Comentarios'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/15310795/posts/default/4493713975883543234'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/15310795/posts/default/4493713975883543234'/><link rel='alternate' type='text/html' href='http://pcostesi.blogspot.com/2008/03/ikariam.html' title='ikariam'/><author><name>Pablo Alejandro Costesich</name><uri>https://profiles.google.com/109197123025560796978</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh3.googleusercontent.com/-RiwlUyLCcO4/AAAAAAAAAAI/AAAAAAAAAkU/uNS7Zn7RHxI/s512-c/photo.jpg'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-15310795.post-8924300894803978000</id><published>2008-02-21T15:07:00.009-02:00</published><updated>2008-02-24T01:31:59.526-02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='todos mueren'/><category scheme='http://www.blogger.com/atom/ns#' term='lestat'/><category scheme='http://www.blogger.com/atom/ns#' term='juego'/><category scheme='http://www.blogger.com/atom/ns#' term='guerra'/><category scheme='http://www.blogger.com/atom/ns#' term='defcon'/><category scheme='http://www.blogger.com/atom/ns#' term='nuclear'/><title type='text'>DEFCON III</title><content type='html'>&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_D0WLQzWgpCk/R72x9r8gfJI/AAAAAAAAAHI/uUFI4bvVhmg/s1600-h/lalala1.bmp"&gt;&lt;img style="margin: 0pt 10px 10px 0pt; float: left; cursor: pointer; width: 296px; height: 222px;" src="http://2.bp.blogspot.com/_D0WLQzWgpCk/R72x9r8gfJI/AAAAAAAAAHI/uUFI4bvVhmg/s400/lalala1.bmp" alt="" id="BLOGGER_PHOTO_ID_5169483620617845906" border="0" /&gt;&lt;/a&gt;Al momento que estoy escribiendo esta entrada, a la madrugada de este día ocurrió ese eclipse lunar y yo me había tomado una copa de New Age que encontré en la heladera y me daba pena desperdiciar. ¿Qué tiene que ver? Que, obviamente como pueden ver, no estaba en pleno uso de mis facultades mentales al momento de la batalla... Que vendría a ser una tercera guerra mundial.&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_D0WLQzWgpCk/R72y-78gfKI/AAAAAAAAAHQ/RfkEfLb0HhE/s1600-h/lalala2.bmp"&gt;&lt;img style="margin: 0pt 0pt 10px 10px; float: right; cursor: pointer; width: 310px; height: 232px;" src="http://3.bp.blogspot.com/_D0WLQzWgpCk/R72y-78gfKI/AAAAAAAAAHQ/RfkEfLb0HhE/s400/lalala2.bmp" alt="" id="BLOGGER_PHOTO_ID_5169484741604310178" border="0" /&gt;&lt;/a&gt;Claro que para jugar DEFCON ya hay que estar un poco loco, pero lo mejor de todo es que mientras más cosas que son impredecibles hacés, mejor. En mi caso, jugué como &lt;span style="font-style: italic;"&gt;casi&lt;/span&gt; siempre&lt;span style="font-style: italic;"&gt;&lt;span style="font-style: italic;"&gt;, &lt;/span&gt;&lt;/span&gt;pero hice un par de ataques que son dignos de un loco. ¿Quién en su sano juicio habría de poner los submarinos en modo de ataque AL LADO de las bases aéreas? ¿Quién mandaría un ataque por el camino más largo y otro por el más corto, dejando la otra mitad de la flota sin hacer nada? Sólo un loco... O borracho (con una copa ya me basta).&lt;br /&gt;&lt;br /&gt;Bueh, a pesar de eso, gané, pero por muy poco. Acá subí unas fotos de unas cuantas partidas, así se entretienen.&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_D0WLQzWgpCk/R723lL8gfLI/AAAAAAAAAHY/OI2b1wbgYGo/s1600-h/lalala3.bmp"&gt;&lt;img style="margin: 0pt 10px 10px 0pt; float: left; cursor: pointer; width: 304px; height: 228px;" src="http://4.bp.blogspot.com/_D0WLQzWgpCk/R723lL8gfLI/AAAAAAAAAHY/OI2b1wbgYGo/s400/lalala3.bmp" alt="" id="BLOGGER_PHOTO_ID_5169489796780817586" border="0" /&gt;&lt;/a&gt;Cabe destacar que los juegos de DEFCON profesionales duran poco tiempo, apenas llegan a las dos horas y son masacres nucleares. En este último que jugamos con Les, pusimos los radares de tal forma que podíamos ver todo el territorio del otro, lo cual nos posibilitó atacar no bien fue DEFCON I y matarnos en menos de 3 horas. Lindo juego, fue rápido y casi profesional. Claro que las locuras que hicimos fue porque estábamos ambos pasados en una o dos copas.&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_D0WLQzWgpCk/R725v78gfMI/AAAAAAAAAHg/jU8pFWDwj-A/s1600-h/lalala4.bmp"&gt;&lt;img style="margin: 0pt 0pt 10px 10px; float: right; cursor: pointer; width: 270px; height: 203px;" src="http://3.bp.blogspot.com/_D0WLQzWgpCk/R725v78gfMI/AAAAAAAAAHg/jU8pFWDwj-A/s400/lalala4.bmp" alt="" id="BLOGGER_PHOTO_ID_5169492180487666882" border="0" /&gt;&lt;/a&gt;Ah, otra cosa... Los juegos se extienden mucho por algo que se llama Disuasión Nuclear, que no es ni más ni menos que "Yo no te ataco para que vos no me ataques". El que jugó &lt;span style="font-style: italic;"&gt;Balance of Power&lt;/span&gt; o vio &lt;span style="font-style: italic;"&gt;Dr. Strangelove or how I learned to stop worrying and love the bomb&lt;/span&gt; sabe bien lo que digo.&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_D0WLQzWgpCk/R7260r8gfNI/AAAAAAAAAHo/MNYwcqnH6sc/s1600-h/overkill.bmp"&gt;&lt;img style="margin: 0pt 10px 10px 0pt; float: left; cursor: pointer; width: 270px; height: 204px;" src="http://2.bp.blogspot.com/_D0WLQzWgpCk/R7260r8gfNI/AAAAAAAAAHo/MNYwcqnH6sc/s400/overkill.bmp" alt="" id="BLOGGER_PHOTO_ID_5169493361603673298" border="0" /&gt;&lt;/a&gt;Último offtopic: la película que mencioné es de Kubrick, uno de los mejores (sino el mejor) director de cine de la historia.&lt;span style="font-style: italic;"&gt; En 2001, Odisea en el Espacio&lt;/span&gt;, Kubrick al parecer le encargó a Pink Floyd que haga la música de la última parte de la película... Track que pasó a llamarse Echoes, el cual estoy escuchando ahora.&lt;div class="blogger-post-footer"&gt;&lt;a href="&lt;$BlogSiteFeedUrl$&gt;" title="Atom feed"&gt;Site Feed&lt;/a&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/15310795-8924300894803978000?l=pcostesi.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pcostesi.blogspot.com/feeds/8924300894803978000/comments/default' title='Comentarios de la entrada'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=15310795&amp;postID=8924300894803978000' title='3 Comentarios'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/15310795/posts/default/8924300894803978000'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/15310795/posts/default/8924300894803978000'/><link rel='alternate' type='text/html' href='http://pcostesi.blogspot.com/2008/02/defcon-iii.html' title='DEFCON III'/><author><name>Pablo Alejandro Costesich</name><uri>https://profiles.google.com/109197123025560796978</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh3.googleusercontent.com/-RiwlUyLCcO4/AAAAAAAAAAI/AAAAAAAAAkU/uNS7Zn7RHxI/s512-c/photo.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://2.bp.blogspot.com/_D0WLQzWgpCk/R72x9r8gfJI/AAAAAAAAAHI/uUFI4bvVhmg/s72-c/lalala1.bmp' height='72' width='72'/><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-15310795.post-1626974404608690931</id><published>2008-02-18T12:00:00.003-02:00</published><updated>2008-02-24T01:30:07.327-02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='linux'/><category scheme='http://www.blogger.com/atom/ns#' term='comic'/><title type='text'>XKCD</title><content type='html'>&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://imgs.xkcd.com/comics/network.png"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer; width: 400px;" src="http://imgs.xkcd.com/comics/network.png" alt="" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;Pueden considerarme un nerd, freak, geek... Todo lo que quieran, pero mi sueño sin cumplir es este:&lt;br /&gt;&lt;br /&gt;Eso es un comic de &lt;a href="http://xkcd.com/350/"&gt;XKCD&lt;/a&gt;. Siempre me sentí identificado con ese comic!!!&lt;div class="blogger-post-footer"&gt;&lt;a href="&lt;$BlogSiteFeedUrl$&gt;" title="Atom feed"&gt;Site Feed&lt;/a&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/15310795-1626974404608690931?l=pcostesi.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pcostesi.blogspot.com/feeds/1626974404608690931/comments/default' title='Comentarios de la entrada'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=15310795&amp;postID=1626974404608690931' title='1 Comentarios'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/15310795/posts/default/1626974404608690931'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/15310795/posts/default/1626974404608690931'/><link rel='alternate' type='text/html' href='http://pcostesi.blogspot.com/2008/02/xkcd.html' title='XKCD'/><author><name>Pablo Alejandro Costesich</name><uri>https://profiles.google.com/109197123025560796978</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh3.googleusercontent.com/-RiwlUyLCcO4/AAAAAAAAAAI/AAAAAAAAAkU/uNS7Zn7RHxI/s512-c/photo.jpg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-15310795.post-3474194045529970115</id><published>2008-02-18T03:13:00.003-02:00</published><updated>2008-02-24T01:29:52.268-02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='todos mueren'/><category scheme='http://www.blogger.com/atom/ns#' term='juego'/><category scheme='http://www.blogger.com/atom/ns#' term='guerra'/><title type='text'>WolfKing</title><content type='html'>Ph33r M3 1337 K3YB04RD!&lt;br /&gt;&lt;br /&gt;Es impresionante, la verdad que anda excelente y te deja menos adoloridas las manos si es que tenés síndrome del túnel carpiano como yo. Toda persona que juegue fps' en una portátil o escritorio reducido que se precie debe tener un teclado para juegos, como este.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_D0WLQzWgpCk/R7mW0r8gfFI/AAAAAAAAAGQ/KTrhBoPbk8A/s1600-h/18-02-08_1226.jpg"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer;" src="http://2.bp.blogspot.com/_D0WLQzWgpCk/R7mW0r8gfFI/AAAAAAAAAGQ/KTrhBoPbk8A/s400/18-02-08_1226.jpg" alt="" id="BLOGGER_PHOTO_ID_5168327879278230610" border="0" /&gt;&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;a href="&lt;$BlogSiteFeedUrl$&gt;" title="Atom feed"&gt;Site Feed&lt;/a&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/15310795-3474194045529970115?l=pcostesi.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pcostesi.blogspot.com/feeds/3474194045529970115/comments/default' title='Comentarios de la entrada'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=15310795&amp;postID=3474194045529970115' title='0 Comentarios'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/15310795/posts/default/3474194045529970115'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/15310795/posts/default/3474194045529970115'/><link rel='alternate' type='text/html' href='http://pcostesi.blogspot.com/2008/02/wolfking.html' title='WolfKing'/><author><name>Pablo Alejandro Costesich</name><uri>https://profiles.google.com/109197123025560796978</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh3.googleusercontent.com/-RiwlUyLCcO4/AAAAAAAAAAI/AAAAAAAAAkU/uNS7Zn7RHxI/s512-c/photo.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://2.bp.blogspot.com/_D0WLQzWgpCk/R7mW0r8gfFI/AAAAAAAAAGQ/KTrhBoPbk8A/s72-c/18-02-08_1226.jpg' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-15310795.post-8300414838832834330</id><published>2008-02-18T03:02:00.004-02:00</published><updated>2008-02-23T03:13:49.094-02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='java'/><category scheme='http://www.blogger.com/atom/ns#' term='juego'/><category scheme='http://www.blogger.com/atom/ns#' term='rolgps'/><category scheme='http://www.blogger.com/atom/ns#' term='regnum'/><category scheme='http://www.blogger.com/atom/ns#' term='manuales'/><category scheme='http://www.blogger.com/atom/ns#' term='sunos'/><title type='text'>rolgps</title><content type='html'>Por un tiempo fue un excelente proyecto. Me refiero a esta "enciclopedia de coordenadas" que hicimos entre Arlick, Sunos y yo. El proyecto en su etapa inicial alcanzó todas las expectativas y las superó conforme el paso del tiempo.&lt;br /&gt;&lt;br /&gt;El problema viene con las licencias. No tengo tiempo para discutir si puedo o no usar el material del juego, quiero concentrarme en los estudios y vacaciones, no en un juego que debería ser un espacio de ocio. Alguno de estos días voy a mandar un mensaje a los que poseen la licencia de propiedad intelectual para pedirles permiso de uso en la aplicación.&lt;br /&gt;&lt;br /&gt;Por otra parte, prefiero dejar el proyecto en segundo plano por la tecnología que estoy usando. Los threads hacen un trabajo HERMOSO en el tema de búsquedas, pero tener que esperar 3 segundos para parsear un archivo completo de XML me enerva. Tiene que haber una forma más rápida sin usar bases de datos y manteniendo la tecnología de XML para leer un archivo de más de 8000 líneas (de los cuales hay arriba de 1000 objetos).&lt;br /&gt;&lt;br /&gt;El nuevo proyecto planeaba implementar un mapa, poner las opciones por ficheros XML, tener un motor totalmente independiente de la aplicación (la idea era tener dos, de los cuales elegir) y ser totalmente actualizable desde la base. ¡Incluso, la idea era que pueda admitir "mods" para distintos juegos! Más adelante podíamos incluir áreas, trazados de mapas por vectores y miles de cosas por la modularidad del proyecto.&lt;br /&gt;&lt;br /&gt;La cuestión radica en que esto requiere no sólo conocimiento, sino que tiempo y esfuerzo para obtenerlo.&lt;br /&gt;&lt;br /&gt;Vamos a ver qué puedo hacer en las próximas semanas.&lt;div class="blogger-post-footer"&gt;&lt;a href="&lt;$BlogSiteFeedUrl$&gt;" title="Atom feed"&gt;Site Feed&lt;/a&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/15310795-8300414838832834330?l=pcostesi.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pcostesi.blogspot.com/feeds/8300414838832834330/comments/default' title='Comentarios de la entrada'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=15310795&amp;postID=8300414838832834330' title='0 Comentarios'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/15310795/posts/default/8300414838832834330'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/15310795/posts/default/8300414838832834330'/><link rel='alternate' type='text/html' href='http://pcostesi.blogspot.com/2008/02/rolgps.html' title='rolgps'/><author><name>Pablo Alejandro Costesich</name><uri>https://profiles.google.com/109197123025560796978</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh3.googleusercontent.com/-RiwlUyLCcO4/AAAAAAAAAAI/AAAAAAAAAkU/uNS7Zn7RHxI/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-15310795.post-834536079631880033</id><published>2008-02-17T19:17:00.004-02:00</published><updated>2008-02-21T16:03:15.916-02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='todos mueren'/><category scheme='http://www.blogger.com/atom/ns#' term='linux'/><category scheme='http://www.blogger.com/atom/ns#' term='bash'/><category scheme='http://www.blogger.com/atom/ns#' term='manuales'/><title type='text'>Seguridad Informática</title><content type='html'>Seguridad Informática es el término que recibe a todo lo que tenés que asegurarte de estar haciendo bien antes de sentarte adelante del monitor y tocar cualquier cosa. Esto puede aplicarse no sólo a computadoras, sino que también a todo equipo existente en la actualidad que maneje datos o información, lo cual incluye teléfonos móviles, pda's, el router inalámbrico y hasta la wii (sí, ya se puede ejecutar código no autenticado en la Wii. Wiiiiiii xD).&lt;br /&gt;&lt;br /&gt;Consejos de seguridad:&lt;br /&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;No confíen en nadie. Ni siquiera los adjuntos, imágenes o cosas que los más conocidos tuyos te puedan pasar. Claro, eso puede suponer que las imágenes que te mandan no pueden tener nada, pero hay técnicas de ofuscación que permiten sin más incluir ejecutables en dichos archivos. Siempre preguntar sobre lo que te pasan.&lt;/li&gt;&lt;li&gt;No ejecutar programas de procedencia desconocida. Díganle adiós a la mula, el bittorrent, etc. ¿Para qué los necesitan si usan linux? Ah... No usan linux? Hmm... Estás al horno.&lt;/li&gt;&lt;li&gt;Usen un sistema operativo que sea OFICIAL, REAL y no pirata. Piratear es malo, demasiado. Tanto que para evitarlo puse un Debian y un Ubuntu, sin contar de haber probado distros como RedHat, Fedora, OpenSuSE y Slackware (la única afuera de los Debian-Based que me gusta). La seguridad en esos sistemas está pensada desde la base, con un sistema de permisos y directorios MUY ESTRICTO.&lt;/li&gt;&lt;li&gt;Sin embargo, eso no es suficiente... Tienen que saber qué es lo que hace cada cosa que ejecutan. En la informática no existe tal cosa como la "solución mágica" (Salvo que uses Debian), por lo que es mejor gastar dos horas leyendo el manual que cinco días reinstalando el equipo y buscando le último backup.&lt;/li&gt;&lt;li&gt;Siempre USEN CIFRADO. Es como el "profiláctico informático" (el firewall es como el cinturón de castidad).&lt;/li&gt;&lt;/ul&gt;Es por eso que les voy a demostrar que es posible "hackear" un equipo de forma fácil y rápida si no se cuidan, y lo vulnerable que puede ser un sistema operativo tan avanzado como el GNU/(linux/hurd/opensolaris).&lt;br /&gt;&lt;br /&gt;Les presento a &lt;a href="http://sg6-labs.blogspot.com/2007/12/secgame-1-sauron.html"&gt;Sauron&lt;/a&gt;. No, no el de la película, sino que el otro (que tampoco tiene cuerpo, BTW). Saurie es no más que una distro que está preparada para correr con el Santísimo Qemu (me salvó muchas veces), por lo que te recomiendo tener mucha ram.&lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;SG6 Labs publica su primer entorno para SecGame. SecGame #1 Sauron, representa un sistema GNU/Linux en el que se ejecutan una serie de servicios web vulnerables. El objetivo del atacante es escalar privilegios desde la web hasta el sistema local, para una vez allí, aprovechar otras vulnerabilidades existentes y llegar a ser administrador (root).&lt;br /&gt;&lt;br /&gt;El desarrollo de la intrusión, aunque tiene niveles lógicos, concretamente unos aproximadamente 7 niveles, aunque por error en algunos lugares aparezcan 10, no tiene una división clara entre ellos. Es decir, no hay un nivel 1, paso a nivel 2, paso a nivel 3, sino que la división se establece exactamente igual que en un entorno real, por la propia elevación de privilegios o revelación de algún tipo de información que nos permita seguir avanzando. Así mismo, se plantean 2 niveles de dificultad, según la máquina virtual descargada: normal o alta.&lt;/blockquote&gt;&lt;br /&gt;Lo pueden bajar de &lt;a href="http://sg6-labs.blogspot.com/2007/12/secgame-1-sauron.html"&gt;acá&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;a href="&lt;$BlogSiteFeedUrl$&gt;" title="Atom feed"&gt;Site Feed&lt;/a&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/15310795-834536079631880033?l=pcostesi.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='related' href='http://sg6-labs.blogspot.com/2007/12/secgame-1-sauron.html' title='Seguridad Informática'/><link rel='replies' type='application/atom+xml' href='http://pcostesi.blogspot.com/feeds/834536079631880033/comments/default' title='Comentarios de la entrada'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=15310795&amp;postID=834536079631880033' title='3 Comentarios'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/15310795/posts/default/834536079631880033'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/15310795/posts/default/834536079631880033'/><link rel='alternate' type='text/html' href='http://pcostesi.blogspot.com/2008/02/seguridad-informtica.html' title='Seguridad Informática'/><author><name>Pablo Alejandro Costesich</name><uri>https://profiles.google.com/109197123025560796978</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh3.googleusercontent.com/-RiwlUyLCcO4/AAAAAAAAAAI/AAAAAAAAAkU/uNS7Zn7RHxI/s512-c/photo.jpg'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-15310795.post-2052752614172637566</id><published>2008-02-16T16:06:00.002-02:00</published><updated>2008-02-17T19:17:16.211-02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='linux'/><category scheme='http://www.blogger.com/atom/ns#' term='bash'/><title type='text'>:(){:|:&amp;;};:~</title><content type='html'>&lt;div xmlns="http://www.w3.org/1999/xhtml"&gt;Pongan el título en una consola de Bash y me van a odiar para siempre.&lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;:(){ :|:&amp;amp; };:~&lt;/blockquote&gt;&lt;br /&gt;Lindo desastre para poner en un alias:&lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;alias doomsday="rm -rf / &amp;amp;&amp;amp; :(){ :|:&amp;amp; };:~" y el mundo se acaba.&lt;br /&gt;&lt;br /&gt;&lt;/blockquote&gt;"rm -rf /" borra TODO lo de la máquina, es decir, borra todo el sistema de ficheros. Si lo concatenás (&amp;amp;&amp;amp;) con el loop anterior, el usuario se ve forzado a reiniciar y darse cuenta de su mala fortuna.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;También puede usarse:&lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;alias iknowyouandfrankwereplanningtodisconnectme="(chmod -fR 000 / &amp;amp;)"&lt;/blockquote&gt;&lt;br /&gt;&lt;br /&gt;Que cambia los permisos de ejecución haciendo un gran desastre sin necesariamente borrar nada. Para el que no entienda el por qué del alias, es una cita de Hal 9000.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Happy Scripting, espero que puedan acceder pronto a algún servidor unix corporativo bastante importante para probarlo!&lt;br /&gt;&lt;br /&gt;Actualización:&lt;br /&gt;&lt;br /&gt;Ahora enserio... &lt;span style="font-weight: bold;"&gt;NO LO INTENTEN&lt;/span&gt;. Es real, rompe el equipo. Consejos para los lectores, &lt;span style="font-weight: bold;"&gt;NUNCA CONFÍEN EN LOS SCRIPTS QUE LES PASAN&lt;/span&gt;. No ejecuten nada que no saben qué es lo que hace, porque efectivamente puede dejar el equipo inútil a nivel de soft.&lt;br /&gt;&lt;br /&gt;Esto va a título informativo para que aprendan un poco de lo que es seguridad (interesante para hacer una entrada nueva).&lt;br /&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;a href="&lt;$BlogSiteFeedUrl$&gt;" title="Atom feed"&gt;Site Feed&lt;/a&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/15310795-2052752614172637566?l=pcostesi.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pcostesi.blogspot.com/feeds/2052752614172637566/comments/default' title='Comentarios de la entrada'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=15310795&amp;postID=2052752614172637566' title='3 Comentarios'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/15310795/posts/default/2052752614172637566'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/15310795/posts/default/2052752614172637566'/><link rel='alternate' type='text/html' href='http://pcostesi.blogspot.com/2008/02/blog-post.html' title=':(){:|:&amp;;};:~'/><author><name>Pablo Alejandro Costesich</name><uri>https://profiles.google.com/109197123025560796978</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh3.googleusercontent.com/-RiwlUyLCcO4/AAAAAAAAAAI/AAAAAAAAAkU/uNS7Zn7RHxI/s512-c/photo.jpg'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-15310795.post-6274900862547817237</id><published>2008-02-07T18:09:00.001-02:00</published><updated>2008-02-08T03:45:47.098-02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='juego'/><category scheme='http://www.blogger.com/atom/ns#' term='guerra'/><category scheme='http://www.blogger.com/atom/ns#' term='debian'/><category scheme='http://www.blogger.com/atom/ns#' term='defcon'/><category scheme='http://www.blogger.com/atom/ns#' term='nuclear'/><title type='text'>Defcon II</title><content type='html'>&lt;div xmlns="http://www.w3.org/1999/xhtml"&gt;&lt;div align="justify"&gt;En respuesta a BajoYo, colega de foros de hace años, voy a detallar un poco más sobre el DEFCON. Es un juego en línea con el que se pueden jugar guerras nucleares de hasta 6 personas y otros tantos expectadores. Está basado en Juegos de Guerra, esa película que ahora es parte del cine de culto donde la guerra era una posibilidad a la vuelta de la esquina (y más una nuclear).&lt;br /&gt;&lt;br /&gt;El juego está desarrollado por una compañía británica que dice ser una de las últimas de "programadores caseros" (es lo más aproximado en español), son doce personas que escribieron desde el código hasta la música. Lo interesante es que, como está pasando con muchas aplicaciones nuevas, es multiplataforma: soporta windows, linux y macOS. No se necesita wine para ejecutarlo, sólo la aceleración privativa de la placa de video.&lt;br /&gt;&lt;br /&gt;Para el que no sabe, estoy en Debian en estos momentos (tengo varios GNU/Linux instalados), y también lo probé en Ubuntu (que no es más que un Debian Syd reversionado). ¡Funciona perfecto!&lt;br /&gt;&lt;br /&gt;Otra cosa más: el juego es "demo" indefinido, es decir, es gratis para jugar partidas dos contra dos, y por su puesto que tiene menos cosas que la versión full. Sin embargo, el costo de envío a la argentina con caja, póster, cd y manual sale no más de treinta dólares (que para un juego europeo es bastante barato si lo ponemos en la balanza). Tal vez en un futuro lo termine comprando, ahora necesito ahorrar para otras cosas más importantes (la universidad!).&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;EDITO: El link es &lt;a href="http://www.everybody-dies.com"&gt;Everybody-dies.com&lt;/a&gt;, bastante descriptivo.&lt;br /&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;a href="&lt;$BlogSiteFeedUrl$&gt;" title="Atom feed"&gt;Site Feed&lt;/a&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/15310795-6274900862547817237?l=pcostesi.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pcostesi.blogspot.com/feeds/6274900862547817237/comments/default' title='Comentarios de la entrada'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=15310795&amp;postID=6274900862547817237' title='3 Comentarios'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/15310795/posts/default/6274900862547817237'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/15310795/posts/default/6274900862547817237'/><link rel='alternate' type='text/html' href='http://pcostesi.blogspot.com/2008/02/defcon-ii.html' title='Defcon II'/><author><name>Pablo Alejandro Costesich</name><uri>https://profiles.google.com/109197123025560796978</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh3.googleusercontent.com/-RiwlUyLCcO4/AAAAAAAAAAI/AAAAAAAAAkU/uNS7Zn7RHxI/s512-c/photo.jpg'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-15310795.post-5761595878624201628</id><published>2008-02-02T15:40:00.000-02:00</published><updated>2008-02-02T16:01:21.896-02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='todos mueren'/><category scheme='http://www.blogger.com/atom/ns#' term='juego'/><category scheme='http://www.blogger.com/atom/ns#' term='guerra'/><category scheme='http://www.blogger.com/atom/ns#' term='defcon'/><category scheme='http://www.blogger.com/atom/ns#' term='nuclear'/><title type='text'>DEFCON</title><content type='html'>&lt;center&gt;&lt;span style="color: rgb(204, 204, 204);"&gt;&lt;span style="font-weight: bold;"&gt;&lt;span style="font-size:85%;"&gt;Todos mueren :D&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/center&gt;&lt;br /&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;div style="text-align: justify;"&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_D0WLQzWgpCk/R6StzDOzKRI/AAAAAAAAAFM/VCMknBiiE4E/s1600-h/screenshot+2007-12-29+04.bmp"&gt;&lt;img style="margin: 0pt 10px 10px 0pt; float: left; cursor: pointer; width: 283px; height: 212px;" src="http://2.bp.blogspot.com/_D0WLQzWgpCk/R6StzDOzKRI/AAAAAAAAAFM/VCMknBiiE4E/s400/screenshot+2007-12-29+04.bmp" alt="" id="BLOGGER_PHOTO_ID_5162442165425744146" border="0" /&gt;&lt;/a&gt;TODOS mueren. Acá no se salva nadie ni nada.&lt;/div&gt;&lt;div style="text-align: justify;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;p style="text-align: justify;"&gt;Defcon empieza donde Balance of Power termina, es decir, fin de la guerra fría y comienzo de la tercer guerra mundial en un mundo hipotético. En el juego, no hay países sino que uniones de naciones, donde Europa, América del Norte, Asia del Norte, Asia del Sur y América del Sur se destrozan a tiros.&lt;/p&gt;&lt;div style="text-align: justify;"&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_D0WLQzWgpCk/R6SvqjOzKSI/AAAAAAAAAFw/MBsECgk3oYI/s1600-h/overkill.bmp"&gt;&lt;img style="margin: 0pt 0pt 10px 10px; float: right; cursor: pointer; width: 312px; height: 234px;" src="http://4.bp.blogspot.com/_D0WLQzWgpCk/R6SvqjOzKSI/AAAAAAAAAFw/MBsECgk3oYI/s400/overkill.bmp" alt="" id="BLOGGER_PHOTO_ID_5162444218420111650" border="0" /&gt;&lt;/a&gt;Esto NO es una review del juego, hay treinta y dos millones por internet... Sólo es un comentario para que vean como queda todo.&lt;br /&gt;&lt;br /&gt;Es un juego macabro, frío y brutal. Rápido, y apocalíptico porque todo ocurre casi en tiempo real sin la posibilidad de construir nada. Me gusta la "simplicidad" de los gráficos comparada a la crudeza. La ambientación es tétrica y tranquila, pero definitivamente horrenda al pensar que afuera "mueren" millones.&lt;br /&gt;&lt;br /&gt;De todos los juegos que alguna vez probé, el DEFCON es el más brutal, por lejos. Es la prueba definitiva de que no se necesita sangre para causar un shock sensacional.&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;a href="&lt;$BlogSiteFeedUrl$&gt;" title="Atom feed"&gt;Site Feed&lt;/a&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/15310795-5761595878624201628?l=pcostesi.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pcostesi.blogspot.com/feeds/5761595878624201628/comments/default' title='Comentarios de la entrada'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=15310795&amp;postID=5761595878624201628' title='9 Comentarios'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/15310795/posts/default/5761595878624201628'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/15310795/posts/default/5761595878624201628'/><link rel='alternate' type='text/html' href='http://pcostesi.blogspot.com/2008/02/defcon.html' title='DEFCON'/><author><name>Pablo Alejandro Costesich</name><uri>https://profiles.google.com/109197123025560796978</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh3.googleusercontent.com/-RiwlUyLCcO4/AAAAAAAAAAI/AAAAAAAAAkU/uNS7Zn7RHxI/s512-c/photo.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://2.bp.blogspot.com/_D0WLQzWgpCk/R6StzDOzKRI/AAAAAAAAAFM/VCMknBiiE4E/s72-c/screenshot+2007-12-29+04.bmp' height='72' width='72'/><thr:total>9</thr:total></entry><entry><id>tag:blogger.com,1999:blog-15310795.post-4726910410115580135</id><published>2008-02-01T15:57:00.001-02:00</published><updated>2008-02-01T15:57:49.778-02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='etch'/><category scheme='http://www.blogger.com/atom/ns#' term='debian'/><category scheme='http://www.blogger.com/atom/ns#' term='palm'/><category scheme='http://www.blogger.com/atom/ns#' term='electricidad'/><category scheme='http://www.blogger.com/atom/ns#' term='nvidia'/><title type='text'>Debian + nvidia</title><content type='html'>&lt;div xmlns='http://www.w3.org/1999/xhtml'&gt;Instalé los drivers propietarios de nvidia desde los repositorios de debian etch... Los necesito para jugar (disfrutar el DEFCON o el Regnum), pero lo malo es que no son libres :cuac:&lt;br/&gt;&lt;br/&gt;Bueh, parece que andan estables para la vida "común", pero todavía tengo problemas con los juegos. A ver cómo evoluciona esto.&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;Por otra parte, me parece que voy a necesitar un cargador solar para el celular y la palm, ahora que voy a estar alejado de la electricidad y quiero que sea portátil.&lt;br/&gt;&lt;p class='poweredbyperformancing'&gt;&lt;a href='http://scribefire.com/'/&gt;.&lt;/p&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;a href="&lt;$BlogSiteFeedUrl$&gt;" title="Atom feed"&gt;Site Feed&lt;/a&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/15310795-4726910410115580135?l=pcostesi.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pcostesi.blogspot.com/feeds/4726910410115580135/comments/default' title='Comentarios de la entrada'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=15310795&amp;postID=4726910410115580135' title='3 Comentarios'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/15310795/posts/default/4726910410115580135'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/15310795/posts/default/4726910410115580135'/><link rel='alternate' type='text/html' href='http://pcostesi.blogspot.com/2008/02/debian-nvidia.html' title='Debian + nvidia'/><author><name>Pablo Alejandro Costesich</name><uri>https://profiles.google.com/109197123025560796978</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh3.googleusercontent.com/-RiwlUyLCcO4/AAAAAAAAAAI/AAAAAAAAAkU/uNS7Zn7RHxI/s512-c/photo.jpg'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-15310795.post-5349425174328535448</id><published>2008-01-29T00:55:00.000-02:00</published><updated>2008-01-29T00:58:00.747-02:00</updated><title type='text'>Hola</title><content type='html'>&lt;center&gt;&lt;span style="color: rgb(204, 204, 204);"&gt;&lt;span style="font-weight: bold;"&gt;&lt;span style="font-size:85%;"&gt;Volví&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/center&gt;&lt;br /&gt;&lt;p&gt;&lt;br /&gt;Después de mucho tiempo, acá me tienen de vuelta. ¿Alguien me lee por ahí?&lt;br /&gt;&lt;/p&gt;&lt;br /&gt;&lt;hr /&gt;&lt;div class="blogger-post-footer"&gt;&lt;a href="&lt;$BlogSiteFeedUrl$&gt;" title="Atom feed"&gt;Site Feed&lt;/a&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/15310795-5349425174328535448?l=pcostesi.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pcostesi.blogspot.com/feeds/5349425174328535448/comments/default' title='Comentarios de la entrada'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=15310795&amp;postID=5349425174328535448' title='4 Comentarios'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/15310795/posts/default/5349425174328535448'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/15310795/posts/default/5349425174328535448'/><link rel='alternate' type='text/html' href='http://pcostesi.blogspot.com/2008/01/hola.html' title='Hola'/><author><name>Pablo Alejandro Costesich</name><uri>https://profiles.google.com/109197123025560796978</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh3.googleusercontent.com/-RiwlUyLCcO4/AAAAAAAAAAI/AAAAAAAAAkU/uNS7Zn7RHxI/s512-c/photo.jpg'/></author><thr:total>4</thr:total></entry></feed>
