Migración a Python 2.7

En esta página se describen las medidas necesarias para actualizar tu aplicación Python al entorno de ejecución de Python 2.7. Después de seguir este tutorial, tu aplicación podrá aprovechar las numerosas funciones nuevas del entorno de ejecución de Python 2.7, como el multihilo, el motor de plantillas Jinja2, el acceso y la subida de bytecode, y varias bibliotecas de terceros incluidas.

Requisitos previos y consideraciones

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

  • Si la aplicación usa Django, debe usar la versión 1.2 o una posterior. Consulta la documentación de Django para obtener más información sobre la actualización.
  • Si quieres usar solicitudes simultáneas, la aplicación debe usar controladores de secuencias de comandos de Web Server Gateway Interface (WSGI), tal como se explica en el artículo Usar WSGI.

Además de cumplir estos requisitos 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 a fondo después de actualizarla. En la siguiente lista se identifican los principales problemas de compatibilidad y se indican recursos adicionales para resolverlos:

  • Rendimiento: el rendimiento de la aplicación puede cambiar después de actualizar a Python 2.7. Si experimentas un aumento de la latencia de respuesta, puedes aumentar la clase de instancia del frontend y habilitar las solicitudes simultáneas. Las solicitudes simultáneas permiten que tu aplicación funcione más rápido con un coste de instancia más bajo en instancias más grandes. Para obtener más información, consulta Gestió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 cómo actualizar, consulta las notas de la versión de Django.
  • PyCrypto Crypto.Util.randpool ha quedado obsoleto. Ahora se utiliza 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 directamente plantillas de Django, jinja2 u otro motor de plantillas que elijas.
  • WebOb Python 2.7 es compatible con la versión 1.1 de WebOb. Esta versión no es totalmente retrocompatible con la versión anterior (0.9). Si la aplicación usa WebOb, debes probarla a fondo para detectar cualquier error que se produzca como resultado 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 incluye el módulo json de la biblioteca estándar, que es equivalente y mucho más rápido.

Solicitudes simultáneas y WSGI

La función de Python 2.7 que más afecta al diseño y al rendimiento de tu aplicación es su compatibilidad con aplicaciones multiproceso que pueden gestionar solicitudes simultáneas. La capacidad de gestionar solicitudes simultáneas mejora el uso, lo que puede aumentar significativamente el rendimiento de tu aplicación, especialmente en el caso de las aplicaciones que utilizan clases de instancias superiores que aprovechan varios núcleos de CPU.

Para habilitar el multihilo, las aplicaciones deben pasar del enfoque basado en la interfaz de puerta de enlace común (CGI) de los tiempos 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 CGI, que se diseñaron para gestionar las solicitudes de forma secuencial, dependen de las variables de entorno para acceder a los flujos de entrada y salida.

Aunque el modelo WSGI para gestionar solicitudes ofrece a las aplicaciones un acceso más directo a los flujos de entrada y salida (lo que permite que se realicen solicitudes simultáneas), la gestión de varias solicitudes en paralelo puede provocar condiciones de carrera cuando la lógica de un controlador de solicitudes se basa en datos con un ámbito superior al local o interactúa con ellos, como el estado de la aplicación. Por este motivo, es importante codificar de forma defensiva para hacer frente a las condiciones de carrera y asegurarse de que la nueva aplicación WSGI sea segura para los subprocesos.

Consulta más información sobre cómo hacer que tu aplicación sea segura para subprocesos.

Actualizar app.yaml

Python 2.7 requiere un elemento de configuración 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 true, App Engine envía las solicitudes simultáneamente; si false, las envía de forma secuencial. El siguiente encabezado app.yaml permite enviar solicitudes simultáneas:

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

Usar WSGI

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

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

También debes mover el 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()
"""

Aun así, puedes especificar controladores de secuencias de comandos CGI en app.yaml. Sin embargo, las solicitudes gestionadas por secuencias de comandos CGI se procesan de forma secuencial, no simultánea. Además, no puedes tener un archivo app.yaml que combine secuencias de comandos CGI y aplicaciones WSGI, y no puedes definir threadsafe como true si defines algún controlador CGI.

Algunas convenciones de los entornos de ejecución de Python anteriores, como el uso de main() y la comprobación de __name__ == 'main', ya no están disponibles. Estas medidas ayudaban a que las secuencias de comandos CGI del pasado permanecieran en la caché, pero ahora que ejecutas aplicaciones WSGI directamente, estos pasos ya no son necesarios.

Usar el directorio raíz de la aplicación

En Python 2.5, las secuencias de comandos CGI se ejecutaban con el directorio de trabajo actual definido 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 principio del ciclo de vida 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 que el directorio de trabajo actual sea la raíz de la aplicación.

Configurar bibliotecas

El tiempo de ejecución de Python 2.7 incluye algunos módulos de terceros. Algunas están disponibles de forma predeterminada, mientras que otras solo se pueden usar 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 debe usar la versión más reciente del módulo. Esto resulta útil si estás desarrollando una aplicación que aún no tiene usuarios, ya que no tendrás que hacer un seguimiento de las versiones nuevas. Sin embargo, si tu aplicación se está usando activamente, ten cuidado: puede que te sorprenda que tu aplicación empiece a usar una nueva versión de biblioteca que no sea compatible con versiones anteriores. Para usar la versión más reciente, sigue estos pasos:

libraries:
- name: PIL
  version: latest

Para ver una lista de las bibliotecas admitidas, consulta Bibliotecas de terceros.

Hacer que tu aplicación sea segura para subprocesos

Gestionar solicitudes simultáneas es sencillo si cada controlador solo interactúa con variables dentro de su ámbito. Sin embargo, la situación se complica rápidamente si un controlador modifica los recursos mientras otro los lee. Asegurarse de que tu aplicación se comporta como se espera, aunque varias solicitudes puedan manipular los mismos datos e interferir entre sí, se conoce como "threadsafe".

La regla principal a la hora de diseñar una aplicación que sea segura para subprocesos es limitar el uso de recursos compartidos (como información de estado o variables globales) tanto como sea posible. Sin embargo, normalmente no es posible descartar por completo su uso, y es ahí donde entran en juego los mecanismos de sincronización, como los objetos de bloqueo.

En Python 2.7, tienes acceso a la biblioteca de threading de Python, que te permite declarar un bloqueo en un bloque de lógica que obliga a que el código interno se ejecute de forma secuencial en lugar de simultánea. Echa un vistazo al 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

Este código muestra la creación de una caché de algunas variables de configuración globales en una variable llamada _config_cache. En este caso, el uso de un objeto de bloqueo llamado _config_lock asegura que la comprobación de un _config_cache preexistente se comporte de forma fiable. De lo contrario, esta variable podría perder tiempo haciendo varios viajes a Datastore para definir la misma variable varias veces con los mismos datos, ya que todas las solicitudes simultáneas han detectado que _config_cache estaba vacío.

No elijas usar bloqueos a la ligera. Un bloqueo obliga a bloquear cualquier otro hilo que ejecute este método. Esto puede suponer un cuello de botella para el rendimiento.

Actualizar una aplicación a webapp2

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

El framework web incluido en el entorno de ejecución de Python 2.7 se ha actualizado de webapp a webapp2. Entre otras cosas, webapp2 añade una mejor gestión de excepciones y de rutas de URIs, un objeto de respuesta con todas las funciones y un mecanismo de envío más flexible.

Las plantillas webapp se han desactivado. En su lugar, puedes usar Jinja2, Django o el sistema de plantillas que quieras (siempre que esté escrito en Python puro).

En App Engine, webapp2 se ha asignado a webapp y webapp2 es retrocompatible. Sin embargo, después de actualizar, debes probar a fondo tu aplicación y familiarizarte con la nueva sintaxis y las nuevas funciones de webapp2, en lugar de seguir usando la compatibilidad con versiones anteriores.

¡Y ya has completado la actualización!

Una vez que hayas subido la aplicación, debes probarla a fondo para asegurarte de que es compatible con versiones anteriores. Si tienes problemas, consulta los foros.