Migra a Python 2.7

En esta página, se abordan las medidas necesarias para actualizar tu aplicación de Python al entorno de ejecución de Python 2.7. Después de seguir esta explicación, con tu aplicación podrás aprovechar las numerosas capacidades nuevas del entorno de ejecución de Python 2.7, que incluyen el multiprocesamiento, el motor de plantillas Jinja2, la carga y el acceso al código de bytes, y varias bibliotecas nuevas de terceros incluidas.

Requisitos previos y consideraciones

Para utilizar Python 2.7, una aplicación debe cumplir los siguientes requisitos:

  • Si la aplicación usa Django, debe utilizar la versión 1.2 o posterior. Consulta la documentación de Django para conocer los detalles de la actualización.
  • Si deseas usar solicitudes simultáneas, la aplicación debe utilizar los controladores de secuencia de comandos de la Interfaz de puerta de enlace del servidor web (WSGI), como se describe en Usar de WSGI.

Además de cumplir estos requisitos previos generales, debes usar versiones específicas de algunas funciones de App Engine y bibliotecas de terceros. Asegúrate de actualizar las versiones que incluyas e importes en tu aplicación, y prueba la aplicación exhaustivamente después de la actualización. La siguiente lista identifica los problemas de compatibilidad principales y sugiere otros recursos para resolverlos:

  • Rendimiento: el rendimiento de la aplicación puede cambiar después de actualizar a Python 2.7. Si experimentas una mayor latencia de respuesta, puedes aumentar la clase de instancia de frontend y habilitar solicitudes simultáneas. Las solicitudes simultáneas permiten que tu aplicación tenga un rendimiento más rápido con un menor costo de instancia en instancias más grandes. Para obtener más información, consulta Administración de solicitudes en instancias.
  • Django: debes usar Django 1.2 o una versión posterior con Python 2.7. Para obtener información sobre la actualización, consulta las Notas de la versión Django.
  • PyCrypto: Crypto.Util.randpool está obsoleto y es preferible usar Crypto.Random. Para obtener más información, consulta Qué hacer con RandomPool
  • webapp: Las plantillas de aplicaciones web están obsoletas en Python 2.7. En su lugar, puedes usar las plantillas de Django de forma directa, jinja2 o un motor de plantillas diferente de tu elección.
  • WebOb: Python 2.7 es compatible con WebOb versión 1.1. Esta versión no es totalmente compatible con la versión anterior (0.9). Si la aplicación utiliza WebOb, debes probarlo ampliamente para detectar cualquier error que resulte de la actualización.
  • zipimport: Python 2.7 no admite zipimport, pero Python 2.7 puede importar de forma nativa desde archivos zip.
  • simplejson: Python 2.7 no admite simplejson, pero sí incluye el módulo json equivalente y mucho más rápido de la biblioteca estándar.

Solicitudes concurrentes y WSGI

La característica de Python 2.7 que más afecta el diseño y el rendimiento de tu aplicación es su compatibilidad con aplicaciones multiproceso que pueden manejar solicitudes simultáneas. La capacidad de manejar solicitudes simultáneas da como resultado una utilización mejorada que puede optimizar significativamente el rendimiento de tu aplicación, en especial en el caso de aplicaciones que utilizan clases de instancia más altas que explotan múltiples núcleos de CPU.

Para habilitar el multiprocesamiento, las aplicaciones deben pasar del enfoque basado en la Interfaz de entrada común (CGI) de los entornos de ejecución de Python anteriores a un enfoque basado en la interfaz de puerta de enlace del servidor web (WSGI). Esto se debe a que las secuencias de comandos de CGI, que se diseñaron con el fin de manejar solicitudes en serie, dependen de las variables de entorno para acceder a los flujos de entrada y salida.

Si bien el modelo WSGI para el manejo de solicitudes brinda a las aplicaciones un acceso más directo a los flujos de entrada y salida (habilitando solicitudes simultáneas), el manejo de varias solicitudes en paralelo puede generar condiciones de carrera cuando la lógica de un controlador de solicitudes depende o interactúa con datos con un nivel superior al ámbito local, como el estado de la aplicación. Por esta razón, es importante codificar de manera defensiva para responder a las condiciones de carrera a fin de garantizar que tu nueva aplicación WSGI maneje de forma segura los subprocesos.

Consulta Manejar de forma segura los subprocesos si deseas conocer más detalles.

Actualiza app.yaml

Python 2.7 requiere un elemento de configuración de runtime especial en el encabezado de app.yaml. Ten en cuenta que el elemento threadsafe: [ true | false ] es obligatorio para las aplicaciones de Python 2.7. Si es true, App Engine envía solicitudes de forma simultánea; si es false, App Engine las envía en serie. El siguiente encabezado app.yaml habilita las solicitudes simultáneas:

application: myapp
version: 1
runtime: python27
api_version: 1
threadsafe: true
...

Usa WSGI

El entorno de ejecución de Python 2.7 te permite ejecutar directamente una aplicación de interfaz de puerta de enlace de servidor web (WSGI), en lugar de usar el adaptador run_wsgi_app para ejecutar el programa como una secuencia de comandos de CGI. Para ello, reemplaza el controlador CGI (p. ej., myapp.py) en app.yaml por un nombre de aplicación WSGI (p. ej., myapp.app).

...
handlers:
- url: /.*
  script: myapp.app
...

También debes mover tu objeto de aplicación WSGI al ámbito global:

import webapp2

class MainPage(webapp2.RequestHandler):
  def get(self):
    self.response.headers['Content-Type'] = 'text/plain'
    self.response.out.write('Hello, WebApp World!')

app = webapp2.WSGIApplication([('/', MainPage)])

""" Old code:
def main():
  run_wsgi_app(app)

if __name__ == '__main__':
  main()
"""

Aún puedes especificar controladores de secuencias de comandos de CGI en app.yaml, sin embargo, las solicitudes controladas por las secuencias de comandos de CGI se procesan en serie, no de forma simultánea. Además, no puedes tener un archivo app.yaml que mezcle secuencias de comandos de CGI y aplicaciones WSGI, y no puedes configurar threadsafe como true si defines cualquier controlador CGI.

Algunas convenciones de entornos de ejecución de Python anteriores, como el uso de main() y la verificación de __name__ == 'main', ahora están obsoletas. Estas medidas ayudaron a que las secuencias de comandos de CGI del pasado se mantuvieran en caché, pero ahora que estás ejecutando aplicaciones WSGI directamente, estos pasos ya no son necesarios.

Utilizar el directorio raíz de la aplicación

En Python 2.5, las secuencias de comandos de CGI se ejecutaban con el directorio de trabajo actual configurado en el directorio que contenía la secuencia de comandos. Esto ha cambiado en Python 2.7. Con WSGI, el directorio de trabajo actual al comienzo de la vida útil del controlador de solicitudes es el directorio raíz de la aplicación.

Revisa el código de tu aplicación y asegúrate de que todos los controladores estén escritos para esperar que el directorio de trabajo actual sea la raíz de la aplicación.

Configurar bibliotecas

El entorno de ejecución de Python 2.7 incluye algunos módulos de terceros. Varias de estas bibliotecas están disponibles mediante configuración predeterminada; otras solo están disponibles si se configuran. Puedes especificar qué versión quieres usar.

libraries:
- name: PIL
  version: "1.1.7"
- name: webob
  version: "1.1.1"

Puedes especificar que la aplicación utilice la última versión del módulo. Esto es útil si estás desarrollando una aplicación que aún no tiene usuarios: no necesitas realizar un seguimiento de las nuevas versiones. Pero si la aplicación se utiliza de manera activa, ten cuidado: tal vez te sorprenda saber que la aplicación comienza a utilizar una versión nueva de biblioteca que no es compatible con las versiones anteriores. Para utilizar la última versión:

libraries:
- name: PIL
  version: latest

Para obtener una lista de las bibliotecas compatibles, consulta Bibliotecas de terceros.

Manejar de forma segura los subprocesos

El manejo de solicitudes simultáneas es simple si cada controlador solo interactúa con variables dentro de su alcance. Pero se complica rápidamente si un controlador modifica los recursos a medida que otro los está leyendo. Asegurarse de que la aplicación se comporte como se espera, incluso aunque varias solicitudes estén manipulando los mismos datos y que interfieran entre sí, es lo que se conoce como manejar de forma segura los subprocesos.

La regla principal al diseñar una aplicación para que maneje de forma segura los subprocesos es limitar el uso de recursos compartidos (como información de estado o variables globales) con la mayor frecuencia posible. Sin embargo, por lo general, no es posible descartar completamente su uso, y ahí es donde entran mecanismos de sincronización como los objetos de bloqueo.

En Python 2.7, tienes acceso a la biblioteca de subprocesos de Python, que te permite declarar un bloqueo en un bloque de lógica que obliga a que el código interno se ejecute en serie en lugar de simultáneamente. Considera el siguiente código:

class Configuration(ndb.Model):
  some_config_data = ndb.StringProperty()
  _config_cache = None
  _config_lock = threading.Lock()
  @classmethod
  def get_config(cls):
    with cls._config_lock:
      if not cls._config_cache:
        cls._config_cache = cls.get_by_id('config')
    return cls._config_cache

En este código, se muestra la creación de una memoria caché de algunas variables de configuración global en una variable llamada _config_cache. Aquí, el uso de un objeto de bloqueo llamado _config_lock garantiza que la verificación de una _config_cache preexistente se comporte de manera confiable. De lo contrario, esta variable podría hacer varios viajes a Datastore para establecer la misma variable varias veces con los mismos datos, esto se debe a que todas las solicitudes que competían encontraron que _config_cache estaba vacío, lo cual sería una pérdida de tiempo.

No uses los bloqueos a la ligera. Un bloqueo obliga a otros subprocesos que ejecutan este método a bloquear. Esto puede generar un cuello de botella de rendimiento.

Actualiza tu aplicación a webapp2

Nota: Si no usas webapp como tu controlador de solicitudes, puedes omitir esta sección.

El framework web incluido en el entorno de ejecución de Python 2.7 se actualizó de webapp a webapp2. Además, webapp2 agrega el enrutamiento URI mejorado y el manejo de excepciones, un objeto de respuesta con todas las funciones y un mecanismo de envío más flexible.

Las plantillas webapp ahora están obsoletas. En su lugar, puedes usar Jinja2, Django o un sistema de plantillas de tu elección (siempre que esté escrito en Python puro).

En App Engine, webapp2 tiene un alias para webapp y webapp2 es retrocompatible. Sin embargo, después de la actualización, aún debes probar de manera exhaustiva tu aplicación y familiarizarte con la sintaxis y las capacidades nuevas de webapp2, en lugar de seguir confiando en la retrocompatibilidad.

¡Tu actualización está completa!

Una vez que hayas subido la aplicación, debes probarla exhaustivamente para garantizar la compatibilidad con versiones anteriores. Si experimentas problemas, consulta los foros.