Foros Club Delphi

Foros Club Delphi (https://www.clubdelphi.com/foros/index.php)
-   Python (https://www.clubdelphi.com/foros/forumdisplay.php?f=46)
-   -   Bibliotecas de terceros en hospedaje compartido (https://www.clubdelphi.com/foros/showthread.php?t=80227)

D-MO 10-09-2012 23:44:28

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.

mamcx 11-09-2012 00:04:46

Unas notas:

1) Porque no usas las funciones matematicas de python?

2) Una definicion tipo
Código PHP:

Nombre class: 

esta reemplazada por
Código PHP:

Nombre class:(object) 

.

3) La expresion regular se puede mejor cambiando a "\d+". "\d" solo capura numeros, "+" se asegura que haya al menos 1 numero que capturar, en cambio "*" dice, captura tenga o algo que capturar.

4) Si encierras con etiqueta [php] masomenos te da un coloreado parecido para python ;)

Por lo demas, gracias por el aporte!

D-MO 11-09-2012 00:29:31

Cita:

Empezado por mamcx (Mensaje 442705)
1) Porque no usas las funciones matematicas de python?

Digamos que quise hacerlo lo mas simple posible para no meter funciones extras y que se entienda a simple vista sin explicar lo que hace cada función. creo que se entiende mas fácil x * x que math.pow(x, 2), además de que debería importar math y explicar sus funciones. Ya en un escenario real porsupuesto que las usaría... perdón, uso ;).

Cita:

Empezado por mamcx (Mensaje 442705)
2) Una definicion tipo
Código PHP:

Nombre class: 

esta reemplazada por
Código PHP:

Nombre class:(object) 

.

Con webpy no he hecho nada mas grande que lo que hice ahora y lo elegí para esta tutorial por su simplicidad, solo hice un copy & paste del ejemplo de la web del proyecto y no me preocupé tanto por ser pythonic... pero viendo tu ejemplo, no sería:
Código PHP:

Nombre class(object): 

Cita:

Empezado por mamcx (Mensaje 442705)
3) La expresion regular se puede mejor cambiando a "\d+". "\d" solo capura numeros, "+" se asegura que haya al menos 1 numero que capturar, en cambio "*" dice, captura tenga o algo que capturar.

Claro, pero por lo mismo de la nota 1, no quería extender el texto tratando de explicar el porqué un patrón u otro... además de que no soy un experto en ello.

Cita:

Empezado por mamcx (Mensaje 442705)
4) Si encierras con etiqueta [php] masomenos te da un coloreado parecido para python ;)

Gracias, no había pensado en ello.

Cita:

Empezado por mamcx (Mensaje 442705)
Por lo demas, gracias por el aporte!

A ti gracias por las observaciones ;).

Saludos

roman 11-09-2012 01:30:29

Muchas gracias D-MO, mañana lo leeré con calma.

// Saludos

Casimiro Notevi 11-09-2012 02:18:17

ufff... un curso acelerado, habrá que ponerse con ello :)
Gracias.


La franja horaria es GMT +2. Ahora son las 08:28:19.

Powered by vBulletin® Version 3.6.8
Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
Traducción al castellano por el equipo de moderadores del Club Delphi