You are on page 1of 4

Python: Pyramid DESARROLLO

El framework que no fue construido por aliengenas

Pyramid
poco a poco. POR JOS MARA RUZ

Uno de los rivales de peso de Django est creciendo en popularidad

jango es el framework que ms est contribuyendo a la extensin del uso de Python. Al igual que ocurri con Ruby y Ruby on Rails, Django se ha convertido en una gran baza para la comunidad Python y la excusa perfecta para probar Python. Est bien documentado, dispone de gran nmero de extensiones y el respaldo de grandes empresas por qu querra alguien crear un competidor? La comunidad Python dista mucho de ser monoltica, aparecen mltiples alternativas para casi todo. Lo curioso es que el novato en los frameworks web es Django! Antes de su aparicin ya existan otros distintos, y Pyramid es el descendiente directo de algunos de ellos [1].

Un hola mundo Minimalista


Podemos instalar Pyramid de muchas formas, pero la ms cmoda desde el punto de vista de Python consiste en crear un virtualenv e instalar en su interior Pyramid:
$ virtualenv --no-site-packages U --distribute pruebas-pyramid $ cd pruebas-pyramid $ source bin/activate (pruebas-pyramid)$ pip U install pyramid

travs de una instancia de Configurator, donde vamos aadiendo rutas, vistas y (como ya veremos) muchos otros tipos de componentes. Configurator es la base sobre la que montamos nuestro sitio web. El concepto de vista es sencillo, al igual que en Django, pudiendo usarse una funcin cualquiera que admita como parmetro un objeto Request con la informacin de la peticin. Se distingue entre declarar una vista y emplearla en distintas rutas. Cada ruta tiene un nombre, route_name, que nos permite conectarla con cualquier ruta. As, los mtodos add_route y add_view trabajan conjuntamente para definir el comportamiento de la web. Una vez hemos acabado con la configuracin podemos arrancar el servidor. Para este sencillo ejemplo hacemos uso de la funcin serve() de paste, que implementa un servidor web en Python que acepta como parmetro una aplicacin WSGI, que obtenemos llamando a make_wsgi_app() de Configurator. Las rutas pueden contener parmetros en su interior que podemos capturar de distintas formas, como ya veremos. Para poder arrancar el servidor slo tenemos que ejecutar el fichero como un programa Python cualquiera e ir a la direccin 127.0.0.1:8080.

dependencia, por lo que podemos usarlo directamente:


$paster create --list-templates Available templates: basic_package: A basic U setuptools-enabled package paste_deploy: A web application U deployed through paste.deploy pyramid_alchemy: pyramid U SQLAlchemy project using traversal pyramid_jinja2_starter: U pyramid jinja2 starter project pyramid_routesalchemy:U pyramid SQLAlchemy project using U url dispatch (no traversal) pyramid_starter:pyramid starter U project pyramid_zodb: U pyramid ZODB starter project

m or gu ef ile .c om

Con estos cuatro pasos dispondremos de un virtualenv con las libreras que Pyramid necesita para funcionar. Es posible crear un proyecto Pyramid con el comando paster (un proyecto como los que creamos con Django), pero como vamos a crear una primera aplicacin minimalista, slo necesitamos en principio un fichero con el contenido que aparece en el Listado 1. A diferencia de Django, Pyramid es un sistema bastante estructurado y centrado en componentes. La configuracin se efecta a

Creando un Proyecto
La generacin de cdigo fuente, el llamado scaffolding, es, a da de hoy, un elemento indispensable de la mayora de los frameworks web. Pyramid no provee directamente un sistema de scaffolding, lo que ira en contra de su poltica de reutilizacin. En lugar de ello hace uso de paster, un sistema independiente de scaffolding que comparten otros proyectos. Cuando instalamos Pyramid con pip se instal paster como

Con la opcin --list-templates podemos ver los proyectos que paster puede generar. Pyramid nos ofrece varias alternativas, desde la ms tradicional, empleando routes y sqlalchemy, hasta otras ms exticas heredadas de Zope, como pyramid_zodb o pyramid_alchemy. La diferencia entre ambas posibilidades est en la forma en que se estructuran las urls y en la base de datos a usar. Zope permite el uso de un sistema llamado traversal que genera automticamente las urls empleando para ello las relaciones entre los modelos de datos usados. La mayora de frameworks web, en casi todos los lenguajes de programacin, se decantan en su lugar por la definicin de las urls de forma explcita, para as tener ms control sobre ellas. Nosotros nos conformaremos con el enfoque tradicional, por lo que podemos crear el proyecto con:

WWW.LINUX- MAGAZINE.ES

Nmero 75

49

DESARROLLO Python: Pyramid

Python, en Pyramid decidieron optar por seguir usando ficheros de configuracin .ini. Una vez haya finalizado el proceso podemos arrancar el servidor web con paster:
$ paster serve U development.ini
Figura 1: Pgina por defecto de Pyramid y debug_toolbar.

Una vez instalada debemos indicar a Pyramid que cargue la extensin y con qu ficheros queremos que use Jinja2 (en nuestro caso los que acaben en .html). Debemos modificar el fichero ejemplo/ejemplo/__init__.py, que alberga la configuracin para nuestro proyecto, y poner dentro de main:
config = U Configurator(settings=settings) config.include(pyramid_jinja2) config.add_renderer(.html,U pyramid_jinja2.rendererU _factory) config.add_static_view(staticU , ejemplo:static) config.scan() config.add_route(portada, /)

$ paster create -t U pyramid_routesalchemy U ejemplo

Como resultado obtendremos un directorio llamado ejemplo que albergar nuestro proyecto, y en su interior un mdulo Python llamado tambin ejemplo. Para poder arrancar el proyecto tenemos que generar primero un fichero de configuracin e instalar los paquetes necesarios mediante el comando:
$ cd ejemplo $ python setup.py develop

Podemos ver la pgina generada en la ruta http://localhost:6543 Esta pgina incluye a la derecha una pestaa que nos da acceso al development toolbar de Pyramid, que nos proporcionar informacin muy valiosa durante el desarrollo de la aplicacin, as como enlaces a la documentacin de Pyramid.

Renderers, Vistas y Plantillas


Pyramid permite configurar diferentes renderers que pueden convivir en el mismo proyecto. Por ejemplo, podemos configurar varios sistemas de plantillas a la vez (Mako, Chamaleon, Jinja2,) y hacer que Pyramid seleccione el correcto basndose en la extensin de la plantilla a usar. En este ejemplo usaremos Jinja2, un sistema de plantillas muy parecido al empleado por Django pero ms flexible y potente. Primero tenemos que instalar la extensin de Pyramid para Jinja2:
$ pp install pyramid_jinja2

La llamada a add_renderer() es la que nos permite indicar a Pyramid que las plantillas con extensin .html debern ser renderizadas empleando Jinja2. Adems llamamos a scan(), que se encargar de buscar las vistas que definamos y nos ahorrar el tener que aadirlas una a una con add_view(), como vimos en el Listado 1. Pero para que esto sea posible nuestra vista debe cambiar la forma de trabajar; debemos poner el siguiente cdigo en ejemplo/ejemplo/views.py:
from pyramid.view import U view_config @view_config (route_name = U portada, renderer = U ejemplo:templates/portada.html) def portada(request): return {saludo:Hola mundo!!}

Pyramid trae dos configuraciones: develop para desarrollo y production para el entorno de produccin. Cada una aparecer como un fichero con extensin .ini que nos permitir configurar el proyecto. Mientras que otros frameworks, como Django, prefieren que la configuracin se haga usando cdigo

Listado 1: Ejemplo de Pyramid Bsico.


01 from paste.httpserver import serve 02 from pyramid.configuration import Configurator 03 from pyramid.response import Response 04 05 def hola_mundo(request): 06 nombre = request.matchdict.get(nombre, mundo) 07 return Response(Hola {0}!.format(nombre)) 08 09 if __name__ == __main__: 10 11 12 13 14 15 16 17 config = Configurator() config.add_route(index, /) config.add_route(hola, /{nombre}) config.add_view(hola_mundo, route_name=hola) config.add_view(hola_mundo, route_name=index) app = config.make_wsgi_app() serve(app, host=0.0.0.0)

Listado 2: Vista que Procesa Parmetros


01 from pyramid.view import view_config 02 03 @view_config(route_name=portada, 04 renderer=ejemplo:templates/portada.html) 05 def portada(request): 06 saludo = Hola mundo!! 07 if request.POST: 08 nombre = request.params. get (nombre, saludo) 09 if nombre: 10 saludo = Hola {0}.format(nombre) 11 12 return {saludo: saludo} 13 14 @view_config(route_name=formulario, 15 renderer=ejemplo:templates/formulario.html) 16 def formulario(request): 17 return {}

50

Nmero 75

WWW.LINUX- MAGAZINE.ES

Python: Pyramid DESARROLLO

Para indicar el route_name y la plantilla que usaremos en la vista portada usaremos el decorador @view_config(). En l podemos definir todos los parmetros que necesita Pyramid para usar la vista. Con el parmetro renderer indicamos que queremos la plantilla portada.html, que debe estar dentro del directorio templates del mdulo ejemplo. Cada mdulo puede disponer de sus propias plantillas independientes, lo que aumenta la modularidad del diseo. Adems, como la plantilla acaba en .html, Pyramid emplear el renderer Jinja2. Jinja2 permite establecer herencia entre plantillas, por lo que crearemos una plantilla ejemplo/ejemplo/templates/base.html:
<html> <body> <h1>Bienvenido<h1> <hr/> {% block contenido %} {% endblock %} </body> </html>

{% block U contenido %} <h2>{{saludo}}U </h2> {% endblock %}

Y otra plantilla ms llamada ejemplo/ejemplo/templates/portada.html:


{% extends ejemplo:U templates/ base.html %}

La extensin pyramid_jinja2 se encarga de convertir la ruta ejemplo:templates /base.html en una ruta del sistema de ficheros que Jinja2 Figura 2: El comando top funcionando en nuestro navegador. pueda utilizar. Vamos a aadir una nueva vista para demostrar A request.route_url() le pasamos el cmo funcionan los enlaces y los formuroute_name de la vista que queremos que larios (ver Listado 2, Listado 3 y Listado procese el formulario. De esta forma pode4). mos decidir cambiar qu vista lo procesar Creamos una nueva vista que apuntasiguiendo cualquier criterio que queramos, mos a la ruta por defecto de nuestro propuesto que la asignacin de un route_name yecto. De esta forma la pgina principal a una vista puede variar durante la ejecucin mostrar un formulario para que podade la llamada (por ejemplo, empleando crimos pasar un nombre. En el Listado 4 terios de seguridad, o si el usuario est regispodemos ver el cdigo de la plantilla trado o no). ejemplo/ejemplo/templates/formulario.ht En el Listado 2 podemos observar que el ml, donde generamos la url que procesar tratamiento de los datos es rudimentario. el formulario as: Pyramid no cuenta con una librera procesador de formularios como Django, sino que <form action = U dependemos del uso de una librera externa. {{request.route_url U Existen varias opciones posibles, pero las (portada)}} method=post> ms conocidas son FormEncode y FormAl-

Listado 3: Configuracin Necesaria para el Listado 2


01 config = Configurator(settings=settings) 02 config.include(pyramid_jinja2) 03 config.add_renderer(.html, pyramid_jinja2.renderer_factory) 04 config.add_static_view(static, ejemplo:static) 05 config.scan() 06 config.add_route(portada, /hola) 07 config.add_route(formulario, /)

Listado 4: Plantilla formulario.html


01 {% extends ejemplo:templates/base.html %} 02 {% block contenido %} 03 <form action={{request.route_url(portada)}} method=post> 04 <p> 05 <input type=text name=nombre/> 06 <button type=submit>enviar</button> 07 </p> 08 </form> 09 {% endblock %}

Listado 5: Plantilla top.html


01 {% extends ejemplo:templates/base.html %} 02 {% block extrahead %} 03 <script src=http://code.jquery.com/jqu ery-1.6.2.min.js></script> 04 <script src=http://cdn.socket.io/stable/socket.io.js></script> 05 <script> 06 var socket = null; 07 var txt = null 08 $(function() { 09 socket = new io.Socket(null, {}); socket.on(connect, function() { 11 socket.send({type: connect, userid: 123}); 12 }); 13 socket.on(message, function(obj) { 14 if (obj.type == showdata) { 15 console.log(Message, JSON.stringify(obj)); 16 txt = obj.txt; 17 $(#htop).html(txt); 18 } 19 }); 20 socket.connect(); 21 }); 10 22 </script> 23 <style> 24 #htop { 25 font-family: monospace; 26 font-size: 12pt; 27 background: black; 28 color: green 29 } 30 </style> 31 {% endblock %} 32 {% block contenido %} 33 <h2>htop</h2> 34 <pre id=htop ></pre> 35 {% endblock %}

WWW.LINUX- MAGAZINE.ES

Nmero 75

51

DESARROLLO Python: Pyramid

chemy [5] [6] . En nuestro caso he decidido procesar a mano la peticin.

Un Ejemplo Ms Potente
De verdad compensa la flexiblidad que nos aporta Pyramid? Con Django es todo mucho ms sencillo, puesto que las decisiones sobre qu libreras emplear ya han sido tomadas. Adems, todas las libreras estn controladas por el proyecto, por lo que su integracin es perfecta. Personalmente creo que Pyramid comienza a rendir cuando necesitamos hacer cosas que no son tradicionales. Uno de los problemas de Django consiste en que fue diseado en un entorno muy bien definido: un peridico. Django no se encuentra muy bien preparado para el entorno actual, donde tecnologas como HTML5 o websockets comienzan a ser cada vez ms importantes. Como ejemplo final de Pyramid vamos a crear una pgina que emplear socket.io [7] para mandar datos a nuestro navegador en tiempo real, lo que definitivamente no es la tpica pgina web tradicional. Instalamos las libreras necesarias:
pip install gevent U gevent-websocket gevent-socketio

stas nos permitirn arrancar nuestro proyecto con un servidor basado en gevent en lugar de usar paster, lo que nos permitir responder a consultas continuadas sin necesidad de cerrar el canal de comunicacin con el navegador.

En el Listado 5 podemos ver el cdigo de la plantilla htop.html. En ella cargamos tanto jquery como la librera socket.io.js, que se encarga de establecer un canal continuo de comunicacin entre el navegador y el servidor. Si el navegador soporta websockets, los usar, pero en caso contrario tratar de interactuar usando otros mecanismos. Lo mejor de socket.io es que nos permite programar toda la interaccin mediante mensajes. Cuando recibimos un mensaje showdata, cambiamos el texto de la etiqueta con id htop por el que recibimos del servidor. Realmente sencilla verdad? No tenemos que ser conscientes ni siquiera sobre cmo se recibe el mensaje o cmo se procesa. El Listado 6 muestra el cdigo que realmente ocurre la magia. Creamos una subclase de SocketIOContext donde conectamos y mandamos un mensaje connected al navegador remoto. Seguidamente definimos la funcin sendhtop, que ser ejecutada por gevent como si fuese una hebra independiente para cada conexin que recibamos. En dicha funcin ejecutamos htop con dos parmetros que le indican que slo nos muestre el estado de los procesos una vez y pare su ejecucin. De esta manera podemos obtener una instantnea de la situacin de nuestro ordenador. La salida de htop la mandamos en un mensaje showdata al navegador e indicamos a gevent que espere 1 segundo. Todo esto se ejecutar en un bucle infinito mientra el navegador est conectado, cosa que sabremos con el resultado de self.io.connected(). De esta forma nuestro navegador

mostrar htop como en la url http://127.0.0.1:6543/htop como si se estuviese ejecutando en l! Es fcil imaginar las posibilidades de esta tecnologa (ver Listado 7 para configuracin).

Conclusin
Si bien Django es el framework web dominante en Python, Pyramid puede ser una alternativa muy interesante si necesitamos realizar una aplicacin web que no sea tradicional. Su sistema basado en componentes nos da acceso a libreras de alta calidad y realmente potentes que normalmente son un fastidio integrar. En este artculo slo hemos rascado la superficie de Pyramid que cuenta con libreras realmente avanzadas para autenticacin, por ejemplo pero espero que el lector haya podido dar sus primeros pasos con el framework que no ha I sido construido por aliengenas! [8].

RECURSOS
[1] Pyramid: https://docs.pylonsproject. org/projects/pyramid/dev/ [2] Pylons: https://www.pylonsproject. org/ [3] Turbogear: http://turbogears.org/ [4] Repoze.bfg: http://bfg.repoze.org/ [5] FormEncode: http://www. formencode.org/en/latest/index.html [6] FormAlchemy: http://code.google. com/p/formalchemy/ [7] Socket.io: http://socket.io/ [8] Pyramid, not built by aliens!: https:// pylonsproject.org/denials/pyramid. html

Listado 6: Conexin socket.io en views.py.


01 from pyramid_socketio.io import SocketIOContext, socketio_manage 02 import gevent 03 04 class ConnectIOContext(SocketIOContext): 05 def msg_connect(self, msg): 06 self.msg(connected) 07 import subprocess 08 09 def sendtop(): 10 prev = None 11 while self.io.connected(): 12 cmd = top -b -n 1 13 p = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE) 14 txt = p.communicate()[0] 15 self.msg(showdata, txt = txt) 16 gevent.sleep(1.0) 17 18 self.spawn(sendtop) 19 20 @view_config(route_name=socket.io) 21 def socket_io(request): 22 print Socket.IO request running 23 retval = socketio_manage(ConnectIOContext(request)) 24 return Response(retval) 25 26 @view_config(route_name=top, 27 renderer=ejemplo:templates/htop.html) 28 def htop(request): 29 return {}

Listado 7: Configuracin necesaria para el ejemplo con socket.io


01 config = Configurator(settings=settings) 02 config.include(pyramid_jinja2) 03 config.add_renderer(.html, pyramid_jinja2.renderer_factory) 04 config.add_static_view(static, ejemplo:static) 05 config.scan() 06 config.add_route(portada, /hola) 07 config.add_route(formulario, /) 08 config.add_route(socket.io, socket.io/*remaining) 09 config.add_route(top, /htop)

52

Nmero 75

WWW.LINUX- MAGAZINE.ES

You might also like