Ver Mensaje Individual
  #1  
Antiguo 11-09-2012
Avatar de D-MO
D-MO D-MO is offline
Miembro
 
Registrado: ago 2005
Ubicación: root@debian:/#
Posts: 1.042
Reputación: 20
D-MO Va por buen camino
Bibliotecas de terceros en hospedaje compartido

Aquí la segunda parte del uso de python en hospedaje compartido, la primera parte está aquí.

Ahora veremos algo con mas posibilidades que simplemente el "Hola Mundo" de la primera parte. Se trata de utilizar bibliotecas de terceros dentro de nuestro proyecto en un hospedaje compartido sin que estas bibliotecas hayan sido instaladas previamente por root, sino que utilizando un directorio local/privado de paquetes python. Esta no es la mejor*1 forma de hacerlo pero opté por ella porque para fines didácticos es la mas fácil y funcional, además de que será el punto de partida para una siguiente fase, un poquito mas avanzada.

El objetivo ahora es poder utilizar un framework web de python que sea el punto de partida para realizar una aplicación web cualquiera utilizando bibliotecas de terceros.

Una herramienta muy fácil de utilizar es web.py, un framework bastante sencillo como poderoso. De él conozco muy poco, pero lo poco que conozco es suficiente para lo que necesitamos.

La aplicación web que propongo no tendrá una funcionalidad muy útil que digamos y mucho menos será nada avanzado, puesto que, lo que nos interesa ahora es utilizar bibliotecas de terceros y empezar a hacer algo dinámico para partiendo de esto lleguemos a hacer algo mas útil donde utilicemos Bases de Datos, usuarios, servicios externos, etc...

Entonces, dejando claro esto, me parece conveniente hacer una utilidad que realice operaciones matemáticas según parámetros de url. Como dije arriba, nada complicado ni útil, pero si didáctico.

Partiendo de la primera parte de este tutorial, creamos un nuevo archivo .py que será el cgi de esta aplicación, llamémosle mate.py, quedándonos entonces una url como: http://midominio.com/cgi-bin/mate.py

Agreguemos a este archivo el mismo código del primer tutorial, con el fin de asegurarnos que nos funciona como el anterior y partamos de allí.

Código:
#!/usr/bin/env python
# -*- coding: UTF-8 -*-

# enable debugging
import cgitb
cgitb.enable()

print "Content-Type: text/plain;charset=utf-8"
print

print "Hello World!"
No olvidar asignarle permisos entre 755 y 777 con tal de que apache lo pueda ejecutar. Ahora a probarlo y si obtenemos el saludo esperado, entonces continuamos.

Prerequisitos
Antes que nada debemos saber que prerequisitos tienen los paquetes que vamos a utilizar porque debemos descargarlos manualmente*2 ya que de momento no contamos con un gestor de paquetes.

Como vamos a utilizar webpy, este sería el primer paquete, además, webpy tiene como prerequisito otro paquete llamado flup. Los links para las versiones estables en este momento son:
Webpy - http://webpy.org/static/web.py-0.37.tar.gz
Flup - http://pypi.python.org/pypi/flup/1.0.2

Estos paquetes vienen con la estructura estandar de paquetes python pero como ahora no tenemos un gestor de paquetes debemos fijarnos bien sobre que es lo que vamos a subir al servidor para que funcione como esperamos (pues de lo contrario no funcioanrá nada). En este caso, estos paquetes tienen en su interior un directorio llamado web y flup respectivamente, estos son los directorios que nos interesan.

Extraigamos el contenido de estos paquetes y copiemos los directorios web y flup dentro del cgi-bin (se que no es lo mas indicado, pero como dije antes, es solo para fines didácticos) quedándonos ahora una estructura de directorios parecida a esta:
Código:
/cgi-bin/
 |...web/
      |...***
      |...application.py
      |...browser.py
      |...***
 |...flup/
      |...client/
      |...server/
      |...__init__.py
 |...mate.py
Ahora modifiquemos mate.py para incluir lo mínimo requerido para hacer funcionar webpy y dejémoslo de esta forma:

Código:
#!/usr/bin/env python
# -*- coding: UTF-8 -*-

# enable debugging
import cgitb; cgitb.enable()

# Third party libraries
import web

urls = (
    '/.*', 'index',
)

app = web.application(urls, globals())

class index:        
    def GET(self):
    	return 'Hola'

app.run()
Si todo va bien, de nuevo el navegador nos estará saludando.

URLs limpias
Como esta aplicación actuará dependiendo de la url que el usuario ingrese, no me gusta para nada que la url base sea "/cgi-bin/mate.py", en cambio, prefiero utilizar el prefijo "/mate/" de modo que me permita tener múltiples aplicaciones configuradas en un mismo dominio/ip. Para ello recurrimos al ModRewrite de apache y agregamos al .htaccess algo como esto:
Código:
RewriteEngine on
RewriteBase /
RewriteRule ^mate(.*)$ cgi-bin/mate.py [PT]
No soy ningún experto en apache y menos en reglas de reescritura, así que esto puede ser mejorado.

También debemos configurar las urls de nuestra aplicación para incluir el prefijo puesto que webpy hace un match sobre la url completa. Lo dejamos de la siguiente manera:

Código:
urls = (
    '/mate/', 'index',
)
De este modo, cuando entremos a http://midominio.com/mate/ no estará cargando la vista index de nuestra aplicación.

Ahora es tiempo de agregar las operaciones que realizará nuestra utilidad matemática, primero nos vamos por las urls.

Código:
urls = (
    '/mate/', 'index',
    '/mate/suma/(.*)/(.*)', 'suma',
    '/mate/resta/(.*)/(.*)', 'resta',
    '/mate/cuadrado/(.*)', 'cuadrado',
    '/mate/cubo/(.*)', 'cubo'
)
Cita:
Observaciones:
  • Todas las urls llevan el prefijo /mate/
  • (.*) es una expresión regular que será transformada en argumento para la función GET de la vista
  • Pueden existir tantos grupos de expresiones regulares como parámetros necesitemos
Ahora nos vamos a las vistas:
Código:
class suma:        
    def GET(self, x, y):
        ix, iy = int(x), int(y)
        r = ix + iy
        return r


class resta:        
    def GET(self, x, y):
        ix, iy = int(x), int(y)
        r = ix - iy
        return r


class cuadrado:        
    def GET(self, x):
        ix = int(x)
        r = ix * ix
        return r


class cubo:        
    def GET(self, x):
        ix = int(x)
        r = ix * ix * ix
        return r
Cita:
Observación:
El mismo número de expresiones regulares tenemos en urls como en parámetros del método GET de la vista adicional al self, ej: '/mate/suma/(.*)/(.*)' => def GET(self, x, y)
Eso sería mas que suficiente para mostrar el resultado, pero en este caso, quiero que la respuesta venga en formato "Resultado: [ <R> ]" así que defino un metodo que me servirá para empaquetar el resultado, manteniendo un código limpio.

Código:
def response(r):
    return 'Resultado: [ %s ]' % r
Este método lo dejo en la parte superior (por simple estética) y lo llamo desde cada vista de la siguiente manera:

Código:
class cubo:        
    def GET(self, x):
        ix = int(x)
        r = ix * ix * ix
        return response(r)
Además, quiero poner una ayuda en el index, así que defino la variable "help" al inicio y devuelvo su valor en la vista.

Código:
help = '''
Servicio Web de Mate
====================

Usa cualquiera de las siguientes urls:

  /suma/<X>/<Y>		Suma los valores X y Y
  /resta/<X>/<Y>	Resta el valor de Y a X
  /cuadrado/<X>		Devuelve el cuadrado de X
  /cubo/<X>		Devuelve el cubo de X
  
  Nota: Los valores de X y Y deben ser enteros
'''
El código final queda de la siguiente manera:

Código:
#!/usr/bin/env python
# -*- coding: UTF-8 -*-

# enable debugging
import cgitb; cgitb.enable()

# Third party libraries
import web

help = '''
Servicio Web de Mate
====================

Usa cualquiera de las siguientes urls:

  /suma/<X>/<Y>		Suma los valores X y Y
  /resta/<X>/<Y>	Resta el valor de Y a X
  /cuadrado/<X>		Devuelve el cuadrado de X
  /cubo/<X>		Devuelve el cubo de X
  
  Nota: Los valores de X y Y deben ser enteros
'''

def response(r):
    return 'Resultado: [ %s ]' % r


urls = (
    '/', 'index',
    '/suma/(.*)/(.*)', 'suma',
    '/resta/(.*)/(.*)', 'resta',
    '/cuadrado/(.*)', 'cuadrado',
    '/cubo/(.*)', 'cubo'
)


app = web.application(urls, globals())


class index:        
    def GET(self):
    	return help


class suma:        
    def GET(self, x, y):
        ix, iy = int(x), int(y)
        r = ix + iy
        return response(r)


class resta:        
    def GET(self, x, y):
        ix, iy = int(x), int(y)
        r = ix - iy
        return response(r)


class cuadrado:        
    def GET(self, x):
        ix = int(x)
        r = ix * ix
        return response(r)


class cubo:        
    def GET(self, x):
        ix = int(x)
        r = ix * ix * ix
        return response(r)


app.run()
¿Quién se anima ahora a hacer este wiki.

Notas
  • *1.- Digo que no es la mejor forma no porque no funcione bien, sino porque al trabajar aplicaciones grandes que requieran de múltiples bibliotecas de terceros será muy difícil de mantener al día todo, sin embargo, de que funciona, funciona... ¡Y lo hace muy bien!.
  • Para no complicar el tema, los valores a procesar se convierten a enteros. Fallará (intencionalmente) si se usan decimales.
Responder Con Cita