El Núcleo

Opciones de la Línea de Comandos

Es posible omitir el GUI e iniciar web2py directamente desde la línea de comandos escribiendo algo como:

python web2py.py -a 'your password' -i 127.0.0.1 -p 8000

Cuando web2py se inicia, crea un archivo llamado “parameters_8000.py” donde guarda la clave hash. Si usa “<ask>” como la clave, web2py le sugiere una.

Para seguridad adicional, puede iniciar web2py así:

python web2py.py -a '<recycle>' -i 127.0.0.1 -p 8000

En este caso web2py reutiliza la clave previamente guardada. Si no se provee clave alguna, o si el archivo “parameters_8000.py” es borrado, la interfaz administrativa web es deshabilitada.

En algunos sistemas Unix/Linux, si la clave es:

 <pam_user:some_user>

web2py usa la clave PAM de la cuenta del sistema operativo de some_user para autenticar el administrador, a menos que se bloquee por la configuración PAM.

web2py normalmente funciona con Cpython (la implementación C del intérprete de Python creado por Guido van Rossum), pero también puede funcionar con Jython (la implementación Java del intérprete). La segunda posibilidad permite el uso de web2py en el contexto de una infraestructura J2EE. Para usar Jython, simplemente reemplace “pythons web2py.py...” por “jython web2py.py”. Detalles acerca de la instalación de Jython, así como de módulos zxJDBC necesarios para acceder a las bases de datos pueden ser encontrados en el Capítulo 12.

El script “web2py.py” puede tomar muchos argumentos de la línea de comandos especificando el máximo número de tareas, habilitación de SSL, etc. Para una lista completa, escriba:

     >>> python web2py.py -h
     Usage: python web2py.py

     web2py Web Framework startup script. ATTENTION: unless a password
     is specified (-a 'passwd'), web2py will attempt to run a GUI.
     In this case command line options are ignored.
     Options:
       --version             show program's version number and exit
       -h, --help            show this help message and exit
       -i IP, --ip=IP        ip address of the server (127.0.0.1)
       -p PORT, --port=PORT  port of server (8000)
       -a PASSWORD, --password=PASSWORD
                                 password to be used for administration
                                 use -a "<recycle>" to reuse the last
                                 password
       -u UPGRADE, --upgrade=UPGRADE
                                 -u yes: upgrade applications and exit
       -c SSL_CERTIFICATE, --ssl_certificate=SSL_CERTIFICATE
                                 file that contains ssl certificate
       -k SSL_PRIVATE_KEY, --ssl_private_key=SSL_PRIVATE_KEY
                                 file that contains ssl private key
       -d PID_FILENAME, --pid_filename=PID_FILENAME
                                 file to store the pid of the server
       -l LOG_FILENAME, --log_filename=LOG_FILENAME
                                 file to log connections
       -n NUMTHREADS, --numthreads=NUMTHREADS
                                 number of threads
       -s SERVER_NAME, --server_name=SERVER_NAME
                                 server name for the web server
       -q REQUEST_QUEUE_SIZE, --request_queue_size=REQUEST_QUEUE_SIZE
                                 max number of queued requests when server unavailable
       -o TIMEOUT, --timeout=TIMEOUT
                                 timeout for individual request (10 seconds)
       -z SHUTDOWN_TIMEOUT, --shutdown_timeout=SHUTDOWN_TIMEOUT
                                 timeout on shutdown of server (5 seconds)
       -f FOLDER, --folder=FOLDER
                                 folder from which to run web2py
       -v, --verbose         increase --test verbosity
       -Q, --quiet           disable all output
       -D DEBUGLEVEL, --debug=DEBUGLEVEL
                                 set debug output level (0-100, 0 means all,
                                 100 means none; default is 30)
       -S APPNAME, --shell=APPNAME
                                 run web2py in interactive shell or IPython
                                 (if installed) with specified appname
       -P, --plain           only use plain python shell; should be used
                                 with --shell option
       -M, --import_models   auto import model files; default is False;
                                 should be used with --shell option
       -R PYTHON_FILE, --run=PYTHON_FILE
                                 run PYTHON_FILE in web2py environment;
                                 should be used with --shell option
       -T TEST_PATH, --test=TEST_PATH
                                 run doctests in web2py environment;
                                 TEST_PATH like a/c/f (c,f optional)
       -W WINSERVICE, --winservice=WINSERVICE
                                 -W install|start|stop as Windows service
       -C, --cron            trigger a cron run manually; usually invoked
                                 from a system crontab
       -N, --no-cron         do not start cron automatically
       -L CONFIG, --config=CONFIG
                                 config file
       -F PROFILER_FILENAME, --profiler=PROFILER_FILENAME
                                 profiler filename
       -t, --taskbar         use web2py gui and run in taskbar
                                 (system tray)
       --nogui               text-only, no GUI
       -A ARGS, --args=ARGS  should be followed by a list of arguments to be passed
                                 to script, to be used with -S, -A must be the last
                                 option
       --interfaces=INTERFACES
                                 allows multiple interfaces to be served

Las opciones en minúscula son usadas para configurar el servidor web. La opción -L le dice a web2py que lea las opciones de configuración desde un archivo, -W instala web2py como un servicio de Windows, mientras que las opciones -S, -P y -M inician un shell interactivo de Python. La opción -T encuentra e inicia pruebas de controlador en un entorno de ejecución web2py. Por ejemplo, el siguiente ejemplo corre pruebas de todos los controladores en la aplicación “welcome”:

     python web2py.py -vT welcome

si inicia web2py como un servicio de Windows, -W, no es conveniente ingresar la configuración usando argumentos de línea de comandos. Por ésta razón, en la carpeta de web2py hay un archivo de configuración de ejemplo “options_std.py” para el servidor web interno:

     import socket, os
     ip = '127.0.0.1'
     port = 8000
     password = '<recycle>' ### <recycle> means use the previous password
     pid_filename = 'httpserver.pid'
     log_filename = 'httpserver.log'
     ssl_certificate = " ### path to certificate file
     ssl_private_key = " ### path to private key file
     numthreads = 10
     server_name = socket.gethostname()
     request_queue_size = 5
     timeout = 10
     shutdown_timeout = 5
     folder = os.getcwd()

Este archivo contiene la configuración predeterminada de web2py. Si usted edita este archivo, necesitará importarlo de manera explícita con la opción -L en línea de comandos. Solo funciona si usted corre web2py como un servicio de Windows.

Despacho

web2py mapea los URLs de la forma:

http://127.0.0.1:8000/a/c/f.html

a la función f() en el controlador “c.py” en la aplicación “a”. Si f no está presente, web2py usa por defecto la función de controlador index. Si c no está presente, web2py usa por defecto el controlador “default.py”, y si a no está presente, web2py usa de manera predeterminada la aplicación init. Si no hay aplicación init, web2py trata de usar la aplicación welcome. Todo ésto se muestra de manera esquemática en la siguiente imagen:

(los nombres de la aplicación, controlador y función por defecto pueden ser cambiados en routes.py; vea Default Application, Controller and Function más abajo)

Por defecto, cualquier solicitud nueva también crea una nueva sesión. Además, una cookie de sesión es retornada al navegador del cliente para realizar un seguimiento de la sesión.

La extensión .html es opcional; se asume.html por defecto. La extensión determina la extensión de la vista que renderiza la salida de la función f() del controlador. Permite que el mismo contenido sea servido en múltiples formatos (html, xml, json, rss, etc.).

Funciones que toman argumentos o que comienzan con doble subguión (underscore) no son expuestos públicamente y sólo pueden ser llamadas por otras funciones.

Hay una excepción hecha para URLs de la forma:

http://127.0.0.1:8000/a/static/filename

No hay controlador llamado “static”. Web2py interpreta ésto como una solicitud para un archivo llamado “filename” en la subcarpeta “static” de la aplicación “a”

Cuando archivos estáticos son descargados, web2py no crea una sesión, ni emite un cookie o ejecuta los modelos. Web2py siempre retorna los archivos estáticos en bloques de 1 MB, y envia CONTENIDO PARCIAL cuando el cliente envía una solicitud RANGE para un subset del archivo.

Web2py también soporta el protocolo IF_MODIFIED_SINCE, y no envía el archivo si ya está guardado en el cache del navegador y si el archivo no ha cambiado desde esa versión.

Web2py mapea las solicitudes GET/POST de la forma:

http://127.0.0.1:8000/a/c/f.html/x/y/z?p=1&q=2

a la función f en el controlador “c.py” en la aplicación a, y guarda los parámetros del URL en la variable request como sigue:

1
     request.args = ['x', 'y', 'z']

y:

1
     request.vars = {'p':1, 'q':2}

y:

1
2
3
     request.application = 'a'
     request.controller = 'c'
     request.function = 'f'

En éste ejemplo, tanto request.args[i] como request.args(i) pueden ser usadas para regresar el i-ésimo elemento de request.args, pero mientras el primero levanta una excepción si la lista no tiene tan índice, el segundo regresa None en éste caso.

1
     request.url

guarda todo el URL de la solicitud actual (sin incluir las variables GET)

1
     request.ajax

está por defecto en False pero es True si web2py determina que la acción fué llamada por una solicitud de Ajax.

Si la solicitud es una solicitud Ajax y es iniciada por un componente web2py, el nombre del componente puede ser encontrado en:

1
     request.cid

Los componentes son explicados con detenimiento en el capítulo 13.

Si la solicitud HTTP es un GET, entonces request.env.request_method se establece a “GET”; si es un POST, request.env.request_method es establecido a “POST”. Variables de búsquedas URL son almacenadas en el diccionario de almacenaje request.vars; también son almacenadas en request.get_vars (después de una solicitud GET) o request.post_vars (después de una solicitud POST).

Web2py guarda las variables de entorno web2py y WSI en request.env, por ejemplo:

1
     request.env.path_info = 'a/c/f'

Y encabezados HTTP en variables del entorno, por ejemplo:

     request.env.http_host = '127.0.0.1:8000'

     Note que web2py valida todos los URLs para prevenir ataques que recorran el directorio.

A las URLs sólo se les permite contener caracteres alfanuméricos, subguiones (underscore), barras oblicuas (slashes); the args pueden contener puntos no consecutivos. Los espacios son reemplazados por subguiones antes de la validación. Si la sintaxis del URL es inválida, web2py devuelve un mensaje error HTTP 400.

Si el URL corresponde a una solicitud de un archivo estático, web2py simplemente lee y retorna (streams) el archivo solicitado.

Si el URL no solicita un archivo estático, web2py procesa la solicitud en el siguiente orden:

  • Analiza las cookies.
  • Crea un entorno en el cual ejecutar la función.
  • Inicializa request, response, cache.
  • Abre una sesión existente o crea una nueva.
  • Ejecuta los modelos pertenecientes a la aplicación solicitada.
  • Ejecuta el la función de acción del controlador solicitada.
  • Si la función retorna un diccionario, ejecuta la vista asociada.
  • Al ser correcta la solicitud, confirma todas las transacciones abiertas.
  • Guarda la sesión.
  • Devuelve una respuesta HTTP.

Note que el controlador y la vista son ejecutadas en diferentes copias del mismo entorno; por lo tanto, la vista no ve el controlador, pero ve los modeles y ve las variables retornadas por la función acción del controlador.

Si una excepción (distinta a HTTP) ocurre, web2py hace lo siguiente:

  • Almacena el registro en un archivo de error y le asigna un número de ticket.
  • Revierte todas las transacciones abiertas.
  • Retorna una página de error reportando el número del ticket.

Si la excepción es una excepción HTTP, se asume que éste es el comportamiento deseado (por ejemplo, una redirección HTTP), y todas las transacciones abiertas de bases de datos se confirman. El comportamiento después de ésto es especificado por la excepción HTTP en sí. La clase de excepción HTTP no es una excepción estándar de Python; es definida por web2py.

Librerías

Las librerías de web2py son expuestas a las aplicaciones del usuario como objetos globales. Por ejemplo (request, response, session, cache), clases (auxiliares, validadores, API DAL), y funciones (T y redirect).

Éstos objetos están definidos en los siguientes archivos básicos:

     web2py.py
     gluon/__init__.py    gluon/highlight.py   gluon/restricted.py  gluon/streamer.py
     gluon/admin.py       gluon/html.py        gluon/rewrite.py     gluon/template.py
     gluon/cache.py       gluon/http.py        gluon/rocket.py      gluon/storage.py
     gluon/cfs.py         gluon/import_all.py  gluon/sanitizer.py   gluon/tools.py
     gluon/compileapp.py  gluon/languages.py   gluon/serializers.py gluon/utils.py
     gluon/contenttype.py gluon/main.py        gluon/settings.py    gluon/validators.py
     gluon/dal.py         gluon/myregex.py     gluon/shell.py       gluon/widget.py
     gluon/decoder.py     gluon/newcron.py     gluon/sql.py         gluon/winservice.py
     gluon/fileutils.py   gluon/portalocker.py gluon/sqlhtml.py     gluon/xmlrpc.py
     gluon/globals.py     gluon/reserved_sql_keywords.py

La aplicación tar gzipped basada en base de datos que se incluye en web2py es:

1
     welcome.w2p

Es creado durante la instalación y reemplazado al actualizar.

La primera vez que usted inicia web2py, dos nuevas carpetas son creadas: deposit y applications. La aplicación “welcome” es insertada al archivo “welcome.w2p” para ser usada como aplicación basada en base de datos. La carpeta deposit es usada como almacenamiento temporal para instalar y desinstalar aplicaciones.

Las unidades de prueba de web2py están en:

     gluon/tests/

Hay manejadores de conexión usados para conectar con varios servidores web:

1
2
3
4
5
6
7
     cgihandler.py
     gaehandler.py
     fcgihandler.py
     wsgihandler.py
     modpythonhandler.py
     gluon/contrib/gateways/__init__.py
     gluon/contrib/gateways/fcgi.py

(fcgi.py fué desarrollado por Allan Saddi)

Hay dos archivos ejemplo:

1
2
     options_std.py
     routes.example.py

El primero es un archivo opcional de configuración que puede ser pasado a web2py con la opción -L. El segundo es un ejemplo de un archivo de mapeo de URL. Es cargado automáticamente cuando se renombra “routes.py”.

Los archivos:

1
2
     app.yaml
     index.yaml

son archivos de configuración necesarios para el despliegue en el motor de aplicaciones de Google (Google App Engine). Probablemente no tenga que modificarlos, pero puede leer más acerca de ellos en las paginas de documentación de Google.

También hay librerías adicionales, usualmente desarrolladas por terceros:

feedparser (28) por Mark Pilgrim para leer los canales RSS y Atom:

1
2
     gluon/contrib/__init__.py
     gluon/contrib/feedparser.py

markdown2 (29) por Trent Mick para marcado wiki:

1
2
     gluon/contrib/markdown/__init__.py
     gluon/contrib/markdown/markdown2.py

markmin marcado:

1
     gluon/contrib/markmin.py

pysimplesoap es una implementación de servidor SOAP liviano creado por Mariano Reingart:

     gluon/contrib/pysimplesoap/

memcache (30) API Python creado por Evan Martin:

1
2
     gluon/contrib/memcache/__init__.py
     gluon/contrib/memcache/memcache.py

gql, un puerto de DAL hacia el motor de aplicaciones de Google:

1
     gluon/contrib/gql.py

memdb, un puerto de DAL encima de memcache:

1
     gluon/contrib/memdb.py

gae_memcache es un API para usar memcache en el motor de aplicaciones de Google:

1
     gluon/contrib/gae_memcache.py

pyrtf (26) para generar documentos con formato de texto rico (Rich Text Format: RTF), desarrollado por Simon Cusack y revisado por Grant Edwards:

1
2
3
4
5
6
7
8
     gluon/contrib/pyrtf
     gluon/contrib/pyrtf/__init__.py
     gluon/contrib/pyrtf/Constants.py
     gluon/contrib/pyrtf/Elements.py
     gluon/contrib/pyrtf/PropertySets.py
     gluon/contrib/pyrtf/README
     gluon/contrib/pyrtf/Renderer.py
     gluon/contrib/pyrtf/Styles.py

PyRSS2Gen (27) desarrollado por Dalke Scientific Software, para generar canales RSS:

1
     gluon/contrib/rss2.py

Simplejson (25) por Bob Ippolito, la librería estándar para analizar (parse) y escribir objetos JSON:

1
2
3
4
5
     gluon/contrib/simplejson/__init__.py
     gluon/contrib/simplejson/decoder.py
     gluon/contrib/simplejson/encoder.py
     gluon/contrib/simplejson/jsonfilter.py
     gluon/contrib/simplejson/scanner.py

AuthorizeNet (92) proporciona un API para aceptar pagos con tarjetas de crédito mediante la red Autorize.net:

1
     gluon/contrib/AuthorizeNet.py

PAM (72) API de autenticación creado por Chris AtLee:

1
     gluon/contrib/pam.py

Un clasificador Bayesiano para llenar la base de datos con información irrelevante con fines de prueba:

1
     gluon/contrib/populate.py

Un archivo que permite la interacción con la barra de tareas en Windows, cuando web2py está trabajando como un servicio:

1
     gluon/contrib/taskbar_widget.py

login_methods y login_forms opcionales para ser usados en autenticación:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
     gluon/contrib/login_methods/__init__.py
     gluon/contrib/login_methods/basic_auth.py
     gluon/contrib/login_methods/cas_auth.py
     gluon/contrib/login_methods/email_auth.py
     gluon/contrib/login_methods/extended_login_form.py
     gluon/contrib/login_methods/gae_google_account.py
     gluon/contrib/login_methods/ldap_auth.py
     gluon/contrib/login_methods/linkedin_account.py
     gluon/contrib/login_methods/oauth20_account.py
     gluon/contrib/login_methods/openid_auth.py
     gluon/contrib/login_methods/pam_auth.py
     gluon/contrib/login_methods/rpx_account.py

Web2py también contiene una carpeta con útiles scripts:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
     scripts/setup-web2py-fedora.sh
     scripts/setup-web2py-ubuntu.sh
     scripts/cleancss.py
     scripts/cleanhtml.py
     scripts/contentparser.py
     scripts/repair.py
     scripts/sessions2trash.py
     scripts/sync_languages.py
     scripts/tickets2db.py
     ...

Los primeros dos son particularmente útiles debido a que intentan realizar una completa instalación y configuración del entorno de producción de web2py desde cero. Estos son discutidos en el capítulo 12, pero son más o menos auto-explicativos.

Finalmente web2py incluye los siguientes archivos necesarios para construir las distribuciones binarias.

1
2
3
     Makefile
     setup_exe.py
     setup_app.py

Éstos son scripts de configuración para py2exe y py2app respectivamente y son requeridos solamente para las distribuciones binarias de web2py. NUNCA DEBERIA NECESITAR CORRERLOS.

  • En sumario, las librerías de web2py proporcionan la siguiente funcionalidad:
  • Mapean las URLs en llamados a funciones.
  • Se ocupan de los parámetros de paso y retorno por medio de HTTP.
  • Realizan validación a dichos parámetros.
  • Protegen las aplicaciones de la mayoría de los problemas de seguridad.
  • Se ocupan de la persistencia de los datos (base de datos, sesión, cache, cookies)
  • Realizan traducciones de las cadenas para varios idiomas soportados.
  • Generan HTML de manera programática (ejem. desde las tablas de bases de datos).
  • Generan SQL por medio de la capa de abstracción de base de datos (DAL).
  • Generan salida en formato de texto rico (RTF).
  • Generan salidas de valor separado por coma (CVS) desde las tablas de base de datos.
  • Generan canales RSS.
  • Generan cadenas de serialización en notación de objetos JavaScript (JSON) para Ajax.
  • Traducen marcados wiki (Markdown) a HTML .
  • Exponen servicios web XML-RPC.
  • Cargan y descargan grandes archivos por medio de streaming.

Las aplicaciones web2py contienen archivos adicionales, particularmente librerías JavaScript de terceros, tales como jQuery, calendar, EditArea y nicEdit. Sus autores son reconocidos dentro de los mismos archivos.

Aplicaciones

El desarrollo de aplicaciones en web2py se compone de las siguientes partes: * Los modelos (models) describen una representación de los datos como tablas de base de datos y relaciones entre tablas.

  • Los controladores (controllers) describen la lógica y el flujo de trabajo de la aplicación.
  • Las vistas (views) describen como debe ser presentados los datos al usuario usando HTML y JavaScript.
  • Los idiomas (languages) describen como deben ser traducidas las cadenas en la aplicación a varios idiomas soportados.
  • Los archivos estáticos (static files) no requieren procesamiento (ejem. imágenes, hojas de estilo CSS, etc).
  • Los documentos “acerca de” (ABOUT) y “léeme” (README) son auto explicativos.
  • errors guarda los reportes de error generados por la aplicación.
  • session guarda la información relacionada a cada usuario en particular.
  • databases guarda las bases de datos SQLite además de información adicional sobre las tablas.
  • cache guarda los ítem de la aplicación colocados en el cache.
  • modules son otros módulos Python opcionales.
  • Los archivos privados (private) son accesados por los controladores pero no directamente por el desarrollador.
  • Los archivos cargados (uploads) son accesados por los modelos pero no directamente por el desarrollador (ejem. los archivos cargados por los usuarios de la aplicación)
  • test es un directorio para guardar los scripts, objetos modulados, y accesorios de prueba.

Los modelos, vistas, controladores, idiomas y archivos estáticos son accesibles por medio de la interfaz web de administración [diseño]. ABOUT, README y los errores también pueden ser accesados desde la interfaz web de administración a través de los ítem de menús correspondientes. Las sesiones, el cache, los módulos y los archivos privados pueden ser accesibles desde la aplicación pero no mediante la interfaz de administración.

Todo está bien organizado en una clara estructura de directorio que es replicada para cada aplicación web2py instalada, aunque el usuario nunca necesita accesar a los archivos de sistema directamente:

     __init__.py  ABOUT        LICENSE    models    views
     controllers  modules      private    tests     cron
     cache        errors       upload     sessions  static

“__init__.py” es un archivo vacío que es necesario para que Python (y web2py) puedan importar los módulos del directorio modules.

Note que la aplicación admin simplemente provee de una interfaz web a las aplicaciones web2py que están en el sistema de archivos del servidor. Las aplicaciones web2py también pueden ser creadas y desarrolladas desde la linea de comandos; usted no necesita usar la interfaz de navegación admin. Una nueva aplicación puede ser creada manualmente replicando la estructura de directorio vista anteriormente bajo, por ejemplo, “applications/newapp/” (o simplemente descomprima el archivo welcome.w2p en el directorio de su nueva aplicación). Los archivos de aplicaciones también pueden ser creados y editados desde la linea de comandos sin tener que usar la interfaz web admin.

API

Los modelos, controladores y vistas son ejecutado en un entorno donde los siguientes archivos ya son importados para nosotros:

Objetos Globales:

1
     request, response, session, cache

Navegación:

1
     redirect, HTTP

Internacionalizacion:

1
     T

Auxiliares:

1
2
3
4
5
6
7
8
     XML, URL, BEAUTIFY
     A, B, BEAUTIFY, BODY, BR, CENTER, CODE, DIV, EM, EMBED,
     FIELDSET, FORM, H1, H2, H3, H4, H5, H6, HEAD, HR, HTML,
     I, IFRAME, IMG, INPUT, LABEL, LEGEND, LI, LINK, OL, UL,
     MARKMIN, MENU, META, OBJECT, ON, OPTION, P, PRE, SCRIPT,
     OPTGROUP, SELECT, SPAN, STYLE, TABLE, TAG, TD, TEXTAREA,
     TH, THEAD, TBODY, TFOOT, TITLE, TR, TT, URL, XHTML,
     xmlescape, embed64

Validadores:

1
2
3
4
5
6
7
     CLEANUP, CRYPT, IS_ALPHANUMERIC, IS_DATE_IN_RANGE, IS_DATE,
     IS_DATETIME_IN_RANGE, IS_DATETIME, IS_DECIMAL_IN_RANGE,
     IS_EMAIL, IS_EMPTY_OR, IS_EXPR, IS_FLOAT_IN_RANGE, IS_IMAGE,
     IS_IN_DB, IS_IN_SET, IS_INT_IN_RANGE, IS_IPV4, IS_LENGTH,
     IS_LIST_OF, IS_LOWER, IS_MATCH, IS_EQUAL_TO, IS_NOT_EMPTY,
     IS_NOT_IN_DB, IS_NULL_OR, IS_SLUG, IS_STRONG, IS_TIME,
     IS_UPLOAD_FILENAME, IS_UPPER, IS_URL

Base de Datos:

1
     DAL, Field

Para compatibilidad con versiones anteriores SQLBD=DAL y SQLField=Field. Le recomendamos usar la nueva sintaxis DAL y Field, en vez de la antigua sintaxis.

Otros objetos y módulos son definidos en las librerías, pero no son importados de manera automática ya que no son usados con tanta frecuencia.

Las entidades centrales del API en el entorno de ejecución de web2py son request, response, session, cache, URL, HTTP, redirect y T y son discutidas más abajo. Unos cuantos objetos y funciones, incluyendo Auth, Crud y Service, son definidos en “gluon/tools.py” y necesitan ser importados como sea necesario:

1
     from gluon.tools import Auth, Crud, Service

request

El objeto request es una instancia de la ubicua clase web2py llamada gluon.storage.Storage, que complementa la clase dict de Python. Es básicamente un diccionario, pero los valores de los ítems pueden ser accesados como atributos:

1
     request.vars

es lo mismo que:

1
     request['vars']

A diferencia de un diccionario, si un atributo (o clave) no existe, no levanta una excepción. En cambio devuelve None.request que tiene los siguientes ítems/atributos, alguno de los cuales también son una instancia de la clase Storage:

  • request.cookies: un objeto Cookie.SimpleCookie() que contiene los cookies pasados con la solicitud HTTP. Actúa como un diccionario de cookies. Cada cookie es un objeto Morsel.
  • request.env: un objeto Storage que contiene las variables de entorno pasadas al controlador, incluyendo las variables de encabezado HTTP de la solicitud HTTP así como los parámetros estándar WSGI. Las variables de entorno son todas pasadas a minúscula, y los puntos son convertidos a subgiones para una fácil memorización.
  • request.application: el nombre de la aplicación solicitada (analizada desde request.env.path_info).
  • request.info: el nombre de la función solicitada(analizada desde request.env.path_info).
  • request.extension: la extensión de la acción solicitada. Por defecto es “html”. Si la función del controlador retorna un diccionario y no especifica una vista, esto es usado para determinar la extensión del archivo de la vista que renderizará el diccionario (analizado desde request.env.path_info).
  • request.folder: el directorio de la aplicación. Por ejemplo, si la aplicación es “welcome”, request.folder toma el valor de la ruta absoluta “/path/to/welcome”. En sus programas, usted siempre debe usar esta variable y la función os.path.join para construir las rutas a los archivos que necesite accesar. Aunque web2py siempre usa rutas absolutas, es una buena regla nunca cambiar de manera explícita la actual carpeta en funcionamiento (cualquiera que esta sea) ya que esta no es una practica segura.
  • request.now: un objeto datetime.datetime que guarda la hora y fecha de la actual solicitud.
  • request.args: una lista de los componentes de ruta del URL seguido por el nombre de la función del controlador; equivalente a request.env.path_info.split(‘/’)[3:]
  • request.vars: un objeto gluon.storage.Storage que contiene las variables de busqueda HTTP GET y HTTP POST.
  • request.get_vars: un objeto gluon.storage.Storage que contiene solo las variables de búsqueda HTTP GET.
  • request.post_vars: un objeto gluon.storage.Storage que contiene solo las variables de búsqueda HTTP POST.
  • request.client: la dirección IP del cliente determinada, de estar presente, por request.env.http_x_forwareded_for o en caso contrario por request.env.remote_addr. Mientras esto puede ser útil, no es muy confiable, debido a que http_x_forwareded_for puede ser falsificado.
  • request.body: una secuencia de archivos de sólo lectura que contiene el cuerpo de la solicitud HTTP. Esto es analizado (parsed) de manera automática para obtener el request.post_vars y luego es rebobinado (rewinded). Puede ser leído con request.body.read().
  • request.ajax es True si la función es llamada por medio de una solicitud Ajax.
  • request.cid es el id del componente que generó la solicitud Ajax (si hay alguna). Puede leer más sobre componentes en el capítulo 13.
  • request.wsgi un gancho que permite hacer llamados a aplicaciones WSGI de terceros desde acciones internas.

Como un ejemplo, la siguiente llamada en un sistema típico:

http://127.0.0.1:8000/examples/default/status/x/y/z?p=1&q=2

da como resultado la siguiente tabla:

variable value
request.application examples
request.controller default
request.function index
request.extension html
request.view status
request.folder applications/examples/
request.args [‘x’, ‘y’, ‘z’]
request.vars <Storage {‘p’: 1, ‘q’: 2}>
request.get_vars <Storage {‘p’: 1, ‘q’: 2}>
request.post_vars <Storage {}>
request.cid None
request.wsgi hook
request.env.content_length 0
request.env.content_type  
request.env.http_accept text/xml,text/html;
request.env.http_accept_encoding gzip, deflate
request.env.http_accept_language en
request.env.http_cookie session_id_examples=127.0.0.1.119725
request.env.http_host 127.0.0.1:8000
request.env.http_max_forwards 10
request.env.http_referer http://web2py.com/
request.env.http_user_agent Mozilla/5.0
request.env.http_via 1.1 web2py.com
request.env.http_x_forwarded_for 76.224.34.5
request.env.http_x_forwarded_host web2py.com
request.env.http_x_forwarded_server 127.0.0.1
request.env.path_info /examples/simple_examples/status
request.env.query_string remote_addr:127.0.0.1
request.env.request_method GET
request.env.script_name  
request.env.server_name 127.0.0.1
request.env.server_port 8000
request.env.server_protocol HTTP/1.1
request.env.web2py_path /Users/mdipierro/web2py
request.env.we2bpy_version Version 1.83.1
request.env.web2py_runtime_gae (optional, defined only if GAE detected)
request.env.wsgi_errors <open file, mode ‘w’ at >
request.env.wsgi_input  
request.env.wsgi_multiprocess False
request.env.wsgi_multithread True
request.env.wsgi_run_once False
request.env.wsgi_url_scheme http
request.env.wsgi_version 10

Que variables de entorno son en realidad definidas depende del servidor web. Aquí estamos asumiendo el uso del servidor wsgi Rocket incluido. El grupo de variables no es muy distinto al usar el servidor web Apache.

Las variables request.env.http_* son analizadas desde el encabezado de la solicitud HTTP.

Las variables request.env.web2py_* no son analizadas desde el entorno del servidor web, pero son creadas por web2py en caso de que sus aplicaciones necesiten saber acerca de la ubicación y versión de web2py, y de si está corriendo sobre el motor de aplicaciones Google (debido a que optimización adicional puede ser necesaria). También note las variables request.env.wsgi_*. Éstas son específicas al adaptador wsgi.

response

response es otra instancia de la clase Storage. Contiene lo siguiente:

response.body: un objeto StringIO en el cual web2py escribe el cuerpo de la página sacada. NUNCA CAMBIE ESTA VARIABLE.

response.cookies: similar a request.cookies, pero mientras la última contiene las cookies enviadas desde el cliente al servidor, ésta contiene las cookies enviadas por el servidor al cliente. La cookie de sesión es manejada automáticamente.

response.download(request, db): un método usado para implementar la función del controlador que permite la descarga de archivos cargados.

response.files: una lista de CSS y JS requeridos por la página. Ellos serán automáticamente enlazados en el encabezado del “layout.html” estándar. Para incluir un nuevo archivo CSS o JS, sólo agréguelo a la lista. Se ocupa de los duplicados. El orden es significativo.

response.flash: paramétro opcional que puede ser incluidos en las vistas. Normalmente usados para notificar al usuario acerca de algo que haya ocurrido.

response.headers: un dict (diccionario) para los encabezados de respuestas HTTP.

response.menu: parámetro opcional que puede ser incluido en las vistas, normalmente usado para pasar un árbol de menú de navegación a la vista. Puede ser renderizada por el auxiliare MENU.

response.meta: un objeto de almacenaje (como un diccionario) que contiene información de almacenaje propia como response.meta.author,** response.meta.description**, y/o **response.meta.keywords*. El contenido de la variable propia es automáticamente colocado en la etiqueta META necesaria por el código en “web2py_ajax.html” que es incluido por defecto en “views/layout.html”

response.postprocessing: esta es una lista de funciones, por defecto vacía. Éstas funciones son usadas para filtrar el objeto de respuesta a la salida de una acción, antes de que la salida sea renderizada por la vista. Puede ser usada para implementar soporte para plantillas de otros idiomas.

response.render(views, vars): un método usado para llamar explícitamente a la vista dentro del controlador. view es un parámetro opcional que es el nombre del archivo de la vista, vars es un diccionario de valores nombrados pasados a la vista.

response.session_file: secuencia de archivo que contiene la sesión.

response.session_file_name: nombre del archivo donde la sesión será guardada.

response.session_id: el id de la sesión actual. Es determinado automáticamente. NUNCA CAMBIE ÉSTA VARIABLE.

response.session_id_name: el nombre de la cookie de sesión para la aplicación. NUNCA CAMBIE ÉSTA VARIABLE.

response.status: El entero del código de estatus HTTP que será pasado a la respuesta. Por defecto es 200 (OK).

response.stream(file, chunk_size): cuando es devuelto por un controlador, web2py envía de manera continua el contenido del archivo de regreso al cliente en bloques del tamaño chunk_size.

response.subtitle: parámetro opcional que puede ser incluido en las vistas. Debe contener el subtítulo de la página.

response.title: parámetro opcional que puede ser incluido en las vistas. Debe contener el título de la página y debe ser renderizada por por el título HTML TAG en el encabezado.

response._vars: esta variable es accesible sólo en la vista, no la acción. Contiene el valor retornado por la acción a la vista.

response.view: el nombre de la plantilla de vista que debe renderizar la página. Por defecto es:

1
     "%s/%s.%s" % (request.controller, request.function, request.extension)

o, si ese archivo no puede ser localizado:

1
     "generic.%s" % (request.extension)

Cambie el valor de ésta variable para modificar el archivo de la vista asociado con una acción particular.

response.xmlrpc(request, methods): cuando es retornado por un controlador, esta función expone los métodos vía XML-RPC(46). Ésta función es obsoleta ya que un mejor mecanismo está disponible sobre el cual se habla en el Capítulo 9.

response.write(text): un método para escribir texto dentro del cuerpo de la página de salida.

response.js puede contener código JavaScript. Éste código será ejecutado si y sólo si la respuesta es recibida por un componente web2py como se discute en el Capítulo 13.

Ya que response es un objeto gluon.storage.Storage puede ser usado para almacenar otros atributos que pueda querer pasar a la vista. Aún cuando no hay restricción, nuestra recomendación es almacenar sólo variables que serán renderizadas por todas las páginas en el layout general (“layout.html”). De todos modos, nosotros le sugerimos fuertemente que se limite a las variables listadas aquí:

     response.title
     response.subtitle
     response.flash
     response.menu
     response.meta.author
     response.meta.description
     response.meta.keywords
     response.meta.*

porque así será más fácil para usted reemplazar el archivo “layout.html” estándar que viene con web2py por otro archivo de disposición, uno que use el mismo grupo de variables. Viejas versiones de web2py usan response.author en vez de response.meta.author y similares para los otros atributos propios.

session

session es otra instancia de la clase Storage. Todo lo que sea almacenado en session, por ejemplo:

1
     session.myvariable = "hello"

puede ser recuperado más adelante:

1
     a = session.myvariable

siempre y cuando el código sea ejecutado dentro de la misma sesión por el mismo usuario (siempre que el usuario no haya borrado los cookies de sesión y que la sesión no haya expirado). Debido a que session es un objeto Storage, tratar de accesar a un atributo/clave que no ha sido establecida no levanta una excepción; en cambio retorna None.

El objeto de la sesión tiene dos métodos importantes. Uno es forget:

1
     session.forget()

Le dice a web2py que no guarde la sesión. Ésto debe ser usado en los controladores donde las acciones son llamadas a menudo y que no necesitan llevar registro de la actividad del usuario.

El otro metodo es connect:

1
     session.connect(request, response, db, masterapp=None)

Donde db es el nombre de una conexión abierta a una base de datos (como esté retornada por DAL). Le dice a web2py que usted quiere guardad las sesiones en la base de datos y no en el sistema de archivos. web2py crea una tabla:

1
2
3
4
5
6
7
     db.define_table('web2py_session',
                           Field('locked', 'boolean', default=False),
                           Field('client_ip'),
                           Field('created_datetime', 'datetime', default=now),
                           Field('modified_datetime', 'datetime'),
                           Field('unique_key'),
                           Field('session_data', 'text'))

y almacena las sesiones cPickled en el campo session_data.

La opción masterapp=None, por defecto, le dice a web2py que trate de recuperar una sesión existente para la aplicación con el nombre request.application, en la aplicación que está corriendo.

Si usted quiere que dos o más aplicaciones compartan sesiones, defina masterapp con el nombre de la aplicación maestra.

Usted puede verificar el estado de su aplicación en cualquier momento imprimiendo las variables de sistema request, session y response. Una manera de hacer esto es creando una acción dedicada:

1
2
     def status():
              return dict(request=request, session=session, response=response)

cache

cache es un objeto global que tambiés está disponible en el entorno de ejecución de web2py. Tiene dos atributos:

  • cache.ram: el cache de la aplicación en la memoria principal.
  • cache.disk: el cache de la aplicación en disco.

cache es llamable, esto permite que sea usado como un decorador para acciones de almacenamiento en cache y vistas.

El siguiente ejemplo almacena en cache de la memoria RAM la función time.ctime():

1
2
3
4
     def cache_in_ram():
              import time
              t = cache.ram('time', lambda: time.ctime(), time_expire=5)
              return dict(time=t, link=A('click me', _href=request.url))

La salida de lambda: time.ctime() es almacenada en RAM por 5 segundos. La cadena ‘time‘ es usada como clave de cache.

El siguiente ejemplo almacena en el cache del disco la función time.ctime():

1
2
3
4
     def cache_on_disk():
              import time
              t = cache.disk('time', lambda: time.ctime(), time_expire=5)
              return dict(time=t, link=A('click me', _href=request.url))

La salida de lambda: time.ctime() es almacenada en el cache del disco (usando el módulo de estantería) y luego en la RAM por 5 segundos. web2py busca primero en la RAM y si no está ahi, busca en el disco. Si no está ni en la RAM ni en el disco, lambda: time.ctime() es ejecutado y el cache es actualizado. Ésta técnica es útil en un entorno multiproceso. Los dos tiempos no tienen que ser el mismo.

El siguiente ejemplo está almacenando en el cache de la RAM la salida de la función del controlador (pero no la vista):

1
2
3
4
5
     @cache(request.env.path_info, time_expire=5, cache_model=cache.ram)
     def cache_controller_in_ram():
              import time
              t = time.ctime()
              return dict(time=t, link=A('click me', _href=request.url))

El diccionario retornado por cache_controller_in_ram es almacenado en el cache de la RAM por 5 segundo. Note que el resultado de un select de base de datos no puede ser almacenado en cache sin antes ser serializado. Una mejor manera es almacenar en cache la base de datos directamente usando el método select, argumento cache.

El siguiente ejemplo esta haciendo cache de la salida de la función del controlador en disco (pero no la vista):

1
2
3
4
5
6
     @cache(request.env.path_info, time_expire=5, cache_model=cache.disk)
     def cache_controller_on_disk():
              import time
              t = time.ctime()
              return dict(time=t, link=A('click to reload',
                                        _href=request.url))

El diccionario retornado por cache_controller_on_disk es almacenado en cache del disco por 5 segundos. Recuerde que web2py no puede hacer cache de un diccionario que contiene objetos no seleccionables.

Es también posible hacer cache de la vista. El truco es renderizar la vista en la función del controlador, de manera que el controlador retorne una cadena. Esto es hecho retornando response.render(d) donde d es el diccionario que queríamos pasar a la vista:

El tiempo de expiración (time_expire) puede ser establecido en 0 para forzar un refrescamiento de cache y se establecido a None para que el contenido no expire jamás.

Usted puede limpiar una o más variables del cache con:

1
     cache.ram.clear(regex='...')

Donde regex es una expresión regular que busca todas las claves que usted quiere que se eliminen del cache.

El siguiente ejemplo hace cache de la salida de la función del controlador en la RAM (incluyendo la vista renderizada):

1
2
3
4
5
6
     @cache(request.env.path_info, time_expire=5, cache_model=cache.ram)
     def cache_controller_and_view():
              import time
              t = time.ctime()
              d = dict(time=t, link=A('click to reload', _href=request.url))
              return response.render(d)

response.render(d) devuelve la vista renderizada como una cadena que es almacenada en cache por 5 segundos. Esta es la mejor y más rápida manera de hacer cache.

También es posible definir otros mecanismos para hacer cache como memcache. Memcache está disponible por medio de gluon.contrib.memcache y es discutido en detalle en el Capítulo 11.

URL

La función URL es una de las funciones más importantes en web2py. Genera las rutas de URL internas para las acciones y los archivos estáticos.

He aquí un ejemplo:

1
     URL('f')

Es mepeado en

     /[application]/[controller]/f

Note que la salida de la función URL depende del nombre de la aplicación actual, el controlador que hace el llamado y otros parámetros. web2py soporta mapeo y mapeo inverso de URL. El mapeo de URL permite redefinir el formato de URLs externas. Si usted usa la función URL para generar todos los URLs internos, entonces las adiciones o cambios a los mapeos de URL evitarán enlaces rotos dentro de la aplicación web2py.

Usted puede pasar parámetros adicionales a la función URL, por ejemplo, términos extras en la ruta URL (args) y variables de busqueda URL (vars):

1
     URL('f', args=['x', 'y'], vars=dict(z='t'))

Es mapeada en:

     /[application]/[controller]/f/x/y?z=t

Los atributos args de manera automática son analizados, decodificados, y finalmente almacenados en request.args por web2py. De manera similar, las variables vars son analizadas, decodificadas, y luego guardadas en request.vars.args y vars provee el mecanismo básico mediante el cual web2py intercambia información con el navegador del cliente.

Si los argumentos args contienen sólo un elemento, no hay necesidad de pasarlo a una lista.

Usted también puede usar la función URL para generar URLs a acciones en otros controladores y otras aplicaciones:

1
     URL('a', 'c', 'f', args=['x', 'y'], vars=dict(z='t'))

es mapeada en:

/a/c/f/x/y?z=t

También es posible especificar una aplicación, controlador o función usando argumentos nombrados:

1
     URL(a='a', c='c', f='f')

Si no hay nombre de la aplicación “a”, se asume la actual aplicación.

1
     URL('c', 'f')

Si no hay nombre del controlador, se asume el actual.

1
     URL('f')

En vez de pasar el nombre de la función del controlador también es posible pasar la función en sí

1
     URL(f)

Por las razones antes mencionadas, usted siempre debe usar la función URL para generar los URLs de los archivos estáticos para sus aplicaciones. Los archivos estáticos están almacenados en la subcarpeta static de la aplicación (allí es donde van cuando son cargados usando la interfaz administrativa). Web2py proporciona un controlador ‘estático’ virtual cuyo trabajo es recuperar archivos de la subcarpeta static, determinar su tipo de contenido, y hacer stream del archivo hacia el cliente. EL siguiente ejemplo genera la URL para el archivo estático “image.png”:

1
     URL('static', 'image.png')

Es mapeada a:

     /[application]/static/image.png

Usted no necesita codificar/escapar los argumentos args y vars; esto es hecho de manera automática.

Por defecto, la extensión correspondiente a la solicitud actual (request.extension) es agregada a la función, a menos que request.extension sea html, el caso predeterminado. Ésto puede ser cambiado al incluir de manera explícita una extensión como parte del nombre de la función URL(f=’name.ext’) o con el argumento de extensión:

1
     URL(..., extension='css')

La extensión actual puede ser explícitamente suprimida:

1
     URL(..., extension=False)

HTTP y redirect

web2py define solo una nueva excepción llamada HTTP. Ésta excepción puede ser evocada en cualquier lugar de un modelo, controlado, o una vista mediante el comando:

1
     raise HTTP(400, "my message")

Causa que el flujo de control salte lejos del código del usuario, regrese a web2py, y devuelva una respuesta HTTP como:

     HTTP/1.1 400 BAD REQUEST
     Date: Sat, 05 Jul 2008 19:36:22 GMT
     Server: Rocket WSGI Server
     Content-Type: text/html
     Via: 1.1 127.0.0.1:8000
     Connection: close
     Transfer-Encoding: chunked

     my message

El primer argumento de HTTP es el código de estatus HTTP. El segundo argumento es la cadena que será retornada como el cuerpo de la respuesta. Argumentos opcionalmente nombrados adicionales son usados para construir el encabezado de la respuesta HTTP. Por ejemplo:

1
     raise HTTP(400, 'my message', test='hello')

Genera:

     HTTP/1.1 400 BAD REQUEST
     Date: Sat, 05 Jul 2008 19:36:22 GMT
     Server: Rocket WSGI Server
     Content-Type: text/html
     Via: 1.1 127.0.0.1:8000
     Connection: close
     Transfer-Encoding: chunked
     test: hello

     my message

Si usted no quiere confirmar la transacción de base de datos abierta, retroceda antes de levantar la excepción.

Cualquier excepción distinta de HTTP cause que web2py eche para atrás cualquier transacción de base de datos abierta, registre el error, expida un ticket al visitante, y devuelva una página de error estándar.

Ésto significa que solo HTTP puede ser usada para control de flujo entre páginas. Otras excepciones deben ser capturadas por la aplicación, de lo contrario web2py producirá un ticket.

El comando:

     redirect('http://www.web2py.com'

Es simplemente un atajo a:

1
2
3
     raise HTTP(303,
                     'You are being redirected <a href="%s">here</a>' % location,
                     Location='http://www.web2py.com')

Los argumentos con nombre del método inicializador HTTP son traducidos a las directivas de encabezado HTTP, en este caso la locación del objetivo de la redirección, redirect, toma un segundo argumento opcional, el cual es el código de estatus HTTP para la redirección (303 por defecto). Cambie éste número a 307 para una redirección temporal o a 301 para una redirección permanente.

La manera más común para redirección de usuario es redireccionar a otras páginas en la misma aplicación y (opcionalmente) pasar parámetros:

1
     redirect(URL('index',args=(1,2,3),vars=dict(a='b')))

T e Internacionalización

El objeto T es el traductor de idioma. Constituye una sola instancia global de la clase gluon.language.translator de web2py. Todas las cadenas constantes (y sólo cadenas constantes) deben ser marcadas con T, por ejemplo:

1
     a = T("hello world")

Cadenas que son marcadas con T son identificadas por web2py como que necesitan traducción de idioma y serán traducidas cuando el código (en el modelo, controlador o vista) sea ejecutado. Si la cadena a traducirse no es una constante sino una variable, sera agregada al archivo de traducción en tiempo de ejecución (excepto en GAE) para ser traducidas luego.

El objeto T también puede contener variables interpoladas, por ejemplo:

1
     a = T("hello %(name)s", dict(name="Massimo"))

La primera cadena es traducida acorde con el archivo del idioma deseado y el nombre name de la variable es reemplazado independiente del idioma.

Concatenar traducción de cadenas no es una buena idea; es por ello que web2py no le permite hacerlo:

1
     T("blah ") + name + T(" blah")   # invalid!

Pero si permite:

1
     T("blah %(name)s blah", dict(name='Tim'))

O la sintaxis alternativa

1
     T("blah %(name)s blah") % dict(name='Tim')

En ambos casos la traducción ocurre antes de que el nombre de la variable sea sustituido en el la ranura “%(name)s”. Lo siguiente, en cambio, NO DEBE SER USADO:

1
     T("blah %(name)s blah" % dict(name='Tim'))

porque la traducción ocurriría después de la sustitución.

El idioma requerido es determinado por el campo “Accept-Language” en el encabezado HTTP, pero ésta selección se puede sobreescribir programáticamente solicitando un archivo en específico, por ejemplo:

1
     T.force('it-it')

el cual lee el archivo de idioma “languages/it-it.py”. Los archivos de idioma pueden ser creados y editados por medio de la interfaz administrativa.

Normalmente la traducción de cadenas es evaluada perezosamente cuando la vista es renderizada; por lo tanto el método traductor force no debería ser llamado dentro de una función.

Es posible deshabilitar la evaluación perezosa via

1
     T.lazy = False

De ésta manera las cadenas son traducidas inmediatamente por el operador T basado en el actual idioma aceptado o forzado.

Un problema común es el siguiente. La aplicación original está en inglés. Supongamos que hay un archivo de traducción (por ejemplo italiano, “it-it.py”) y el cliente HTTP declara que acepta tanto inglés (en) como italiano (it-it) en ese orden. La siguiente situación no deseada ocurre: web2py no sabe que el idioma original es inglés (en). Por lo tanto prefiere traducir todo al italiano (it-it) porque sólo encontró el archivo de traducción italiano. Si no hubiese encontrado el archivo “it-it.py”, hubiese utilizado las cadenas de idioma por defecto (inglés).

Hay dos soluciones para éste problema: crear un idioma de traducción para inglés, lo que sería redundante e innecesario, o mejor, decirle a web2py que idioma debería usar las cadenas de idioma por defecto (las cadenas codificadas en la aplicación). Ésto puede hacerse con:

1
     T.set_current_language('en', 'en-en')

Guarda en T.current_languages una lista de los idiomas que no requieren traducción y forza una recarga de todos los archivos de traducción.

Note que “it” y “it-it” son idiomas diferentes desde el punto de vista de web2py. Para soportar ambos, uno necesitaría dos archivos de traducción, siempre en minúscula. Los mismo es verdad para otros idiomas.

El idioma aceptado actualmente es almacenado en

1
     T.accepted_language

Tome en cuenta que T(...) no solo traduzca cadenas sino que también traducir variables:

     >>> a="test"
     >>> print T(a)

In this case the world “test” in translated but, if not found and if the filesystem is writable, it will add it to the list of words to be translated in the language file.

Cookies

web2py usa los módulos de cookies de Python para el manejo de las cookies.

Los cookies del navegador están en request.cookies y las cookies enviadas por el servidor están en response.cookies.

Usted puede configurar una cookie de la siguiente manera:

1
2
3
     response.cookies['mycookie'] = 'somevalue'
     response.cookies['mycookie']['expires'] = 24 * 3600
     response.cookies['mycookie']['path'] = '/'

La segunda linea le dice al navegador que mantenga la cookie por 24 horas. La tercera línea le dice al navegador que envíe la cookie de nuevo a cualquier aplicación (ruta URL) en el dominio actual.

La cookie puede hacerse segura con:

1
     response.cookies['mycookie']['secure'] = True

Una cookie segura sólo es enviada de regreso por HTTPS y no por HTTP.

La cookie puede ser recuperada con:

     if request.cookies.has_key('mycookie'):
     value = request.cookies['mycookie'].value

A menos que las sesiones sean deshabilitadas, web2py, de manera transparente, configura la siguiente cookie y la usa para manejar sesiones:

1
2
     response.cookies[response.session_id_name] = response.session_id
     response.cookies[response.session_id_name]['path'] = "/"

Application init

Al implementar web2py usted querrá configurar una aplicación por defecto, por ejemplo, la aplicación que inicia cuando hay una ruta vacía en el URL, como en:

http://127.0.0.1:8000

Por defecto, cuando se enfrenta a una ruta vacía, web2py busca una aplicación llamada init. Si no hay una aplicación init busca una aplicación llamada welcome.

El nombre de la aplicación por defecto puede ser cambiado de init a otro nombre definiendo default_application en routes.py:

1
     default_application = "myapp"

Nota: default_application apareció por primera vez en web2py versión 1.83.

He aquí cuatro maneras de configurar la aplicación por defecto:

  • Llame a su aplicación principal “init”.
  • Establezca default_application con el nombre de su aplicación en routes.py.
  • Haga un enlace simbólico de “applications/init” a la carpeta de su aplicación.
  • Use reescritura de URL como se discute en la siguiente sección.

Reescritura URL

web2py tiene la habilidad de reescribir la ruta URL de las solicitudes entrantes antes del llamado de la acción del controlador (mapeo URL) y, de manera inversa, web2py puede reescribir la ruta URL generada por la función URL (mapeo reverso de URL). Una razón para hacer esto es para el manejo de URLs antiguas, otra razón es para simplificar las rutas haciendolas más cortas.

Para usar ésta característica, cree un nuevo archivo en la carpeta “web2py” llamada “routes.py” y defina dos listas (o tuplas) de 2-tuplas routes_in y routes_out. Cada tupla contiene dos elementos: el patrón a ser reemplazado y la cadena que lo reemplaza. Por ejemplo:

1
2
3
4
5
6
     routes_in = (
       ('/testme', '/examples/default/index'),
     )
     routes_out = (
       ('/examples/default/index', '/testme'),
     )

Con éstas rutas, el URL:

http://127.0.0.1:8000/testme

es mapeado a:

http://127.0.0.1:8000/examples/default/index

Para el visitante, todos los enlaces al URL de la página se ven como /testme. Los patrones tienen la misma sintaxis como las expresiones regulares Python. Por ejemplo:

1
     ('.*\.php', '/init/default/index'),

mapea todos los URLs terminando con “.php” a la página de inicio.

Algunas veces usted quiere deshacerse del prefijo de la aplicación en el URL porque usted planea exponer sólo una aplicación. Esto puede ser logrado con:

1
2
3
4
5
6
     routes_in = (
       ('/(?P<any>.*)', '/init/\g<any>'),
     )
     routes_out = (
       ('/init/(?P<any>.*)', '/\g<any>'),
     )

También hay una sintaxis alternativa que puede ser mezclada con la notación de la expresión regular presentada. Consiste en usar name en vez de (?P<name>[w_]+) o g<name>. Por ejemplo:

1
2
3
4
5
6
     routes_in = (
       ('/$c/$f', '/init/$c/$f'),
     )
     routes_out = (
       ('/init/$c/$f', '/$c/$f'),
     )

También eliminaría el prefijo de la aplicación “/example” de todas las URLs.

Usando la notación $, usted puede mapear automáticamente routes_in en routes_out, siempre que no use ningun expresión regular. Por ejemplo:

1
2
3
4
5
     routes_in = (
       ('/$c/$f', '/init/$c/$f'),
     )

     routes_out = [(x, y) for (y, x) in routes_in]

Si hay múltiples rutas, se ejecutará la primera en coincidir con el URL. No ningún patrón coincide, la ruta se deja sin cambios.

Usted puede usar $anything para hacer coincidir todo hasta el final de la línea.

He aquí un simple “routes.py” que se hace cargo de las solicitudes favicon y robots:

1
2
3
4
5
     routes_in = (
       ('/favicon.ico', '/examples/static/favicon.ico'),
       ('/robots.txt', '/examples/static/robots.txt'),
     )
     routes_out = ()

Aquí hay un ejemplo más complejo que expone una única aplicación “myapp” sin prefujos innecesarios pero también exponen admin, appadmin y static:

1
2
3
4
5
6
7
8
     routes_in = (
       ('/admin/$anything', '/admin/$anything'),
       ('/static/$anything', '/myapp/static/$anything'),
       ('/appadmin/$anything', '/myapp/appadmin/$anything'),
       ('/favicon.ico', '/myapp/static/favicon.ico'),
       ('/robots.txt', '/myapp/static/robots.txt'),
     )
     routes_out = [(x, y) for (y, x) in routes_in[:-2]]

La sintaxis general para las rutas es más compleja que los ejemplos simples que hemos vistos hasta ahora. He aquí un ejemplo más general y representativo:

1
2
3
4
     routes_in = (
      ('140\.191\.\d+\.\d+:https://www.web2py.com:POST /(?P<any>.*)\.php',
       '/test/default/index?vars=\g<any>'),
     )

Mapea las solicitudes https POST al host www.web2py.com desde una IP remota que coincide con la expresión regular

     140\.191\.\d+\.\d+

solicitando una página que coincida con la expresión regular

     /(?P<any>.*)\.php!

en

     /test/default/index?vars=\g<any>

donde <any> es reemplazado por la expresión regular coincidente.

La sintaxis general es

     [remote address]:[protocol]://[host]:[method] [path]

Toda la expresión se corresponde como una expresión regular, así que “.” siempre debe ser escapado y cualquier subexpresión que coincida puede ser capturada usando (?P<...>...) según la sintaxis regex Python.

Esto permite reenrutar las solicitudes basadas en la dirección IP o dominio del cliente, en función al tipo de solicitud, el método y la ruta. También permite asignar distintos hosts virtuales a diferentes aplicaciones. Cualquier subexpresión coincidente puede ser usada para construir la URL de destino y, eventualmente, pasarse como variable GET.

Todos los servidores web más importantes, tales como Apache y lighttpd, también tiene la habilidad de reescribir URLs. En un entorno de producción ésta puede ser una opción en vez de routes.py. Sin importar lo que decida hacer, le recomendamos fuertemente que no haga hardcode de las URLs internas en sus aplicaciones sino que haga uso de las funciones URL para generarlas. Esto hará que su aplicación sea más portable en caso de que las rutas deban cambiar.

Reescritura de las URL específicas de la aplicación

Una aplicación puede establecer sus propias rutas en el archivo específico de aplicaciones routes.py ubicado en la carpeta base de la aplicación. Esto es habilitado configurando routes_app en el routes.py base para determinar desde la URL entrante el nombre de la aplicación a seleccionar. Cuando ésto sucede, el routes.py específico de la aplicación es usado en lugar del routes.py base.

El formato de routes_app es idéntico al de routes_in, excepto porque el patrón de reemplazo es simplemente el nombre de la aplicación. Si la aplicación de routes_app a la URL entrante no resulta en el nombre de una aplicación, o si el routes.py específico de la aplicación no se encuentra, el routes.py es usado como de costumbre.

Nota: routes_app apareció por primera vez en web2py version 1.83.

Aplicación, Controlador y Función por defecto

El nombre de la aplicación, controlador y función por defecto puede ser cambiado desde init, default, e index respectivamente a otro nombre configurando el valor apropiado en routes.py:

1
2
3
     default_application = "myapp"
     default_controller = "admin"
     default_function = "start"

Nota: Éstos ítems aparecieron por primera vez en web2py versión 1.83.

Rutas en Error

También puede usar “routes.py” para redireccionar al visitante hacia acciones especiales en caso de que haya un error en su servidor. Puede especificar ésta asignación de manera global, para cada aplicación, para cada código de error, para cada aplicación y código de error. He aquí un ejemplo:

1
2
3
4
5
6
     routes_onerror = [
       ('init/400', '/init/default/login'),
       ('init/*', '/init/static/fail.html'),
       ('*/404', '/init/static/cantfind.html'),
       ('*/*', '/init/error/index')
     ]

Para cada tupla la primera cadena se compara con “[appname]/[error_code]”. Si coincide, el usuario es redirigido a la URL en la segunda cadena de la tupla coincidente. En caso de que se haya producido un ticket, el mismo es pasado a la nueva URL como una variable GET llamada ticket.

Errores no coincidentes muestran una página de error predeterminada. Ésta página de error por defecto también se puede personalizar en:

1
2
3
4
     error_message = '<html><body><h1>Invalid request</h1></body></html>'
     error_message_ticket = '''<html><body><h1>Internal error</h1>
               Ticket issued: <a href="/admin/default/ticket/%(ticket)s"
               target="_blank">%(ticket)s</a></body></html>'''

La primera variable contiene el mensaje de error cuando se solicita una aplicación inválida. La segunda variable contiene el mensaje de error cuando un ticket es producido.

Cron

El cron de web2py proporciona a la aplicaciones la habilidad de ejecutar tareas en horas preestablecidas, de manera independiente de la plataforma.

Para cada aplicación la funcionalidad del cron es definida por un archivo crontab “app/cron/crontab”, siguiendo la sintaxis definida en la referencia.(45) (con algunas extensiones específicas de web2py).

Esto significa que cada aplicación puede tener una configuración cron propia y cuya configuración puede ser cambiada desde dentro de web2py sin afectar el sistema operativo del host.

He aquí un ejemplo:

Las últimas dos líneas en este ejemplo usan extensiones a la sintaxis regular de cron para proporcionar funcionalidad adicional web2py.

El cron de web2py tiene un poco de sintaxis extra para dar soporte a aplicaciones específicas de web2py.

Si a los script/tareas se les agrega un asterisco al comienzo (*) y terminan con “.py”, serán ejecutados en el entorno de web2py. Ésto significa que usted tendrá todos los controladores y modelos a su disposición. Si usa dos asteriscos (**), los modelos (MODELs) no serán ejecutados. Ésta es la manera recomendada para hacer llamados ya que tiene menos gastos de recursos de sistema y evita potenciales problemas de bloqueo.

Note que los scripts/funciones ejecutados en el entorno web2py requieren un db.commit() manual al final de la función ya que de lo contrario la operación se revertirá.

web2py no genera tickets o registro significativo en modo shell (en el cual cron es ejecutado). Asegúrese de que su código web2py corrá sin errores antes de configurar una tarea cron, ya que lo más probable es que no los pueda ver cuando corra desde el cron.

Además, sea cuidadoso de como usa los modelos. Aún cuando la ejecución ocurre en un proceso separado, bloques de base de datos deben ser tomados en consideración para evitar que las páginas esperen a las tareas cron que puedan estar bloqueando la base de datos. Use la sintaxis ** si no quiere usar la base de datos en su tarea cron.

También puede hacer un llamado a una función del controlador. No hay necesidad de especificar una ruta. El controlador y la función serán la que la aplicación invoque. Tome especial cuidado acerca de las advertencias listadas previamente. Ejemplo:

1
     @reboot  *  *  *  *  root *mycontroller/myfunction

Dependiendo de como invoque web2py, hay cuatro modelos de operación para el cron de web2py.

  • Cron suave (Soft cron): disponible en todos los modos de ejecución.
  • Cron duro (Hard cron): disponible si se usa el servidor web incorporado (ya sea directamente o por medio del mod_proxy Apache)
  • Cron externo (External cron): disponible si usted tiene acceso al sistema cron propio del sistema.
  • No cron.

En caso de usar el servidor web incluido, lo predeterminado es el cron duro; en todos los demás casos se usa cron suave por defecto.

El cron suave es el predeterminado si usted está usando CGI, FASTCGI o WSGI. Sus tareas serán ejecutadas en el primer llamado (cargado de página) a web2py después del tiempo especificado por crontab (pero después de procesar la página, para que no haya retardo visible para el usuario). Obviamente, hay cierta incertidumbre acerca de cuando exactamente será ejecutada la tarea dependiendo del tráfico que recibe el sitio. También se puede dar que la tarea cron sea interrumpida si el servidor web tiene un tiempo máximo de carga configurado. Si éstas limitación no son aceptables, vea “cron externo”. El cron suave es un último recurso razonable, pero si su servidor web le permite otros métodos cron, deben ser usados en detrimento del cron suave.

El cron duro es la opción predeterminada si usted está usando el servidor web incorporado (ya sea directamente o mediante mod_proxy Apache). Hard cron es ejecutado en una tarea paralela así que, a diferencia del cron suave, no hay limitaciones en cuanto al tiempo de ejecución o precisión en el mismo.

El cron externo nunca es la opción predeterminada, pero requiere que usted tenga acceso al cron del sistema. Corre en un proceso paralelo, así que ninguna de las limitaciones del cron suave aplican aquí. Ésta es la manera recomendada de usar el cron al trabajar con WSGI o FASTCGI.

Ejemplo de la línea a agregar en el crontab del sistema, (usualmente /etc/crontab):

1
     0-59/1 * * * * web2py cd /var/www/web2py/ && python web2py.py -C -D 1 >> /tmp/cron.output 2>&1

Si usted está usando el cron externo, asegúrese de agregar el parámetro de línea de comandos -N a su script de inicio de web2py o configúrelo de manera que no haya choque entre varios tipos de cron. En caso de que no necesite ninguna funcionalidad del cron dentro de algún proceso particular, puede usar el parámetro -N para deshabilitarlo. Note que ésto también puede deshabilitar algunas tareas de mantenimiento (como la limpieza automática de los directorios de sesión). El uso más común de esta función:

  • Usted ya tiene un cron externo configurado activado desde el sistema (lo más común con configuraciones WSGI)
  • Si usted desea depurar su aplicación sin que cron interfiera con las acciones ni con la salida.

Procesos en segundo plano y colas de Tareas

Aún cuando cron es útil para ejecutar tareas en intervalos de tiempo regulares, no es siempre la mejor opción para ejecutar una tarea en segundo plano. Para éste fin web2py proporciona la habilidad de correr cualquier script python como si estuviese dentro de un controlador:

1
     python web2py.py -S app -M -N -R applications/app/private/myscript.py -A a b c

dónde -S app le dice a web2py que ejecute “myscript.py” como “app”, -M le dice a web2py que ejecute models, -N le dice a web2py que no ejecute cron, y -A a b c le pasa los argumentos de linea de comandos opcionales sys.args=[‘a’, ‘b’, ‘c’] a “myscript.py”.

Un típico caso de prueba consiste en procesar una cola.

Considere éste modelo:

1
2
3
4
5
     db.define_table('queue',
              Field('status'),
              Field('email'),
              Field('subject'),
              Field('message'))

y una aplicación que coloca en una cola los mensajes a ser enviados

1
2
3
4
     db.queue.insert(status='pending',
                          email='you@example.com',
                          subject='test',
                          message='test')

El script de procesamiento en segundo plano que envía los emails podría serialización

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
     # in file myscript.py
     import time
     while True:
              rows = db(db.queue.status=='pending').select()
              for row in rows:
                  if mail.send(to=row.email,
                      subject=row.subject,
                      message=row.message):
                      row.update_record(status='sent')
                  else:
                      row.update_record(status='failed')
                  db.commit()
              time.sleep(60) # check every minute

Note que el objeto mail es definido en el archivo db.py en la aplicación basada en base de datos, debido a que la opción -M es visible aquí. Puede ser necesario configurarlo dentro de db.py para que trabaje correctamente. Note también que es importante confirmar cualquier cambio lo más pronto posible para así no bloquear la base de datos para otros procesos concurrentes.

Éste tipo de procesos en segundo plano no deben ser ejecutados por medio de cron (excepto tal vez por cron al reinicio) porque usted debe estar seguro de que sólo una instancia esté siendo ejecutada al mismo tiempo. Con cron es posible que un proceso comience en la iteración cron 1, y no se complete al llegar a la iteración cron 2, por lo que cron lo comienza una y otra vez, obstruyendo el servidor de correo.

Módulos de Terceros

web2py está escrito en Python, por lo que puede importar y usar cualquier módulo Pythom, incluyendo aquellos módulos de terceros. Sólo necesita poder ubicarlos.

Los módulos pueden ser instalados en el directorio oficial de Python “site-packages” o en cualquier lugar donde su aplicación pueda localizarlo.

Los módulos en el directorio “site-packages” son, como su nombre sugiere, paquetes a nivel del sitio. Las aplicaciones que requieran de site-packages no son portables a menos que los módulos sean instalados aparte. La ventaja de tener los módulos en “site-packages” es que pueden ser compartidos por múltiples aplicaciones. Consideremos por ejemplo el paquete de ploteo llamado “matplotlib”. Usted puede instalarlo desde el shell usando el comando PEAK easy_install:

1
     easy_install py-matplotlib

y luego puede importarlo en cualquier modelo/controlador/vista con:

1
     import matplotlib

Las distribuciones binarias web2py de Windows tienen un site-packages en la carpeta de más alto nivel. La distribución binaria de MAC tienen una carpeta site-packages en la carpeta:

1
     web2py.app/Contents/Resources/site-packages

Usted también puede instalar paquetes de manera manual en la carpeta “modules” de la aplicación. La ventaja es que el módulo será copiado automáticamente y distribuido con la aplicación. Si la aplicación se llama “test”, puede importar “mymodule” con:

1
     mymodule = local_import(mymodule)

La función busca a mymodule en la carpeta de módulos locales de la aplicación y lo importa con el nombre en el lado izquierdo del igual.

Ésta función toma tres argumentos: name, reload y app. Cuando usted especifique reload=True, reimportará el módulo en cada solicitud; en caso contrario su proceso Python solo importará el módulo una vez. El valor por defecto es reload=False. app es el nombre de la aplicación de la cual se importará el módulo, por defecto es request.application.

La razón para que funciones así es que ya que un servidor puede ejecutar muchas instancias de web2py, usted no quiere agregar las rutas de sus módulos al sys.path ya que la búsqueda sería dependiente del orden.

Entorno de Ejecución

Aún cuando todo lo descrito aquí funciona bien, le recomendamos en cambio que construya su aplicación usando componentes, tal como es descrito en el capítulo 13.

Los archivos del controlador y modelo no son módulos Python en el aspecto de que no pueden ser importados usando la instrucción import de Python. La razón de ésto es que los modelos y controladores son diseñados para ser ejecutados en un entorno preparado que no ha sido poblado por objetos globales (request, response, session, cache y T) y funciones auxiliares web2py. Ésto es necesario debido a que Python es un lenguaje de ámbito (léxico) estático, mientras que el entorno de web2py es creado dinámicamente.

web2py proporciona la función exec_enviroment para permitirle el acceso a módulos y controladores directamente. exec_enviroment crea un entorno de ejecución web2py, carga el archivo en él y luego retorna un objeto de almacenaje (Storage) que contiene el entorno. El objeto de almacenaje también sirve como un mecanismo de definición de variables. Cualquier archivo Python diseñado para ser ejecutado en el entorno de ejecución puede ser cargado usado exec_enviroment. Los usos para exec_enviroment incluyen:

  • Accesar datos (modelos) de otras aplicaciones.
  • Accesar objetos globales de otros modelos y controladores.
  • Ejecutar funciones controladores de otros controladores.
  • Cargar librerías auxiliares en todo el sitio.

Éste ejemplo lee filas desde la tabla user en la aplicación cas:

1
2
3
     from gluon.shell import exec_environment
     cas = exec_environment('applications/cas/models/db.py')
     rows = cas.db().select(cas.db.user.ALL)

Otro ejemplo: suponga que tiene un controlador “other.py” que contiene:

1
2
     def some_action():
              return dict(remote_addr=request.env.remote_addr)

Vea aquí como puede llamar dicha acción desde otro controlador (o desde el shell de web2py):

1
2
3
     from gluon.shell import exec_environment
     other = exec_environment('applications/app/controllers/other.py', request=request)
     result = other.some_action()

En la línea 2, request=request es opcional. Tiene el efecto de pasar la solicitud actual al entorno de “other”. Sin éste argumento, el entorno contendría un nuevo y vacío (aparte de request.folder) objeto de solicitud. También es posible pasar una respuesta y un objeto de sesión a exec_enviroment. Sea cuidadoso al pasar objetos de solicitud, respuesta y sesión – modificaciones por la acción llamada o dependencias de codificación en la acción llamada puede llevar a efectos secundarios indeseados.

El llamado de función en la línea 3 no ejecuta la vista; simplemente devuelve el diccionario a menos que response.render sea llamado explícitamente por “some_action”.

Una advertencia final: no use exec_enviroment de manera inapropiada. Si usted quiere los resultados de acciones en otras aplicaciones, probablemente debería implementar un API XML-RPC (implementar un API XML-RPC con web2py es casi trivial). No use exec_enviroment como mecanismo de redirección; use el auxiliar redirect.

Cooperación

Hay muchas maneras en las que las aplicaciones pueden cooperar:

  • Las aplicaciones pueden conectar con la misma base de datos y por lo tanto compartir tablas. No es necesario que todas las tablas en la base de datos sean definidas por todas las aplicaciones, pero deben ser definidas por las aplicaciones que las usan. Todas las aplicaciones menos una que usen la misma tabla deben definirla con migrate=False.
  • Las aplicaciones pueden integrar componentes de otras acciones usando el auxiliar LOAD (descrito en el Capítulo 13).
  • Las aplicaciones pueden compartir sesiones.
  • Las aplicaciones pueden llamar a acciones de las otras remotamente por medio de XML-RPC.
  • Las aplicaciones pueden accesar a los archivos de las otras por medio del sistema de archivos (asumiendo que ellas comparten el mismo sistema de archivos)
  • Las aplicaciones pueden llamar a acciones de las otras localmente usando exec_enviroment como se explicó con anterioridad.
  • Las aplicaciones pueden importar módulos de las otras haciendo referencia con:
1
     local_import(...)
  • Las aplicaciones pueden importar cualquier módulo en la ruta de búsqueda PYTHONPATH, sys.path.

Una aplicación puede cargar la sesión de otra aplicación usando el comando:

1
     session.connect(request, response, masterapp='appname', db=db)

Aquí “appname” es el nombre de la aplicación maestra, la que establece el id de sesión inicial en el cookie. db es una conexión de base de datos a la base de datos que contiene la tabla de sesión (web2py_session). Todas las aplicaciones que comparten la misma sesión deben usar la misma base de datos para almacenaje de sesión.

Una aplicación puede cargar un módulo de otra usando

     othermodule = local_import('othermodule',app='otherapp')

     Si una función de módulo necesita accesar a uno de los objetos principales (request, response,  session, cache y T), los objectos deben ser pasados explícitamente a la función. No deje que el         módulo cree otra instancia de los objetos principales. De lo contrario la función no se comportará de la manera esperada.

WSGI

web2py y WSGI tienen una relación de amor y odio. Nuestra percepción es que WSGI fué desarrollado como un protocolo para conectar servidores web a aplicaciones web en un modo portable y lo usamos para ése propósito. web2py es en su núcleo una aplicación WSGI: gluon.main.wsgibase. Algunos desarrolladores han llevado a WSGI a sus límites como protocolo para comunicaciones middleware (software de conectividad) y el desarrollo de aplicaciones web como una cebolla con muchas capas (cada capa siendo un middleware WSGI desarrollado independientemente en framework completo). web2py no adopta esta estructura internamente. Ésto es porque sentimos que la funcionalidad principal de un framework (manejo de cookies, sesiones, errores, transacciones, envío) puede ser optimizado para velocidad y seguridad si son manejados por una única capa comprensiva.

Sin embargo web2py le permite usar aplicaciones y middleware WSGI de terceros de tres maneras (además de sus combinaciones):

  • Usted puede editar el archivo “wsgihandler.py” e incluir cualquier middleware WSGI de terceros.
  • Puede conectar middleware WSGI de terceros a cualquier acción específica en su aplicación.
  • Puede hacer llamados a aplicaciones WSGI de terceros desde sus acciones.

La única limitación es que usted no puede usar middleware de terceros para reemplazar las funciones del núcleo de web2py.

Middleware Externo

Considere el archivo “wsgibase.py”:

1
2
3
4
5
6
7
8
9
     #...
     LOGGING = False
     #...
     if LOGGING:
              application = gluon.main.appfactory(wsgiapp=gluon.main.wsgibase,
                                                  logfilename='httpserver.log',
                                                  profilerfilename=None)
     else:
              application = gluon.main.wsgibase

Cuando login se establece en True, gluon.main.wsgibase es arropado por la función middleware gluon.main.appfactory. Proporciona inicio de sesión al archivo “httpserver.log”. De una manera similar usted puede agregar cualquier middleware de terceros. Hacemos referencia a la documentación WSGI oficial para más detalles.

Middleware Interno

Dada cualquier acción en sus controladores (por ejemplo, index) y cualquier aplicación middleware de terceros (por ejemplo MyMiddleware) que convierte salidas a mayúsculas, usted puede usar un decorador web2py para aplicar la middleware a esa acción. He aquí un ejemplo:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
     class MyMiddleware:
              """converts output to upper case"""
              def __init__(self,app):
                  self.app = app
              def __call__(self,environ, start_response):
                  items = self.app(environ, start_response)
                  return [item.upper() for item in items]
     @request.wsgi.middleware(MyMiddleware)
     def index():
              return 'hello world'

No podemos prometer que todos los middleware de terceros funcionarán con éste mecanismo.

Llamando aplicaciones WSGI

Es fácil llamar aplicaciones WSGI desde una acción web2py. He aquí un ejemplo:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
     def test_wsgi_app(environ, start_response):
              """this is a test WSGI app"""
              status = '200 OK'
              response_headers = [('Content-type','text/plain'),
                                  ('Content-Length','13')]
              start_response(status, response_headers)
              return ['hello world!\n']
     def index():
              """a test action that call the previous app and escapes output"""
              items = test_wsgi_app(request.wsgi.environ,
                                    request.wsgi.start_response)
              for item in items:
                  response.write(item,escape=False)
              return response.body.getvalue()

En éste caso la acción index llama a test_wsgi_app y escapa el valor retornado antes de de retornarlo. Note que index no es en sí una aplicación WSGI y debe usar el API normal de web2py (tal como response.write para escribir en el socket).