WSGI es una interface simple y universal entre los servidores web y las aplicaciones web o frameworks (ver más en PEP 333)
WSGI es similar a la especificación Java Servlet o ASP/ASP.NET. En general, es mucho más simple que dichas especificaciones, y se basa en el estandard CGI con mejoras "pitónicas" para hacerla reentrante, persistente, etc.
Resumen de la Especificación
Del lado de la aplicación, se especifica un punto de entrada (objeto, método, función), con dos parámetros: las variables de entorno (
environ
y la función para iniciar la respuestastart_response(status,response_headers)
que envía el estado y los encabezados), y debe devolver un iterable con los datos para enviar al cliente.Del lado del servidor, se invoca la aplicación por cada pedido que recibe del cliente HTTP, con las variables de entorno establecidas (estilo CGI)
Ejemplo
# Aqui va mi 'Hola PyAr!, pero con WSGI, una maravilla de Python. from wsgiref.simple_server import make_server def hello(environ, start_response): start_response('200 OK',[('Content-type','text/plain')]) return ['Hola PyAr!'] httpd = make_server('',8000, hello).serve_forever()
(copiado de un mail "hello-word" de la lista)
Variables de entorno (diccionario {{{environ}}})
El diccionario environ
que se recibe con cada pedido HTTP, contiene las variables estándard de la especificación CGI, entre ellas:
REQUEST_METHOD: método "GET", "POST", tec.
SCRIPT_NAME : la parte inicial de la "ruta", que corresponde a la aplicación
PATH_INFO: la segunda parte de la "ruta", determina la "ubicación" virtual dentro de la aplicación
QUERY_STRING: la porción de la URL que sigue al "?", si existe
CONTENT_TYPE, CONTENT_LENGTH de la petición HTTP
SERVER_NAME, SERVER_PORT, que combinadas con SCRIPT_NAME y PATH_INFO dan la URL
SERVER_PROTOCOL: la versión del protocolo ("HTTP/1.0" or "HTTP/1.1")
Variables HTTP
Configuración apache + mod_python
mod_python tiene varios handlers o "controladores" (ver documentación):
Handler PSP: utilizado para procesar documentos .psp con código python y html mezclado (similar a PHP)
Handler CGI: emula el entorno CGI (no confundir con WSGI). No es reentrante ni persistente, y es el método más lento para ejecutar scripts web, pero a su vez es históricamente "compatible" con scripts viejos.
Handler Publisher: es un poco mas de "alto nivel". En general, se usaría si uno quiere hacer una aplicación sencilla, con las url mapeadas automáticamente a funciones, etc.
Handler propio: más rapido, pero a costa de tener que programar a mas "bajo nivel" (directamente con las interfaces de apache). Aplicaciones mas avanzadas que requieren un mayor control sobre las url, encabezados, etc., usan handlers propios (ejemplo: trac, moin, etc.):
Igualmente, estos Handlers no son compatibles con WSGI, por eso no recomendaría usar ninguno de ellos directamente, sino a través del wrapper WSGI (con ModPythonGateway) que es un handler "propio" que traduce las peticiones al estandar WSGI. Es algo mucho mas estandar, valga la redundancia, y el día de mañana se puede usar cualquier servidor compatible con python, no solo apache.
Además, puede utilizarse directamente mod_wsgi (ver siguiente sección).
Ejemplos de configuración (tanto en /etc/apache2/... en un archivo .htaccess en el mismo directorio):
# handler Publisher: # se ejecutará cualquier archivo .py del directorio, llamando a la función de la url: # http://www.mysite.com/hello.py/say ejecutara el script hello.py, funcion say <Directory /var/www/html/python/> SetHandler mod_python PythonHandler mod_python.publisher PythonDebug On </Directory> # Handler PSP: # se ejecutará cualquier archivo .psp (código python embebido en texto html) <Directory /var/www/html/psp/> AddHandler mod_python .psp PythonHandler mod_python.psp </Directory> # Handler CGI: # se ejecutará los scripts .py (scripts normales de python) simil linea de comandos <Directory /var/www/cgi-bin/> SetHandler mod_python PythonHandler mod_python.cgihandler Options ExecCGI </Directory> # handler propio: # se ejecuta el archivo myscript.py función handler(req) <Directory /mywebdir> AddHandler mod_python .py PythonHandler myscript PythonDebug On </Directory>
Para configurar una aplicación wsgi en mod_python:
SetHandler python-program PythonHandler modpython_gateway::handler PythonOption wsgi.application app::WSGIApp PythonPath "['C:/Archivos de programa/Apache Software Foundation/Apache2.2/htdocs/app'] + sys.path" PythonOption SCRIPT_NAME /app
Descripción:
Se habilita el handler propio
Se establece el handler a ejecutar (en este caso, el wrapper wsgi)
Se especifican las opciones de la aplicación wsgi (app es el nombre de archivo, WSGIApp es el punto de entrada)
Se agrega el script de la aplicación al path para poder ejecutarla
Se establece el nombre del script a mostrar (sino, en ocasiones, apache puede informar mal o de manera distinta el nombre de script con problemas en el ruteo de urls)
Configuración apache + mod_wsgi
Para usar WSGI directamente desde apache, existe mod_wsgi, que es un módulo mas reciente, totalmente codificado en C para una mejor performance y estabilidad, que simplifica y resuelve las carencias de mod_python:
Ejemplo 1: ejecutar en el mismo proceso que apache (no independiente, estilo mod_python/php/etc.). En este caso se mapea la url /app al script wsgi app.py:
Ejemplo 2: ejecutar en un proceso (interprete) independiente con un usuario arbitrario diferente de apache (estilo FastCGI, mejorando seguridad y performance):
Configuración lighttpd + wsgi
http://cleverdevil.org/computing/24/python-fastcgi-wsgi-and-lighttpd
http://svn.saddi.com/py-lib/trunk/fcgi.py (en realidad es un handler FastCGI compatible con WSGI)
Ejemplo "avanzado"
Con respecto a la diferencia con PHP/PSP, la mayoría de las aplicaciones web en python tienen un solo punto de entrada (un solo .py), que funciona como "despachador", dependiendo de que url te piden, se llama a una función o a otra (generalmente se usa la variable de entorno SCRIPT_NAME o similar, o directamente usar cherrypy, django, turbogears, etc., para que ruteen las peticiones a las clases/funciones que correspondan)
Ejemplo muy simple con WSGI:
def App(environ, start_response): "Punto de entrada WSGI" if environ['SCRIPT_NAME'].endswith("xxxx"): respuesta_html = xxxx(environ) elif environ['SCRIPT_NAME'].endswith("yyyy"): respuesta_html = yyyy(environ) else: respuesta_html = "<html><body><p>la url es inválida!</p></body></html>" start_response ("200 Ok", [('Content-Type','text/html')]) yield respuesta_html
Entonces, si te llaman www.tuservidor.com/aplicacion/xxxx haces una cosa (xxxx), mientras que si llaman a www.tuservidor.com/aplicacion/yyyy haces otra (yyyy). En comparación con php/psp, sería como llamar a www.tuservidor.com/aplicacion.psp?funcion=xxxx o www.tuservidor.com/aplicacion.psp?funcion=yyyy.
Esto es un poco mas difícil de entender, pero a la larga es mas flexible porque no te limita a tener un archivo (estructura "física") para cada dirección (estructura "lógica"), limpiando un poco la url de extensiones .py, signos de interrogación, etc. , haciéndolas mas fáciles de entender para el usuario.