¿Cómo administro las bibliotecas de Python de terceros con Google App Engine? (virtualenv? pip?)

¿Cuál es la mejor estrategia para administrar bibliotecas Python de terceros con Google App Engine?

Digamos que quiero usar Flask, un marco de aplicación web. Una entrada de blog dice hacer esto, lo que no parece correcto:

$ cd /tmp/ $ wget http://pypi.python.org/packages/source/F/Flask/Flask-0.6.1.tar.gz $ tar zxf Flask-0.6.1.tar.gz $ cp -r Flask-0.6.1/flask ~/path/to/project/ (... repeat for other packages ...) 

Debe haber una mejor manera de administrar el código de terceros, especialmente si quiero hacer un seguimiento de las versiones, probar las actualizaciones o si dos bibliotecas comparten un subdirectorio. Sé que Python puede importar módulos desde archivos zip y que pip puede funcionar con un maravilloso archivo de REQUISITOS, y he visto que pip tiene un comando zip para usar con GAE.

(Nota: hay un puñado de preguntas similares, 1 , 2 , 3 , 4 , 5 , pero son específicas de cada caso y realmente no responden a mi pregunta).

¿Qué pasa con simplemente:

 $ pip install -r requirements.txt -t  

Crear / editar /appengine_config.py :

 """This file is loaded when starting a new application instance.""" import sys import os.path # add `lib` subdirectory to `sys.path`, so our `main` module can load # third-party libraries. sys.path.insert(0, os.path.join(os.path.dirname(__file__), 'lib')) 

ACTUALIZAR:

Google actualizó su muestra a appengine_config.py , como:

  from google.appengine.ext import vendor vendor.add('lib') 

Nota: aunque su ejemplo tiene .gitignore ignorando el directorio lib/ , todavía necesita mantener ese directorio bajo control de código fuente si utiliza el método de implementación git-push .

Así es como lo hago:

  • proyecto
    • .Pitón
    • compartimiento
    • lib
      • python2.5
        • paquetes de sitio
    • incluir
    • src
      • app.yaml
      • index.yaml
      • main.yaml

El directorio del project es el directorio de nivel superior donde se encuentra el virtualenv. Obtengo el virtualenv usando los siguientes comandos:

 cd project virtualenv -p /usr/bin/python2.5 --no-site-packages --distribute . 

El directorio src es donde va todo tu código. Cuando implemente su código en GAE, * solo * implemente aquellos en el directorio src y nada más. appcfg.py resolverá los enlaces simbólicos y copiará los archivos de la biblioteca a GAE para usted.

No instalo mis bibliotecas como archivos zip principalmente por conveniencia en caso de que tenga que leer el código fuente, que hago mucho por curiosidad. Sin embargo, si realmente desea comprimir las bibliotecas, coloque el siguiente fragmento de código en main.py

 import sys for p in ['librarie.zip', 'package.egg'...]: sys.path.insert(0, p) 

Después de esto, puede importar sus paquetes comprimidos como de costumbre.

Una cosa a tener en cuenta es setuptools ‘ pkg_resources.py . Lo copié directamente en mi directorio src para que mis otros paquetes con enlaces simbólicos puedan usarlo. Cuidado con cualquier cosa que use entry_point s. En mi caso, estoy usando Toscawidgets2 y tuve que profundizar en el código fuente para cablear manualmente las piezas. Puede ser molesto si tiene muchas bibliotecas que dependen de entry_point .

Yo prefiero buildout .

Configure las dependencias en setup.py en su proyecto o buildout.cfg, fije las versiones en buildout.cfg y especifique qué paquetes no están disponibles en GAE y deben incluirse en packages.zip. rod.recipe.appengine copiará los paquetes requeridos en packages.zip, y mientras inserte packages.zip en el sys.path, se pueden importar a cualquier lugar.

También puede usar las horquillas de github si el paquete que necesita no está en pypi

 find-links = https://github.com/tesdal/pusher_client_python/tarball/rewrite#egg=pusher-2.0dev2 [versions] pusher = 2.0dev2 

y todas estas configuraciones y dependencias están versionadas en git.

En lugar de preguntarse qué copia de Flask se incluye actualmente en su árbol de fonts y quizás se copie en su control de versión (o requiera que los nuevos desarrolladores descompriman y actualicen manualmente), simplemente verifique la versión en buildout.cfg. Si desea una nueva versión, cambie buildout.cfg y vuelva a ejecutar buildout.

También puede usarlo para insertar variables en las plantillas de archivos de configuración, como configurar el id y la versión de appspot en app.yaml si tiene un servidor de pruebas con staging.cfg, etc.

Recientemente he creado una herramienta para este llamado gaenv. Sigue un formato Requirements.txt, pero no lo instala, puede instalarlo con pip install -r Requirements.txt y luego ejecutar la herramienta de línea de comandos gaenv.

 $ pip install -r requirements.txt $ gaenv 

Esto crea enlaces simbólicos automáticamente, también puede instalar gaenv en su virtualenv y ejecutar el binario desde allí. Aquí hay una entrada de blog al respecto:

http://blog.altlimit.com/2013/06/google-app-engine-virtualenv-tool-that.html

también en github

https://github.com/faisalraja/gaenv

La solución de Wernight es la más cercana a la práctica actual en la aplicación de ejemplo oficial de Flask , que ya he mejorado al cambiar la llamada site.addsitedir() a site.addsitedir() para permitir que los paquetes de espacio de nombres procesen a su operadora .pth Archivos .pth (que son importantes para marcos como Pyramid).

Hasta ahora todo bien, pero eso agrega el directorio a la ruta, y por lo tanto pierde la oportunidad de anular las bibliotecas incluidas (como WebOb y solicitudes) con versiones más nuevas.

Lo que se necesita entonces en appengine_config.py (y también estoy intentando que este cambio sea aceptado en los repositorys oficiales) es lo siguiente:

 """This file is loaded when starting a new application instance.""" import os.path import site.addsitedir import sys.path dirname = 'lib' dirpath = os.path.join(os.path.dirname(__file__), dirname) # split path after 1st element ('.') so local modules are always found first sys.path, remainder = sys.path[:1], sys.path[1:] # add `lib` subdirectory as a site directory, so our `main` module can load # third-party libraries. site.addsitedir(dirpath) # append the rest of the path sys.path.extend(remainder) 

La versión final de este código puede vendor.py oculta en un módulo vendor.py y llamada like insertsitedir(index, path) o alguna otra variación, como puede ver en la discusión que atiende esta solicitud de extracción , pero la lógica es más o menos independientemente de cómo funcionará, para permitir que un simple pip install -r requirements.txt -t lib/ funcione para todos los paquetes, incluidos los de espacio de nombres, y para permitir la anulación de las bibliotecas incluidas con nuevas versiones, ya que hasta ahora no he podido Encuentra una alternativa más simple .

Nota: esta respuesta es específica para Flask en Google App Engine.

Consulte el proyecto flask-appengine-template para ver un ejemplo de cómo hacer que las extensiones de Flask funcionen en App Engine. https://github.com/kamalgill/flask-appengine-template

Coloque la extensión en la carpeta del paquete de espacio de nombres en src / packages / flaskext y estará listo. https://github.com/kamalgill/flask-appengine-template/tree/master/src/lib/flaskext

Los paquetes que no son de Flask se pueden colocar en la carpeta src / packages como archivos zip, egg o paquetes descomprimidos, ya que la plantilla del proyecto incluye el fragmento de código sys.path.insert () publicado anteriormente.