viernes, octubre 30, 2009

deseo

* mira las velas*
* mira hacia arriba *
* desea ver mas preguntas de python en la lista de python *
* sopla *

estas cosas funcionan?

sino me compre una torta y unas velas en vano.

al menos tengo torta... *

* not

sábado, octubre 17, 2009

Mapeo de busquedas de couchdb-lucene en couchapp

hasta ahora las vistas de couchdb-lucene las metía en couchdb a mano usando la interfaz de administración, pero me molestaba tener un txt por ahí con la vista y no tenerlo en el svn (menos me gustaba que couchapp no lo manejara).

después de preguntar en el irc de couchapp me dijeron que couchapp mapea directorios y archivos a estructuras json, así que lo que antes era


{
"_id": "_design/search-docs",
"fulltext": {
"documents": {
"index": "function(doc) { /* algo */ }"
},
"repos": {
"index": "function(doc) { /* algo */ }"
}
"authors": {
"index": "function(doc) { /* algo */ }"
}
}
}


se convierte en una jerarquía de directorios en la raíz de mi couchapp como la siguiente


fulltext/
|-- authors
| `-- index.js
|-- documents
| `-- index.js
`-- repos
`-- index.js



_design/search-docs pasa a ser _design/fresita (porque ese es el nombre de mi aplicación). Podemos encontrar las búsquedas en el path

_fti/$app-name/$search

por ejemplo:

_fti/fresita/authors?q=Mariano

todo mas prolijo y manejable..

martes, octubre 13, 2009

calculadora en erlang parte (multiplicación división y modulo)

como la otra vez voy a marcar las diferencias

calc_lexer.xrl:


Definitions.

D = [0-9]
AOP = (\+|-)
MOP = (\*|/|%)
WS = ([\000-\s]|#.*)

Rules.

{AOP} : {token,{add_operator,TokenLine,list_to_atom(TokenChars)}}.
{MOP} : {token,{mul_operator,TokenLine,list_to_atom(TokenChars)}}.
{D}+ : {token,{integer,TokenLine,list_to_integer(TokenChars)}}.
{D}+\.{D}+ : {token,{float,TokenLine,list_to_float(TokenChars)}}.
{WS}+ : skip_token.

Erlang code.

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

nada del otro mundo, hacemos lo mismo que con los signos de adicion pero cambiando la expresion regular y el identificador.

ahora el parser:

calc_parser.yrl

Nonterminals
predicate.

Terminals
add_operator mul_operator integer float.

Rootsymbol predicate.

Left 300 add_operator.
Left 400 mul_operator.

predicate -> predicate add_operator predicate : {unwrap('$2'), '$1', '$3'}.
predicate -> predicate mul_operator predicate : {unwrap('$2'), '$1', '$3'}.

predicate -> integer : unwrap('$1').
predicate -> float : unwrap('$1').

Erlang code.

unwrap({_,_,V}) -> V.

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

de nuevo, agregamos el identificador del nuevo simbolo no terminal, especificamos la asociatividad de la nueva operacion (tiene precedencia sobre la adicion). Y agregamos la regla para que lo transforme en una estructura de datos.

por ultimo la calculadora en si

calc.erl

-module(calc).
-export([solve/1]).

solve(String) ->
{ok, Tokens, _Endline} = calc_lexer:string(String),
{ok, Tree} = calc_parser:parse(Tokens),
matches(Tree).

matches(A) when is_number(A) -> A;
matches({'+', A, B}) -> matches(A) + matches(B);
matches({'-', A, B}) -> matches(A) - matches(B);
matches({'*', A, B}) -> matches(A) * matches(B);
matches({'/', A, B}) -> matches(A) / matches(B);
matches({'%', A, B}) -> matches(A) rem matches(B);
matches(_) -> error.

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

agregamos el matching de las nuevas operaciones.

para probar un poco el codigo de build_calc:test()


test() ->
0 = calc:solve("1 + 2 - 3"),
6 = calc:solve("1 + 2 + 3"),
-4 = calc:solve("1 - 2 - 3"),
0.0 = calc:solve("1.0 + 2 - 3"),
6.0 = calc:solve("1 + 2.0 + 3"),
-4.0 = calc:solve("1 - 2.0 - 3"),
3.1 = calc:solve("1.0 + 2.1"),
7 = calc:solve("1 + 2 * 3"),
10 = calc:solve("2 * 3 + 4"),
7.1 = calc:solve("1.1 + 2 * 3"),
10.2 = calc:solve("2 * 3.1 + 4"),
1 = calc:solve("11 % 2"),
2 = calc:solve("1 + 11 % 2"),
11 = calc:solve("11 % 2 + 10"),
ok.


y por ultimo lo probamos



$ erl
Erlang (BEAM) emulator version 5.6.5 [source] [async-threads:0] [kernel-poll:false]

Eshell V5.6.5 (abort with ^G)
1> c(build_calc).
{ok,build_calc}
2> build_calc:build().
Old inliner: threshold=0 functions=[{yeccpars2_7_,1},
{yeccpars2_6_,1},
{yeccpars2_3_,1},
{yeccpars2_2_,1},
{yeccpars2_7_,1},
{yeccpars2_6_,1},
{yeccpars2_3_,1},
{yeccpars2_2_,1}]
ok
3> rl(calc).
ok
4> rl(calc_parser).
ok
5> rl(calc_lexer).
ok
6> build_calc:test().
ok


en la proxima agregamos expresiones anidadas (parentesis)

haciendo andar couchdb-lucene 0.4 en couchdb 0.10.0

como la mayoría de los posts son recordatorios para cuando lo tenga que hacer de nuevo y me haya olvidado y por si a alguien le sirve :D

principalmente seguí estas dos guías

http://github.com/rnewson/couchdb-lucene
http://wiki.fluidproject.org/pages/viewpage.action?pageId=6823331

pero siempre la generalidad termina dejando algunas cosas flotando que hacen que uno pelee (hasta que mira "watch tail /usr/local/var/log/couchdb/couch.log")

supongo que tienen instalado y andando couchdb 0.10.0 (va a ser el caso si usan ubuntu karmic :)

lo primero que hacemos es bajar la versión estable de couchdb-lucene (actualmente la 0.4)


wget http://cloud.github.com/downloads/rnewson/couchdb-lucene/couchdb-lucene-0.4-jar-with-dependencies.jar.gz


luego (como dice el README que uno no lee) desempacamos el jar.gz con unpack200, si lo desempacamos con file-roller java se queja de que es un jar corrupto.


unpack200 couchdb-lucene-0.4-jar-with-dependencies.jar.gz couchdb-lucene-0.4-jar-with-dependencies.jar


creamos un directorio donde lucene pueda trabajar tranquilo


sudo mkdir /usr/local/var/lib/lucene


copiamos el jar


sudo cp couchdb-lucene-0.4-jar-with-dependencies.jar /usr/local/var/lib/lucene


cambiamos el dueño y los permisos al directorio


chown -R couchdb:couchdb /usr/local/var/lib/lucene
chmod -R 0770 /usr/local/var/lib/lucene


configuramos couchdb para que use lucene como indexador de full text search


sudo vim /usr/local/etc/couchdb/local.ini


(mi archivo local.ini tiene lo siguiente)


; CouchDB Configuration Settings

; Custom settings should be made in this file. They will override settings
; in default.ini, but unlike changes made to default.ini, this file won't be
; overwritten on server upgrade.

[couchdb]
;max_document_size = 4294967296 ; bytes

[httpd]
;port = 5984
;bind_address = 127.0.0.1

[log]
;level = debug

[couch_httpd_auth]
;secret = replace this with a real secret

[external]
fti=/usr/bin/java -Dcouchdb.lucene.dir=/usr/local/var/lib/lucene -jar /usr/local/var/lib/lucene/couchdb-lucene-0.4-jar-with-dependencies.jar -search

[update_notification]
indexer=/usr/bin/java -Dcouchdb.lucene.dir=/usr/local/var/lib/lucene -jar /usr/local/var/lib/lucene/couchdb-lucene-0.4-jar-with-dependencies.jar -index

[couchdb]
os_process_timeout=60000 ; increase the timeout to 60 seconds.

[httpd_db_handlers]
_fti = {couch_httpd_external, handle_external_req, <<"fti">>}

; To create an admin account uncomment the '[admins]' section below and add a
; line in the format 'username = password'. When you next start CouchDB, it
; will change the password to a hash (so that your passwords don't linger
; around in plain-text files). You can add more admin accounts with more
; 'username = password' lines. Don't forget to restart CouchDB after
; changing this.
[admins]
marianoguerra = mysecretpassword
;admin = mysecretpassword


(no, ese no es mi password :)

reiniciamos couchdb (lo matamos y lo levantamos de nuevo, esto depende de como lo levantaste, si no sabes, hace "ps aux | grep couchdb" y mata todo lo que parezca sospechoso :D)

lo levantamos de nuevo (yo lo levanto así)


sudo -i -u couchdb couchdb -b


vamos a la interfaz de administracion:


firefox http://localhost:5984/_utils/


elegimos nuestra base de datos, elegimos crear nuevo documento, clickeamos la pestaña source arriba a la derecha y pegamos algo parecido a lo siguiente:


{
"_id": "_design/search-docs",
"fulltext": {
"by_title": {
"index": "function(doc) { var ret=new Document(); ret.add(doc.title); return ret }"
},
"by_content": {
"index": "function(doc) { var ret=new Document(); ret.add(doc.body); return ret }"
}
}
}


el _id cambia por el nombre que mas te guste, las vistas que están adentro de fulltext también cambian según por que quieras indexar, yo estoy indexando por titulo y contenido de unos documentos.

ahora agregamos algunos documentos que puedan ser indexados, para sacarnos la duda de que lucene esta andando mirar la salida de /usr/local/var/log/couchdb/couch.log no debería haber mensajes de error y "ls /usr/local/var/lib/lucene" nos tendia que mostrar varios archivos de nombres extravagantes.

para probar nuestra búsqueda luego de agregar algunos documentos hacemos una query (yo agregue un documento que decía ficticio por todos lados :D)


curl http://localhost:5984/fresita/_fti/search-docs/by_title?q=ficticio


lo cual me devolvió


{"q":"default:ficticio","etag":"1244bf34b28","view_sig":"7368cf7d6f68ec4c60f40c52303de534","skip":0,"limit":25,"total_rows":1,"search_duration":14,"fetch_duration":0,"rows":[{"id":"9ed8514725e1dca56c57471286f5f389","score":2.556901454925537}]}

sábado, octubre 10, 2009

calculadora en erlang parte 2 (agregando floats)

este cambio es muy simple asi que va a ser un post muy corto, voy a resaltar los cambios

en calc_lexer.xrl

Definitions.

D = [0-9]
AOP = (\+|-)
WS = ([\000-\s]|#.*)

Rules.

{AOP} : {token,{add_operator,TokenLine,list_to_atom(TokenChars)}}.
{D}+ : {token,{integer,TokenLine,list_to_integer(TokenChars)}}.
{D}+\.{D}+ : {token,{float,TokenLine,list_to_float(TokenChars)}}.
{WS}+ : skip_token.

Erlang code.

en calc_parser.yrl

Nonterminals
predicate.

Terminals
add_operator integer float.

Rootsymbol predicate.

Left 300 add_operator.

predicate -> predicate add_operator predicate : {unwrap('$2'), '$1', '$3'}.

predicate -> integer : unwrap('$1').
predicate -> float : unwrap('$1').

Erlang code.

unwrap({_,_,V}) -> V.

en calc.erl

-module(calc).
-export([solve/1]).

solve(String) ->
{ok, Tokens, _Endline} = calc_lexer:string(String),
{ok, Tree} = calc_parser:parse(Tokens),
matches(Tree).

matches(A) when is_number(A) -> A;
matches({'+', A, B}) -> matches(A) + matches(B);
matches({'-', A, B}) -> matches(A) - matches(B);
matches(_) -> error.

para facilitar la construccion, les dejo el codigo que tengo en build_calc.erl



-module(build_calc).
-export([build/0, test/0]).

build() ->
leex:file(calc_lexer),
yecc:file(calc_parser),
compile:file(calc_lexer),
compile:file(calc_parser),
compile:file(calc),
ok.

viernes, octubre 09, 2009

escribiendo una calculadora en erlang parte uno

buscando una excusa para seguir aprendiendo erlang encontré una interesante, escribir algo así como un lenguaje de juguete paso a paso y ver para donde dispara (o donde lo abandono)

en una serie de posts (espero seguir escribiéndolos) voy a ir desde cero hasta un interprete interactivo que permita hacer cosas como estas (y mas)

< a = 10 + (2 * 3) # calculo totalmente innecesario
> 16
< b = a * 2
> 32
< a
> 16
< b
> 32
< a == b or b > a
> true


este post es la patada de inicio asi que vamos a ver la herramientas.

Como lenguaje de programación obviamente vamos a usar erlang (que es lo que quiero aprender :D).

para el análisis léxico vamos a usar leex y para el análisis sintáctico y generación del árbol vamos a usar yecc.

para tener erlang hagan algo similar a:

sudo aptitude install erlang

(adaptenlo a su distro)

yecc viene incluido en erlang así que andamos con suerte, a donde no andamos con suerte es con leex, que lo tendremos que bajar de aquí:

http://forum.trapexit.org/download.php?id=184&sid=9eba63c4011e517629e8d5a73e4e2ff5

pueden buscar si hay una versión mas nueva acá:

http://forum.trapexit.org/viewtopic.php?p=43924#43924

lo bajan, descomprimen y ejecutan make en el directorio, luego para poder usarlo en erlang lo que hice (quizás no muy bueno pero anda :P) es crear un directorio llamado /usr/lib/erlang/lib/leex-0.3 y poner adentro leex.beam y el directorio include.

ahora con todo instalado vamos a largarnos con nuestro primer intento (remito a la documentación de cada uno para darse una mejor idea de que hacen y como se usan).

vamos a definir un lenguaje que nos permita sumar y restar números enteros

creemos un directorio llamado match-1-add-sub-ints

y adentro creamos un archivo llamado calc_lexer.xrl con el siguiente contenido


Definitions.

D = [0-9]
AOP = (\+|-)
WS = ([\000-\s]|#.*)

Rules.

{AOP} : {token,{add_operator,TokenLine,list_to_atom(TokenChars)}}.
{D}+ : {token,{integer,TokenLine,list_to_integer(TokenChars)}}.
{WS}+ : skip_token.

Erlang code.



este archivo lo que hace es separar un string en partes (o tokens) que identifican distintos componentes de nuestro lenguaje. En el caso de un lenguaje para sumar números enteros lo que queremos dividir son los números, los signos + y - y descartar los espacios (y también todo lo que venga después de # ya que son comentarios)

en la sección de definiciones definimos las distintas expresiones regulares.

definimos una expresión regular para números enteros

D = [0-9]

una para los signos + y -

AOP = (\+|-)

y una para los blancos y los comentarios

WS = ([\000-\s]|#.*)

luego en la sección Rules. definimos que debe generar (la parte de la derecha) cuando se encuentre con las expresiones de la izquierda.

si se encuentra con un signo + o - que genere una tupla que dice que es un operador de suma, que ponga la posición en donde encontró el token y que convierta lo que matchea la expresión a un atomo.

{AOP} : {token,{add_operator,TokenLine,list_to_atom(TokenChars)}}.

si se encuentra con uno o mas dígitos que genere una tupla que dice que es un entero, la posición del token y el numero entero que encontró.

{D}+ : {token,{integer,TokenLine,list_to_integer(TokenChars)}}.

si encuentra blancos que los salte.

{WS}+ : skip_token.

ahora ya podemos dividir el texto de entrada en pedazos mas manejables gracias a leex, ahora vamos a ver como salio esto. Iniciamos un interprete de erlang y le decimos a leex que genere un modulo de erlang para manejar los tokens que definimos, luego compilamos el modulo generado y lo probamos con un string valido


$ erl
Erlang (BEAM) emulator version 5.6.5 [source] [async-threads:0] [kernel-poll:false]

Eshell V5.6.5 (abort with ^G)
1> leex:file(calc_lexer).
{ok,"calc_lexer.erl"}
2> c(calc_lexer).
{ok,calc_lexer}
3> {ok, Tokens, _Endline} = calc_lexer:string("1 + 2 - 3 # esto es un comentario").
{ok,[{integer,1,1},
{add_operator,1,'+'},
{integer,1,2},
{add_operator,1,'-'},
{integer,1,3}],
1}


como vemos nos devolvió una estructura de datos con las partes que definimos.

ahora vamos a definir un modulo para poder hacer algo con ese árbol que definimos.

creamos un archivo que se llame calc_parser.yrl con el siguiente contenido:


Nonterminals
predicate.

Terminals
add_operator integer.

Rootsymbol predicate.

Left 300 add_operator.

predicate -> predicate add_operator predicate : {unwrap('$2'), '$1', '$3'}.

predicate -> integer : unwrap('$1').

Erlang code.

unwrap({_,_,V}) -> V.



este documento define los nodos no terminales, los nodos terminales el símbolo raíz, la asociatividad de los operandos y luego define la sintaxis (para mas datos sobre las primeras secciones leer la documentación en linea de yecc o algun libro sobre lenguajes, otra opción es ponerse a romper esto y aprender :D).

la parte mas interesante es la que define que un predicado puede estar compuesto por la suma de dos predicados:

predicate -> predicate add_operator predicate : {unwrap('$2'), '$1', '$3'}.

o puede ser solo un entero

predicate -> integer : unwrap('$1').

lo que esta después de los dos puntos es el código erlang ejecutado cuando esa expresión es encontrada. Lo que hacemos es generar otra estructura de datos que vamos a manipular en erlang.

luego tenemos una función de utilidad en erlang para sacar un valor de interés de una tupla.

ahora generamos el modulo erlang que va a parsear la salida de leex y convertirla en otra estructura:


$ erl
Erlang (BEAM) emulator version 5.6.5 [source] [async-threads:0] [kernel-poll:false]

Eshell V5.6.5 (abort with ^G)
1> yecc:file(calc_parser).
{ok,"calc_parser.erl"}
2> c(calc_parser).
{ok,calc_parser}
3> {ok, Tokens, _Endline} = calc_lexer:string("1 + 2 - 3").
{ok,[{integer,1,1},
{add_operator,1,'+'},
{integer,1,2},
{add_operator,1,'-'},
{integer,1,3}],
1}
4> {ok, Tree} = calc_parser:parse(Tokens)
4> .
{ok,{'-',{'+',1,2},3}}


para los curiosos, si se fijan el resultado que nos dio es muy parecido a una expresión de lisp :)

{'-',{'+',1,2},3}


[1]> (- (+ 1 2) 3)
0


bueno, ahora tenemos algo que se parece mucho a lisp, podríamos escribir lisp! pero no :D vamos a interpretarlo con un programa de erlang.

creen un archivo calc.erl que contenga lo siguiente:


-module(calc).
-export([solve/1]).

solve(String) ->
{ok, Tokens, _Endline} = calc_lexer:string(String),
{ok, Tree} = calc_parser:parse(Tokens),
matches(Tree).

matches(A) when is_integer(A) -> A;
matches({'+', A, B}) -> matches(A) + matches(B);
matches({'-', A, B}) -> matches(A) - matches(B);
matches(_) -> error.


lo que hace este modulo es definir una función que recibe un string por parámetro con el calculo que queremos realizar, lo pasar por el analizador lexicografico, le da la salida de este como entrada al parser y con el árbol sintáctico resultante llama a la función matches que gracias al pattern matching se encarga de ejecutar recursivamente la instrucción correcta y devolver el resultado final (o un error en caso de que algo no matchee).

ahora vamos a compilar esto y probarlo un poco:


$ erl
Erlang (BEAM) emulator version 5.6.5 [source] [async-threads:0] [kernel-poll:false]

Eshell V5.6.5 (abort with ^G)
1> c(calc).
{ok,calc}
2> calc:solve("1 + 2 - 3 # deberia dar 0").
0
3> calc:solve("1 + 2 - 3 - 4").
-4


bueno, ya tenemos la primera versión de nuestra calculadora, en la próxima vamos a agregar soporte para números de punto flotante con algunos leves cambios.

para los fiacosos como yo, el codigo de este ejemplo (y de los subsiguientes) esta en github aca:

http://github.com/marianoguerra/match

sábado, septiembre 26, 2009

mi nuevo proyecto

...porque llega el momento en la vida de toda persona en la que programa su propio "framework" web en python...

el proyecto se llama tubes y esta hosteado (usando tubes :P) acá:

http://a-series-of-tubes.appspot.com/


los invito a probarlo, divertirse un rato, hacer preguntas del tipo "porque otro framework web en python???" etc etc.

viernes, septiembre 11, 2009

Pubsubhubbub developer's guide

naming conventions

create directory to hold all the content of this tutorial

$ mkdir pshb
$ cd pshb

download Google App Engine SDK

! pshb trunk needs the latest version of GAE (1.2.4 as of today) to work, but ! the download page display an older version

$ wget http://googleappengine.googlecode.com/files/google_appengine_1.2.4.zip
$ unzip google_appengine_1
.2.4.zip

! GAE needs python 2.5 in order to work, if you have an older or newer version of python install python 2.5 (OS and distro dependent, wont be covered here)

download the latest version of pshb

$ svn checkout http://pubsubhubbub.googlecode.com/svn/trunk/ pubsubhubbub

start pshb

$ python2.5 google_appengine/dev_appserver.py pubsubhubbub/hub/

! note the python2.5 command

check that the hub started

type http://localhost:8080 on your browser, you should see something like:

Welcome to the demo PubSubHubbub reference Hub server!




download the example

# we will need to fetch the tubes library and the example that we will be using.
$ git clone git
://github.com/marianoguerra/tubes.git

# now we will start the example

$ cd tubes
/ihasfriendz/
$ python main
.py

we should see something like:

 * Running on http://0.0.0.0:8081/
* Restarting with reloader...

components used by the example

publish the feed to the hub

go to the following URL on your browser: http://localhost:8080/publish

on the Topic field enter: http://localhost:8081/atom/stream/_MYUSER_ and click publish

! if everything goes OK, then you wont notice anything on the page, that's ok, browsers act that way to 204 responses

! MYUSER is a placeholder for the user you will use to post notices on the test app later (for example http://localhost:8081/atom/stream/marianoguerra)




subscribing to the hub

go to the following URL on your browser: http://localhost:8080/subscribe

on the Callback field enter: http://localhost:8081/callback on the Topic field enter: http://localhost:8081/atom/stream/_MYUSER_ on the Verify token field enter something random like: iwantmahcookie

! MYUSER is a placeholder for the user you will use to post notices on the test app later (for example http://localhost:8081/atom/stream/marianoguerra)

click Do it

! if everything goes OK, then you wont notice anything on the page, that's OK, browsers act that way to 204 responses


create some content

go to the following URL on your browser: http://localhost:8081/files/index.html

post some content on the form, use the user you used as MYUSER


you can check that the item was posted going manually to http://localhost:8081/atom/stream/_MYUSER_, you should see an atom feed there

manually processing the tasks

when running the hub on the dev server we have to run the task queues by hand, to do that go to http://localhost:8080/_ah/admin/queues



on the Tasks in Queue column of the feed-pulls you should see a number different than 0 (that is the number of messages you created since the last execution of that task).

click on the feed-pulls link, there click on the run button.

when we run the feed pulls task, we tell pshb to fetch the feeds that have new content (the ones that did a post to the hub to inform that there is new content)

! on production hubs this tasks are done automatically

now we go again to the Task Queues page, there the event-delivery queue should have a number different than 0 (the number of messages that are pending to be sent to the subscribers), we click on the event-delivery and then we click on the run button.

when we run the event delivery task, we tell pshb to do a POST on every callback url registered for the feeds that were fetched on the feed-pulls task.


seeing it work

now that we created a feed, informed the hub that we had new content, the hub fetched the content and sent it to the callback, we want to see this content, for this go with your browser to http://localhost:8081/new-notices/, you will see the notices that pshb posted back to you the last time.



! if you refresh the page you will notice that the messages aren't there anymore, that's because the example stores the new messages in a Queue that is flushed when the request for new notices is made, in this way you can see only the new messages.

! the example stores all the information on global variables on main.py (this is to make the example simpler), so every time you change something on main.py and save the server will reload the changes and all the data will disappear.

for lazy people

#!/usr/bin/env sh

# create the example directory
mkdir pshb
cd pshb

CWD
=$(pwd)
EXAMPLE
=$CWD/tubes/ihasfriendz

wget http
://googleappengine.googlecode.com/files/google_appengine_1.2.4.zip
unzip google_appengine_1
.2.4.zip

svn checkout http
://pubsubhubbub.googlecode.com/svn/trunk/ pubsubhubbub


# we will need to fetch the tubes library and the example that we will be using.
git clone git
://github.com/marianoguerra/tubes.git

# now we will start the example

echo
"run \"cd $EXAMPLE; python main.py\" on a shell to run the example"
echo
"run \"cd $PWD; python2.5 google_appengine/dev_appserver.py pubsubhubbub/hub/\" on a shell to run the hub"

jueves, septiembre 10, 2009

playing with mercurial API

to make this code be useful a script like this should be run to test

[ -d fresita ] && rm -rf fresita
mkdir fresita
cd fresita
echo "test" > README.txt


my first test script:

import os

from mercurial import ui
from mercurial import commands
from mercurial import localrepo

ui_ = ui.ui()
create = 1
path = os.path.abspath('./fresita')

def repo_initialized(path):
'''return True if the repo is already initialized'''
return os.path.isdir(os.path.join(path, '.hg'))

if repo_initialized(path):
create = 0

repo = localrepo.localrepository(ui_, path, create)
repo.add(['README.txt'])
modified, added, removed, deleted, unknown, ignored, clean = repo.status()

print 'modified', modified
print 'added', added
print 'removed', removed
print 'deleted', deleted
print 'unknown', unknown
print 'ignored', ignored
print 'clean', clean

repo.commit(text="my first commit")


pretty easy :)

domingo, agosto 30, 2009

Portishead - Magic Doors

por desgracia no me lo dejan embeber, pero los invito a visitar la pagina y deleitarse con un buen tema

http://www.youtube.com/watch?v=yRZlBNaFYqk

La banda fue formada en 1991 por el teclista Geoff Barrow y la cantante Beth Gibbons que tomaron el nombre de la localidad natal de Barrow, situada a 15 km al oeste de Bristol. Barrow había trabajado anteriormente con otras dos bandas de trip hop de Bristol, Massive Attack y Tricky.

Después de estrenar el cortometraje To Kill a Dead Man, Portishead firmó un contrato con la discográfica Go! Beat que editó su primer álbum Dummy en 1994, con la colaboración del guitarrista Adrian Utley. A pesar de la nula campaña de publicidad de la banda, el álbum fue un éxito en Estados Unidos y Europa, los sencillos "Glory Box" y "Sour Times" alcanzaron la cabeza de las listas de éxitos.

Su segundo álbum Portishead, salió a la venta en 1997 e incluyó el sencillo "All Mine". Además, grabaron un concierto con acompañamiento de orquesta en Roseland, Nueva York, donde fueron interpretadas sus canciones más famosas. Éste, salió a la venta en 1998 con un DVD del concierto. En 1999 colaboraron con Tom Jones en una canción de su álbum Reload.

Portishead y otros grupos de Trip hop no están conformes con este término, argumentando que es sólo una invención mediática para catalogar su música inclasificable.

El 24 de enero de 2008 Portishead anunció la salida de un nuevo disco, titulado Third, programado para el 14 de abril del mismo año.

http://en.wikipedia.org/wiki/Third_(Portishead_album)

viernes, agosto 21, 2009

politica de cacheo de requests GET de IE

Si alguna vez estas sirviendo algún contenido dinámico (o requests AJAX) y te das cuenta que IE esta devolviendo siempre un valor viejo y no podes cambiarlo a POST para evitar el cacheo entonces la opción que encontré es setear en el header del response los siguientes valores.

now = time.time()
handler.set_header("Expires", now)
handler.set_header("Last-Modified", now)
handler.set_header("Date", now)

(tradúzcanlo al framework/librería que usen)

PD: hasta ahora parece andar pero le dejo la puerta entreabierta a IE para que me sorprenda...

miércoles, agosto 05, 2009

Gente grosa

no, no tiene nada que ver con computadoras, pero cuando veo a estas personas me dejan de cara

travis pastrana, no puede tener tantos huevos





si hablamos de tecnica pura: rodney mullen



este truco es epico:



el primer best trick que me dejo de cara, el famoso 900 de tony hawk:



matt hoffmann no se queda atras y lo hace no handed en bicicleta



y por ultimo un poco de mi deporte preferido (que hace mucho que no practico :S)

jueves, julio 30, 2009

analizando interfaces de usuario inconscientemente

hoy fui a sacar plata a un cajero link (yo tengo banelco).

Es la primera vez que saco plata en esos cajeros así que no se si todos son iguales pero automáticamente me puse a analizar la interfaz de usuario, me llamo la atención de que si pones 4 números y están bien pasa derecho a la próxima pantalla, no se que hará si los números están mal, habría que probar si poniendo números mal en 5 segundos no hace nada podemos probar muchas combinaciones hasta que pase (no probado).

Pero eso es un detalle nomas, la pantalla de inicio me dio aproximadamente 8 opciones con variaciones mínimas que podrían haber sido movidas a otro submenu, la opción de extracción te pregunta de donde lo queres sacar y cuenta corriente en pesos no esta en ningún extremo si no en el centro a la derecha, los otros diálogos me parecieron también muy llenos de opciones y las opciones que generalmente son las por defecto no están en un lugar constante y visible (por ejemplo siempre arriba a la izquierda o similar).

Por ultimo el cajero te da la plata y el ticket y después te pregunta si queres hacer otra operación o finalizar. Razón por la cual casi me olvido la tarjeta ya que saque la plata, el ticket y como en los otros cajeros la tarjeta te la da antes con una chicharra y una luz es menos factible que te la olvides.

Si soy un colgado olvidador de cosas y pienso giladas pero me di cuenta como el cajero al que voy me simplifica la vida sin que me de cuenta :D.

miércoles, julio 29, 2009

correcciones de problemas de MPI

alguien me paso el tip de que el broadcast de MPI es una operacion colectiva y que no tiene que ir en el rank 0 si no que todos lo tienen que ejecutar ya que tiene el receive implicito, asi que aca los 2 programas corregidos (los que no imprimian los valores de los procesos que no fueran el master).

plus un arreglo en la declaracion de la estructura en el segundo programa.

#include <stdio.h>
#include <mpi.h>

int main (int argc, char** argv) {
int rank, size, value, i;
MPI_Status status;

MPI_Init (&argc, &argv); /* starts MPI */
MPI_Comm_rank (MPI_COMM_WORLD, &rank); /* get current process id */
MPI_Comm_size (MPI_COMM_WORLD, &size); /* get number of processes */

do {
if(rank == 0) {
printf("give me a value: ");
scanf("%d", &value);
}

MPI_Bcast(&value, 1, MPI_INT, 0, MPI_COMM_WORLD);
printf("process %d received %d\n", rank, value);
fflush(stdout);
} while(value >= 0);
printf("negative value, closing\n");

MPI_Finalize();
return 0;
}

#include <stdio.h>
#include <mpi.h>

MPI_Datatype IntDoubleType = 0;

typedef struct {
int value_int;
double value_double;
}IntDouble;

MPI_Datatype create_int_double_type() {
if(IntDoubleType == 0) {
MPI_Aint array_of_displacements[2];
MPI_Datatype array_of_types[2];
int array_of_blocklengths[2];

array_of_displacements[0] = 0;
array_of_types[0] = MPI_INT;
array_of_blocklengths[0] = sizeof(int);

array_of_displacements[1] = sizeof(int);
array_of_types[1] = MPI_DOUBLE;
array_of_blocklengths[1] = sizeof(double);

MPI_Type_struct(1, array_of_blocklengths, array_of_displacements,
array_of_types, &IntDoubleType);
MPI_Type_commit(&IntDoubleType);

}

return IntDoubleType;
}

int main (int argc, char** argv) {
int rank, size, value_int, i;
double value_double;
IntDouble value;
MPI_Status status;

MPI_Init (&argc, &argv); /* starts MPI */
MPI_Comm_rank (MPI_COMM_WORLD, &rank); /* get current process id */
MPI_Comm_size (MPI_COMM_WORLD, &size); /* get number of processes */
create_int_double_type();

do {
if(rank == 0) {
printf("give me an int value: ");
scanf("%d", &value_int);
printf("give me a double value: ");
scanf("%lf", &value_double);
value.value_int = value_int;
value.value_double = value_double;
}

MPI_Bcast(&value, 1, IntDoubleType, 0, MPI_COMM_WORLD);
printf("process %d received %d %lf\n", rank, value.value_int,
value.value_double);
fflush(stdout);
} while(value.value_int >= 0);
printf("negative value, closing\n");

MPI_Finalize();
return 0;
}

sábado, julio 25, 2009

generando funciones dinamicamente (a.k.a generando bytecode a mano)

Este desafio estuvo bueno, alguien en la lista de emesene dijo que para hacer la API de dbus exponiendo los eventos del protocolo (que tienen numero de argumentos variables) necesitaba generar funciones con cantidad de argumentos variables pero que la funcion tenga ese numero fijo, esto significa que si tengo que generar una funcion que recibe 4 argumentos tiene que ser:

def fun(arg0, arg1, arg2, arg3): pass

y no

def fun(*args): pass

ya que la API de dbus usa inspect para determinar el numero de argumentos posicionales

me tomo varias horas sacarlo, buscaba y buscaba y cada vez iba mas a bajo nivel. Hasta que llegue al mas bajo nivel que se puede llegar... generar bytecodes :)

y bue, pelee con eso un buen rato ya que si bien alguna vez jugue haciendo maquinas virtuales de juguete esta era la primera vez contra una en serio.

el resultado es el siguiente:


import dis
import types

def gen(num):
op = dis.opmap.__getitem__
ops = [op('LOAD_GLOBAL'), 0, 0]

for argnum in range(num):
ops.append(op('LOAD_FAST'))
ops.append(argnum)
ops.append(0)

ops.append(op('CALL_FUNCTION'))
ops.append(num)
ops.append(0)
ops.append(op('RETURN_VALUE'))

code_str = ''.join(chr(x) for x in ops)
return code_str

def gen_code(num, global_vars, name='func', filename='magic.py'):
code = gen(num)
varnames = ['arg' + str(x) for x in range(num)]
names = global_vars + varnames

names = tuple(names)
varnames = tuple(varnames)

return types.CodeType(num, num, num, 0, code, (), names, varnames, filename, name, 1, '')

def gen_fun(num, name, func):
code = gen_code(num, [func.__name__], name)
return types.FunctionType(code,{func.__name__: func})

if __name__ == '__main__':
def printer(*args):
print args

f4 = gen_fun(4, 'f4', printer)
dis.dis(f4.func_code)
f4(1,2,3,4)

f1 = gen_fun(1, 'f1', printer)
dis.dis(f1.func_code)
f1('only one arg')

try:
f1(1, 2)
except TypeError:
print 'ok, ok, only one argument'


lo que hace es generar una funcion que recibe N parametros y que lo unico que hace es llamar a otra funcion pasandole esos parametros, lo cual seria algo como:

def fun1(*args): print args

def fun_que_recibe_4_parametros(a,b,c,d): fun1(a,b,c,d)

con lo que si escribimos nuestra funcion en fun1 y creamos las funciones que reciben los distintos parametros con un for tenemos lo que necesitamos :)

la salida de la ejecusion de lo de arriba es:

1 0 LOAD_GLOBAL 0 (printer)
3 LOAD_FAST 0 (arg0)
6 LOAD_FAST 1 (arg1)
9 LOAD_FAST 2 (arg2)
12 LOAD_FAST 3 (arg3)
15 CALL_FUNCTION 4
18 RETURN_VALUE

(1, 2, 3, 4)

1 0 LOAD_GLOBAL 0 (printer)
3 LOAD_FAST 0 (arg0)
6 CALL_FUNCTION 1
9 RETURN_VALUE

('only one arg',)
ok, ok, only one argument


fuentes de inspiracion:

http://pyref.infogami.com/type-code
http://docs.python.org/library/dis.html
http://docs.python.org/library/types
http://docs.python.org/reference/datamodel.html#the-standard-type-hierarchy (la parte de callable types)

lunes, julio 20, 2009

Resolviendo ejercicios de MPI (y tambien en erlang) Parte 4

Sigo molestando con estos problemas, me salte uno que no me daba ganas de resolver y pase al siguiente:

http://www.mcs.anl.gov/research/projects/mpi/tutorial/mpiexmpl/src/ring/C/main.html

Write a program that takes data from process zero and sends it to all of the other processes by sending it in a ring. That is, process i should receive the data and send it to process i+1, until the last process is reached.

la solucion en C salio bastante facil haciendo copypasta de los problemas anteriores y sorprendentemente en este caso los otros procesos si imprimieron a la consola:

#include <stdio.h>
#include <mpi.h>

int main (int argc, char** argv) {
int rank, size, value, i;
MPI_Status status;

MPI_Init (&argc, &argv); /* starts MPI */
MPI_Comm_rank (MPI_COMM_WORLD, &rank); /* get current process id */
MPI_Comm_size (MPI_COMM_WORLD, &size); /* get number of processes */

do {
if(rank == 0) {
printf("give me a value: ");
scanf("%d", &value);
fflush(stdout);
MPI_Send(&value, 1, MPI_INT, rank + 1, 1, MPI_COMM_WORLD);
}
else{
MPI_Recv(&value, 1, MPI_INT, rank - 1, 1, MPI_COMM_WORLD, &status);
printf("process %d received %d\n", rank, value);
if(rank + 1 < size) {
printf("sending to %d\n", rank + 1, value);
MPI_Send(&value, 1, MPI_INT, rank + 1, 1, MPI_COMM_WORLD);
}
fflush(stdout);
}
} while(value >= 0);
printf("negative value, closing\n");

MPI_Finalize();
return 0;
}


la solucion en erlang (tambien robando del ejercicio anterior):

-module(ej5).
-export([run/1]).

run(Total) ->
NextPid = spawn_listeners(Total),
get_values(NextPid).

spawn_listeners(Count) -> spawn_listeners(Count, none).

spawn_listeners(0, NextPid) -> NextPid;
spawn_listeners(Count, LastPid) ->
NextPid = spawn(fun() -> listener(LastPid) end),
spawn_listeners(Count - 1, NextPid).

listener(NextPid) ->
receive
Int ->
io:format("process ~p received ~p~n", [self(), Int]),
if
NextPid /= none ->
io:format("sending to ~p~n", [NextPid]),
NextPid ! Int;
true -> ok
end,
if
Int >= 0 -> listener(NextPid);
true ->
io:format("negative value, closing ~p~n", [self()])
end
end.

get_value(Message, ConversionFun) ->
case io:get_line(Message) of
{error, Reason} -> {error, Reason};
eof -> {error, eof};
Value -> ConversionFun(Value)
end.

get_values(NextPid) ->
case get_value("give me an int value (negative to quit): ", fun(Val) -> string:to_integer(Val) end) of
{error, Reason} ->
io:format("error reading int value (~p)~n", [Reason]),
get_values(NextPid);
{IntValue, _Rest} ->
io:format("sending to ~p~n", [NextPid]),
NextPid ! IntValue,
if
IntValue >= 0 -> get_values(NextPid);
true -> io:format("negative value, closing~n")
end
end.


ejemplo de salida:

$ erl
Erlang (BEAM) emulator version 5.6.5 [source] [async-threads:0] [kernel-poll:false]

Eshell V5.6.5 (abort with ^G)
1> c(ej5).
{ok,ej5}
2> ej5:run(3).
give me an int value (negative to quit): 3
sending to <0.40.0>
process <0.40.0> received 3
sending to <0.39.0>
process <0.39.0> received 3
sending to <0.38.0>
process <0.38.0> received 3
give me an int value (negative to quit): asd
error reading int value (no_integer)
give me an int value (negative to quit): 42
sending to <0.40.0>
process <0.40.0> received 42
sending to <0.39.0>
process <0.39.0> received 42
sending to <0.38.0>
process <0.38.0> received 42
give me an int value (negative to quit): -1
sending to <0.40.0>
negative value, closing
process <0.40.0> received -1
sending to <0.39.0>
ok
negative value, closing <0.40.0>
process <0.39.0> received -1
sending to <0.38.0>
negative value, closing <0.39.0>
process <0.38.0> received -1
negative value, closing <0.38.0>

domingo, julio 19, 2009

Resolviendo ejercicios de MPI (y tambien en erlang) Parte 3

enunciado del problema:

In this assignment, you will modify your arguement broadcast routine to communicate different datatypes with a single MPI broadcast (MPI_Bcast) call. Have your program read an integer and a double-precision value from standard input (from process 0, as before), and communicate this to all of the other processes with an MPI_Bcast call. Use MPI datatypes.

Have all processes exit when a negative integer is read.


aca esta la resolucion en C:

#include <stdio.h>
#include <mpi.h>

MPI_Datatype IntDoubleType = 0;

typedef struct {
int value_int;
double value_double;
}IntDouble;

MPI_Datatype create_int_double_type() {
if(IntDoubleType == 0) {
MPI_Aint array_of_displacements[2];
MPI_Datatype array_of_types[2];
int array_of_blocklengths[2];

array_of_displacements[0] = 0;
array_of_types[0] = MPI_INT;
array_of_blocklengths[0] = 1;

array_of_displacements[1] = 1;
array_of_types[1] = MPI_DOUBLE;
array_of_blocklengths[1] = 1;

MPI_Type_struct(1, array_of_blocklengths, array_of_displacements,
array_of_types, &IntDoubleType);
MPI_Type_commit(&IntDoubleType);

}

return IntDoubleType;
}

int main (int argc, char** argv) {
int rank, size, value_int, i;
double value_double;
IntDouble value;
MPI_Status status;

MPI_Init (&argc, &argv); /* starts MPI */
MPI_Comm_rank (MPI_COMM_WORLD, &rank); /* get current process id */
MPI_Comm_size (MPI_COMM_WORLD, &size); /* get number of processes */
create_int_double_type();
printf("process %d of %d\n", rank, size);

do {
if(rank == 0) {
printf("give me an int value: ");
scanf("%d", &value_int);
printf("give me a double value: ");
scanf("%lf", &value_double);
value.value_int = value_int;
value.value_double = value_double;
MPI_Bcast(&value, 1, IntDoubleType, 0, MPI_COMM_WORLD);
}
else {
MPI_Recv(&value, 1, IntDoubleType, 0, 1, MPI_COMM_WORLD, &status);
printf("process %d received %d %lf\n", rank, value.value_int,
value.value_double);
fflush(stdout);
}
} while(value.value_int >= 0);
printf("negative value, closing\n");

MPI_Finalize();
return 0;
}



aca la resolucion en erlang:

-module(ej3).
-export([run/1]).

run(Total) ->
Pids = spawn_listeners(Total),
get_values(Pids).

spawn_listeners(Count) -> spawn_listeners(Count, []).

spawn_listeners(0, Pids) -> Pids;
spawn_listeners(Count, Pids) ->
Pid = spawn(fun() -> listener() end),
spawn_listeners(Count - 1, [Pid|Pids]).

listener() ->
receive
{Int, Double} ->
io:format("process ~p received ~p ~p~n", [self(), Int, Double]),
if
Int >= 0 -> listener();
true ->
io:format("negative value, closing ~p~n", [self()])
end
end.

get_value(Message, ConversionFun) ->
case io:get_line(Message) of
{error, Reason} -> {error, Reason};
eof -> {error, eof};
Value -> ConversionFun(Value)
end.

get_values(Pids) ->
case get_value("give me an int value (negative to quit): ", fun(Val) -> string:to_integer(Val) end) of
{error, Reason} ->
io:format("error reading int value (~p)~n", [Reason]),
get_values(Pids);
{IntValue, _Rest} ->
case get_value("give me a double value): ", fun(Val) -> string:to_float(Val) end) of
{error, Reason} ->
io:format("error reading double value (~p)~n", [Reason]),
get_values(Pids);
{DoubleValue, _Rest} ->
send_values({IntValue, DoubleValue}, Pids),
if
IntValue >= 0 -> get_values(Pids);
true -> io:format("negative value, closing~n")
end
end
end.

send_values(Value, Pids) ->
lists:foreach(fun(Pid) -> Pid ! Value end, Pids).



Algunas observaciones.
Cuando el problema empieza a escalar en complejidad de los tipos de datos enviados vemos que erlang empieza a hacernos las tareas mas faciles sin tener que declarar tipos complejos antes de enviarlos.
Otra cosa que note es que erlang un poco nos obliga a tratar los errores con el tema de pattern matching por lo que si bien el codigo erlang es un poco mas largo en realidad esta teniendo en cuenta todos los posibles casos de fallo, en el de C por ejemplo si no entro un entero o un float, el comportamiento es indeterminado (en mi maquina se mete en un loop infinito haciendo printf). Ya se que deberia usar algo como un buffer de string y despues usar atoi o similar pero para algo esta scanf :D.

Seguidores

Archivo del Blog