lunes, 16 de mayo de 2016

Primeros pasos con Pyramid

Que quiero hacer con python una una aplicación web simple y precisa, que no consuma de una base de datos y que tampoco produzca HTML sino contenido JSON (Con lo puedo usar AngularJS para el respectivo cliente web). Así que decidí usar Pyramid, que tenía algunas de las cosas que a primera vista necesito (O más bien que creo necesitar).

La siguiente guía está en constante desarrollo. Básicamente estoy haciendo los primeros pasos después de revisar la guía oficial más completa
La recomendación oficial es crear un entorno virtual en el cual instalar los paquetes
$ virtualenv ambiente
$ cd ambiente/
Activamos el entorno, lo que no significa más que configurar unas cuantas variables del sistema para que usen el ambiente virtual, lo que puede verificarse por curiosidad al revisar el path correspondiente a pip
$ source bin/activate
$ which pip
/home/vtacius/public_html/ambiente/bin/pip
Lo siguiente será instalar pyramid, y para empezar bien y rápido, usaremos pcreate para el scaffolding (Término que no es del todo correcto en estas condiciones) de la aplicación
$ pip install pyramid
$ pcreate -s starter aplicacion
$ cd aplicacion/
Este nivel de la aplicación (./ambiente/aplicacion/) aprovechamos para cambiar el fichero setup.py para retirar en el arreglo requires a pyramid_chameleon porque es el paquete para producir HTML por medio de plantillas, y no pienso usarlo. Agregamos nose y webtest porque no se han incluido en el proyecto
import os

from setuptools import setup, find_packages

here = os.path.abspath(os.path.dirname(__file__))
with open(os.path.join(here, 'README.txt')) as f:
    README = f.read()
with open(os.path.join(here, 'CHANGES.txt')) as f:
    CHANGES = f.read()

requires = [
    'pyramid',
    'pyramid_debugtoolbar',
    'waitress',
    'nose',
    'webtest'
    ]

(...)
Ahora instalamos todos los paquetes requeridos por la aplicación en nuestro entorno virtual:
$ python setup.py develop
A este nivel (./ambiente/aplicacion/) hay que volver cuando se quiere realizar los test y correr waitress.

Ahora entramos dentro de nuestra aplicación propiamente, llamada aplicacion, y acomodamos según nuestras necesidades: eliminamos el directorio template y static porque no pienso usarlo, y volvemos un paquete a views porque planeo crecer con la aplicación aunque sea un poquito:
$ cd aplicacion/
$ rm -rf views.py templates/ static
$ mkdir views
$ touch views/__init__.py
Del fichero __init__.py eliminamos las referencias a pyramid_chameleon, y la configuración de contenido estático que nos es innecesaria:
    config.include('pyramid_chameleon')
    config.add_static_view('static', 'static', cache_max_age=3600)
Para este primer ejercicio, basta con crear un fichero en views/ de cualquier nombre (Sí, cualquier nombre, la magia de scan) de la siguiente forma:
# coding: utf-8
from pyramid.view import view_config

@view_config(route_name='home', renderer='json')
def get_actividad_list(request):
    return {'home': '¡Hola Mundo!'}
Volvemos un directorio atrás y corremos la aplicación:
$ pserve development.ini --reload
Ahora lo probamos, desde consola porque somos gente hardcore que odia a los navegadores (¡Muerte a los navegadores!)
$ curl -i -X GET http://localhost:6543
HTTP/1.1 200 OK
Content-Length: 29
Content-Type: application/json; charset=UTF-8
Date: Tue, 17 May 2016 04:11:18 GMT
Server: waitress


{"home": "\u00a1Hola Mundo!"}
Preparé una especie de test que sale más extenso que el código que se quiere testar, tendrá que valer para primer esfuerzo. El punto más discutible es el de la fixture, sin embargo la idea debería estar en ese sentido. El contenido de tests.py queda de la siguiente forma:
# coding: utf-8
from unittest import TestCase

from pyramid import testing

class ViewTests(TestCase):
    def setUp(self):
        self.config = testing.setUp()
        # Atentos a nuestro intento de fixture
        self.res_get_actividad_list = {'home': '¡Hola Mundo!'}

    def tearDown(self):
        testing.tearDown()

    def test_get_actividad_list(self):
        # Hacemos las importaciones acá en lugar de globalmente para que el test sea más sincero
        from .views.actividades import get_actividad_list
        peticion = testing.DummyRequest()
        respuesta = get_actividad_list(peticion)
        self.assertEqual(respuesta['home'], self.res_get_actividad_list['home'])

class ViewFunctionalTest(TestCase):
    def setUp(self):
        # Este método debería considerarse un __init__ más apropiado en la cuestión de testeo
        # Hacemos las importaciones acá en lugar de globalmente para que el test sea más sincero
        from aplicacion import main
        app = main({})
        from webtest import TestApp
      
        self.testapp = TestApp(app)

        # Atentos a nuestro intento de fixture
        self.res_get_actividad_list = {'home': u'¡Hola Mundo!'}
  
    def test_get_actividad_list(self):
        respuesta = self.testapp.get('/', status=200, xhr=True)
        # Es discutible que haga esto, supongo que me aseguro que no vaya a cambiar la respuesta por accidente
        self.assertEqual('application/json', respuesta.content_type) 
        # Trato de usar la fixture para verificar contenido ya conocido
        proposicion = self.res_get_actividad_list['home']
        self.assertEqual(respuesta.json_body['home'], proposicion)

Ahora corremos el test en el directorio raíz de la aplicación
(ambiente)vtacius@ilaria:~/public_html/ambiente/aplicacion> nosetests
..
Name                              Stmts   Miss  Cover   Missing
---------------------------------------------------------------
aplicacion.py                         8      0   100%   
aplicacion/tests.py                  25      0   100%   
aplicacion/views.py                   0      0   100%   
aplicacion/views/actividades.py       3      0   100%   
---------------------------------------------------------------
TOTAL                                36      0   100%   
----------------------------------------------------------------------
Ran 2 tests in 1.123s

OK
Claro, los test automatizados son realmente algo increíble. Apenas pueden reemplazar al hecho de usar curl desde consola, pero todo lo compensan con el hecho de ser automatizados.
El siguiente paso es revisar la aplicación que vamos a publicar por medio de pyramid: fichero_app

No hay comentarios:

Publicar un comentario en la entrada

Otros apuntes interesantes

Otros apuntes interesantes