Ir al contenido principal

This is my blog, more about me at marianoguerra.github.io

🦋 @marianoguerra.org 🐘 @marianoguerra@hachyderm.io 🐦 @warianoguerra

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"

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 :)

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)

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...

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)

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.

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;
}

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)

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
process received 3
sending to
process received 3
sending to
process 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
process received 42
sending to
process received 42
sending to
process received 42
give me an int value (negative to quit): -1
sending to
negative value, closing
process received -1
sending to
ok
negative value, closing
process received -1
sending to
negative value, closing
process received -1
negative value, closing

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.