Se trata de una guía para desarrollar aplicaciones con web.py y ejecutarlas en la infraestructura de Google. Veremos qué es Google App Engine, registro, instalación, cómo desarrollar una aplicación web básica y desplegarla al servidor, configuración, templates y almacenamiento de datos. Algún conocimiento previo sobre web.py es preferbile; para ello, véase Introducción a web.py.
Puedes descargar de aquí la aplicación final como resultado al aplicar los pasos brindados por el artículo.
¿Qué es Google App Engine?
Es una plataforma proporcionada por Google que permite al usuario ejecutar sus propias aplicaciones web en la red de servidores de la empresa. De esta manera, la última se encarga de manejar las peticiones HTTP, atribuyendo a la aplicación mayores o menores recursos en relación a la demanda. En otras palabras, el desarrollador debe preocuparse únicamente por el código dejando a un lado el mantenimiento del servidor. Presenta un panel de control por el cual se administra toda la aplicación, y permite utilizar dominios propios a través de Google Apps.
Si tu objetivo es portar una aplicación web a App Engine, te adelanto que tienes (probablemente bastante) trabajo de migración por delante (según el tamaño). GAE utiliza su propia base de datos y algunas convencionales, y no todas las funciones y módulos de Python están disponibles (véase El entorno).
Si bien es gratis, consiste en diversas cuotas las cuales limitan el alcance de la aplicación. Por ejemplo, si 50 visitantes acceden a mi página, es probable que dicha cuota se encuentre en, aproximadamente, un 10%. Esto indicaría que si accedieran otros 450 visitantes se completaría la cuota, llegando al 100%, y mi aplicación quedaría pausada hasta el reinicio de todas las cuotas (cada 24hs). Otro ejemplo sería el almacenamiento de datos (el cual veremos en detalle posteriormente). Por ejemplo, si la cuota estándar (gratis) se limita a los 1000 MB de datos almacenados y nuestra aplicación alcanza dicha cantidad, podrá seguir recibiendo peticiones (si es que no se ha alcanzado el máximo de ésta cuota), pero no podrá continuar almacenando en la base de datos.
También depende del usuario dismunuir el uso de la cuota, de lo contrario se acabará rápidamente. Por ejemplo, se recomienda utilizar el caché de la base de datos para evitar el aumento de la cuota de operaciones relacionadas con el almacenamiento. Sin embargo, es bastante complejo mantener una web con visitas diarias medianas haciendo uso de la cuota gratuita. Las cuentas premium permiten exceder la cuota gratuita abonando únicamente por lo que se utiliza.
Registro
GAE brinda espacio para 10 aplicaciones de forma gratuita. Para comenzar, debes crear una cuenta de Google (dirección de correo en Gmail) y acceder con ella en este enlace. Una vez registrado e ingresado, si nunca has trabajado con App Engine o no has creado ninguna aplicación deberías ver lo siguiente:
Posteriormente retomaremos esta pantalla para crear nuestra primera aplicación.
Instalación
Como segundo paso deberás descargar el SDK. El despliegue de la aplicación hacia la infraestructura de Google se realiza a través del mismo, a diferencia de otros servicios de hosting gratuito en los cuales los archivos deben ser subidos manualmente a través de una interfaz web. El kit se encuentra constantemente en desarrollo, por lo que al haber nuevas versiones el usuario es notificado. Lamentablemente no hay un sistema de actualizaciones automáticas, es tarea del usuario el descargar e instalar. Incluye un servidor web que simula al que es empleado una vez desplegada la aplicación, para realizar tareas localmente. GAE soporta varios lenguajes: PHP, Python, Java y Go. Puedes encontrar las descargas en este enlace, ¡y asegúrate de descargar el SDK de Python! Nótese que el SDK corre en cualquier computadora con Python 2.7 (actualmente no hay soporte para Python 3), en Windows, Mac OS X y Linux. En los dos primeros, el SDK provee el «Google App Engine Launcher», un programa con interfaz gráfica que simplifica las tareas del usuario permitiendo reemplazar a éste por la consola.
El entorno
App Engine presenta un entorno de Python puro. Por lo tanto, no puedes incluir módulos escritos en C o que necesiten ser compilados. Sin embargo, sí puedes añadir tus propios módulos o paquetes de terceros, siempre que sean compatibles con el entorno. Por ejemplo, las funciones de entrada y salida se encuentran deshabilitadas. En el caso que se precise almacenar, por ejemplo, una imágen, GAE presenta sus propios métodos para lograrlo, almacenando la información en sus bases de datos. Podremos encontrar dos bibliotecas: el conjunto de paquetes y módulos estándar (con ciertas limitacions) y la librería propia de App Engine (como el API de acceso a la base de datos y el framework webapp2). Además de éstas, incluye por defecto Django, jinja2, PyYAML, y varias más. Si deseas incluir un paquete adicional debes posicionarlo en la misma carpeta en donde se encuentra tu aplicación, para que sea desplegado junto con ella. Los módulos estándar ftplib
, socket
, select
, imp
y marshal
se encuentran vacíos. cPickle
no es soportado, por lo que se importa como pickle
.
Desarrollo
Este artículo explica el desarrollo de una aplicación web con el framework web.py. Sin embargo, también puedes utilizar algún otro que sea de tu agrado y soportado por la plataforma (como Django). Incluso GAE ofrece su propio web framework, webapp2.
Primer paso: creación de la aplicación
Para poder comenzar deberás crear una nueva aplicación desde appengine.google.com. Una vez ingresado, presiona en el botón «Create Application». No te preocupes por el espacio, todas las aplicaciones pueden ser desactivadas o removidas.
Completa los campos teniendo en cuenta lo siguiente:
- Application Identifier: dominio por el cual tendremos acceso a nuestra aplicación. Utiliza el botón «Check Availability» para verificar que esté disponible.
- Application Title: título para la aplicación. No es muy relevante pero no puede ser cambiado. Te recomiendo un nombre en minúsculas y sin espacios.
En los demás campos deja los valores por defecto. Por último presiona Create Application. Una vez creada, dirígete nuevamente a appengine.google.com y verás tu aplicación en la lista.
Segundo paso: configuración, estructura y código
Ahora deberás crear una carpeta en donde estará situado todo el contenido de tu aplicación, que será desplegado posteriormente a los servidores de Google. Para una mayor organización te recomiendo que dicha carpeta lleve el nombre o identificador de tu aplicación (el que has especificado en el paso anterior como «Application Title»). Una vez creada la carpeta el primer paso es crear un archivo app.yaml
dentro de la misma, con el siguiente contenido:
application: nombreapp # identificador de la aplicación (coloca el tuyo aquí)
version: 1 # versión de la aplicación
runtime: python27 # intérprete
api_version: 1 # versión del API del entorno de tiempo de ejecución
threadsafe: yes
# Estructura URL
handlers:
- url: /
script: main.app
Luego crea dos archivos más, main.app
y main.py
. El primero no debe tener contenido, el segundo será el script principal de nuestra aplicación.
main.py
#!/usr/bin/env python # -*- coding: utf-8 -*- import web urls = ( "/", "Index" ) app = web.application(urls, globals()).wsgifunc() class Index: def GET(self): return u"<h3>Bienvenido</h3>" if __name__ == "__main__": app = app.gaerun()
Nótese la función gaerun()
en la última línea en lugar de la convencional run()
. Si por alguna razón tu instalación de web.py no cuenta con dicha función, considera añadirla tú mismo, en el archivo application.py
dentro de la carpeta web
(ubicada en la instalación de Python) como un método de la clase application:
def gaerun(self, *middleware): """ Starts the program in a way that will work with Google app engine, no matter which version you are using (2.5 / 2.7) If it is 2.5, just normally start it with app.gaerun() If it is 2.7, make sure to change the app.yaml handler to point to the global variable that contains the result of app.gaerun() For example: in app.yaml (where code.py is where the main code is located) handlers: - url: /.* script: code.app Make sure that the app variable is globally accessible """ wsgiapp = self.wsgifunc(*middleware) try: # check what version of python is running version = sys.version_info[:2] major = version[0] minor = version[1] if major != 2: raise EnvironmentError("Google App Engine only supports python 2.5 and 2.7") # if 2.7, return a function that can be run by gae if minor == 7: return wsgiapp # if 2.5, use run_wsgi_app elif minor == 5: from google.appengine.ext.webapp.util import run_wsgi_app return run_wsgi_app(wsgiapp) else: raise EnvironmentError("Not a supported platform, use python 2.5 or 2.7") except ImportError: return wsgiref.handlers.CGIHandler().run(wsgiapp)
Por último deberás copiar la carpeta web
(directorio de instalación de web.py, ubicada en la instalación de Python) en el directorio de tu aplicación. De esta manera la última podrá acceder al framework una vez desplegada a Google.
Luego de realizar las tareas anteriores, el árbol de directorios debería quedar de la siguiente manera:
- nombreapp/
|
| - app.yaml
| - main.py
| - main.app
| - web/ <-- framework web.py
|
| - application.py
| - db.py
| - etc.
Tercer paso: configuración del Launcher
Nota: solo para usuarios de Windows y Mac OS X. De lo contrario, dirígete al cuarto paso.
El SDK para Windows y Mac OS X incluye el «Google App Engine Launcher», un programa adicional para evitar el uso de la línea de comandos y facilitar las tareas de ejecución y despliegue. Para poder realizar estas dos últimas debemos configurar nuestra aplicación. Ábrelo y verás algo como lo siguiente:
Dirígete al menú File
-> Add Existing Application...
o presiona Ctrl
+ Shift
+ N
. Se abrirá una nueva ventana. En Application Path, presiona el botón Browse ...
y selecciona la ubicación de la carpeta que contiene la aplicación. Especifica el puerto en el que deseas que corra tu aplicación (8080 por defecto) y presiona Add
al terminar. Una vez añadida verás el identificador en la lista de aplicaciones. A partir de ahora podrás ejecutar y desplegar tu aplicación directamente desde el Launcher.
Cuarto paso: servidor de desarrollo local
Nuestra aplicación ya está lista para ser desplegada hacia la infraestructura de Google. Sin embargo, sería óptimo probar la aplicación de manera local antes de subirla al servidor. En Linux, para iniciar el servidor de desarrollo local indica lo siguiente en la terminal:
dev_appserver.py ubicacionapp
Por defecto el puerto 8080 será utilizado. Para cambiarlo, indica:
dev_appserver.py --port=8081 ubicacionapp/
Con el número que desees.
En Windows y Mac OS X, abre el Launcher, selecciona la aplicación en la lista de aplicaciones y presiona el botón Run. Una vez ejecutado ingresa http://localhost:8080/ en la barra de direcciones de tu navegador y verás el mensaje de bienvenida. Para acceder al panel de control de la aplicación ingresa a http://localhost:8080/_ah/admin.
Para finalizar el servidor de desarrollo presiona Ctrl
+ C
en Linux o el botón Stop
en el Launcher.
Despliegue
Ya hemos desarrollado una aplicación y testeado su funcionalidad con el servidor de desarrollo local. Es momento de desplegarla a la infraestructura de Google. Para usuarios de Linux el procedimiento será vía consola. Para Windows y Mac OS X utilizaremos el Launcher.
Linux
Abre la terminal y ejecuta:
appcfg.py --email=yo@gmail.com update ubicacionapp/
La opción --email
indica la cuenta de Google en la cual se encuentra registrada la aplicación. Si tu dirección es @gmail.com, éste puede omitirse (--email=yo
). Luego, será solicitada la contraseña. ubicacionapp/
indica la carpeta o ruta en donde se encuentra el archivo app.yaml
junto con los demás, pero este primero determinará la existencia de una aplicación en dicha ruta para appcfg.py
. En caso de haber especificado erróneamente la ubicación, se quejará diciendo:
appcfg.py: error: Directory does not contain an Documents.yaml configuration file.
Si todo va bien, el output se verá más o menos así:
11:52 AM Host: appengine.google.com
11:52 AM Application: myapp; version: 1
11:52 AM
Starting update of app: myapp, version: 1
11:52 AM Getting current resource limits.
Password for yo: ********
11:52 AM Scanning files on local disk.
11:52 AM Cloning 25 application files.
11:52 AM Uploading 1 files and blobs.
11:52 AM Uploaded 1 files and blobs
11:52 AM Compilation starting.
11:52 AM Compilation completed.
11:52 AM Starting deployment.
11:52 AM Checking if deployment succeeded.
11:52 AM Will check again in 1 seconds.
11:52 AM Checking if deployment succeeded.
11:52 AM Deployment successful.
11:52 AM Checking if updated app version is serving.
11:53 AM Completed update of app: myapp, version: 1
En ciertas ocasiones appcfg.py
se puede tomar varios segundos, incluso minutos, chequeando si el despliegue tuvo éxito.
Windows y Mac OS X
Ejecuta Google App Engine Launcher y selecciona tu aplicación en la lista de aplicaciones. Luego, presiona el botón «Deploy» en la barra superior. La dirección de correo y contraseña serán solicitados. Al finalizar el proceso, la ventana «Delpoyment to Google» indicará que la misma puede ser cerrada.
Conclusión
La aplicación ya se encuentra en funcionamiento. Vista tuapp.appspot.com (siendo tuapp el identificador de tu aplicación) y verás en pantalla el mensaje de bienvenida.
Véase más sobre la subida de una aplicación Python en este enlace.
Templates
web.py compila los templates a código fuente de Python haciendo uso del módulo estándar parser
. Lamentablemente este último no está disponible en el entorno de GAE, por lo que para poder utilizar templates en nuestra aplicación debemos compilarlos previamente. Por lo tanto vamos a modificar la estructura de nuestra aplicación, de la siguiente manera:
- nombreapp/
|
| - app.yaml
| - main.py
| - main.app
| - templates/
| |
| | - index.html
| - web/
|
| - application.py
| - db.py
| - etc.
index.html
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> <head> <meta http-equiv="content-type" content="text/html;charset=utf-8" /> </head> <body> Bienvenido, <strong>$name</strong>! </body> </html>
main.py
#!/usr/bin/env python # -*- coding: utf-8 -*- import web urls = ( "/(.*)", "Index" ) app = web.application(urls, globals()).wsgifunc() render = web.template.render("templates") class Index: def GET(self, name): return render.index(name) if __name__ == "__main__": app = app.gaerun()
app.yaml: cambiar el valor de url.
handlers:
- url: /(.*)
script: main.app
Nótese en el último código que al cambiar la estructura de los URLs en main.py
, también deben modifcarse en app.yaml
.
Si en este momento desplegamos nuestra aplicación e ingresamos, obtendríamos un error como el siguiente:
Error: Server Error
The server encountered an error and could not complete your request. If the problem persists, please report your problem and mention this error message and the query that caused it.
Luego, al dirigirte a los Logs desde el panel de administración de la aplicación verás:
ImportError: No module named templates
Por lo tanto, antes del despliegue deberás compilar los templates abriendo la terminal e insertando:
python web/template.py --compile templates
En Windows puedes crear un archivo compile_templates.bat
y posicionarlo en la carpeta de tu aplicación con el contenido:
C:/python27/python.exe web/template.py --compile templates
Ambos métodos deben ser ejecutados siempre que se realicen modificaciones a los templates, antes de desplegar la aplicación al servidor. Una vez hecho esto, visita tuapp.appspot.com/RecursosPython y verás: «Bienvenido, RecursosPython!».
Almacenamiento de datos
Si bien es posible utilizar MySQL en el entorno de App Engine, en este apartado explicaré cómo usar la base de datos nativa de Google a través del API. Si bien se pueden realizar consultas SQL, se trata de un subconjunto del lenguaje denominado GQL (Google Query Language). Además, el almacenamiento de datos se realiza a través de los propios objetos. Todas las operaciones se llevan a cabo con el módulo google.appengine.ext.db
.
Teniendo en cuenta el código anterior, nuestra aplicación le da la bienvenida a cada usuario que se presenta luego de la barra «/». Ahora haremos que almacene el nombre de cada uno de ellos, y luego los muestre al acceder a «/stats». Antes de comenzar con la base de datos, accede a main.py
y reorganicemos la estructura de los URLs:
urls = ( "/stats", "Stats", "/(.*)", "Index" )
Tambíen en app.yaml
:
handlers:
- url: /stats
script: main.app
- url: /(.*)
script: main.app
Nótese que primero se indica «/stats», para que al ser accedido la petición se enviada a la clase Stats
y no a Index
(prioridad).
A continuación, comencemos con el código de la base de datos importando el módulo:
from google.appengine.ext import db
Anteriormente dije que nuestra aplicación va a registrar a cada visitante, por lo que necesitaremos crear una clase Visitor
con un atributo name, que equivaldría a una tabla y una columna, respectivamente, en las bases de datos relacionales.
class Visitor(db.Model): name = db.StringProperty()
Obsérvese que toda clase que represente el almacenamiento de datos debe heredar de db.Model
. A continuación, se especifican sus atributos. En este caso name es una cadena, pero podríamos haber especificado otros más como IntegerPropery
, BooleanProperty
, o cualquiera de las brindadas en esta lista. Por ejemplo:
class User(db.Model): name = db.StringProperty() alias = db.StringProperty() summary = db.TextProperty() register_date = db.DateTimeProperty() age = IntegerProperty() score = FloatProperty()
Continuando con la aplicación, para añadir un campo a la base de datos como un visitante, debemos crear una instancia de la clase Visitor
y llamar al método put()
.
visitor = Visitor() visitor.name = "Nombre" visitor.put()
De esta manera ya habría un campo en nuestra base de datos del tipo Visitor
denominado «Nombre». Añade el siguiente código a la clase Index
:
class Index: def GET(self, name): if not visitor_exists(name): visitor = Visitor() visitor.name = name visitor.put() return render.index(name)
Así, cada usuario que ingrese a nuestro sitio será almacenado como un visitante. La función visitor_exists()
verifica que el usuario no se encuentre en la base de datos:
def visitor_exists(name): return Visitor().gql("WHERE name = :1", name).get() is not None
Al ser una sola línea se ve algo confuso. Primero, crea una instancia de la clase Visitor
, y a partir de ésta realiza una consulta GQL (recuerda, el subconjunto de SQL de Google). Como podrás ver es similar al lenguaje convencional, una sentencia FROM table SELECT column WHERE field = value
. La diferencia es que como la consulta es llamada desde una clase, no es necesario especificar la tabla ni las columnas, ya que retornará un nuevo objeto del tipo Visitor con todas sus «columnas» (en este caso solo una, name
). El método get()
de la clase GqlQuery
retorna el primer objeto en la tabla, o None
si la misma está vacía.
Por último, la clase Stats deberá retornar la lista de los visitantes:
class Stats: def GET(self): visitors = db.GqlQuery("SELECT * FROM Visitor").fetch(None) if visitors: return ("Lista de visitantes: <br />" + "<br />".join([v.name for v in visitors])) else: return "No hay visitantes."
También se puede realizar una consulta instanciando directamente desde GqlQuery
, pasando como argumento una sentencia SQL convencional SELECT columnas FROM tabla
. El método fetch
retorna un iterable con todos los objetos de la tabla. El parámetro None
indica que no hay un límite.
Finalmente despliega la aplicación y visita tuapp.appspot.com/nombre reiteradas veces. Luego ingresa a tuapp.appspot.com/stats y verás los resultados. También puedes dirigirte al Datastore Viewer del panel de control de tu aplicación.
Código completo:
#!/usr/bin/env python # -*- coding: utf-8 -*- import web from google.appengine.ext import db urls = ( "/stats", "Stats", "/(.*)", "Index" ) app = web.application(urls, globals()).wsgifunc() render = web.template.render("templates") def visitor_exists(name): return Visitor().gql("WHERE name = :1", name).get() is not None class Visitor(db.Model): name = db.StringProperty() class Index: def GET(self, name): if not visitor_exists(name): visitor = Visitor() visitor.name = name visitor.put() return render.index(name) class Stats: def GET(self): visitors = db.GqlQuery("SELECT * FROM Visitor").fetch(None) if visitors: return ("Lista de visitantes: <br />" + "<br />".join([v.name for v in visitors])) else: return "No hay visitantes." if __name__ == "__main__": app = app.gaerun()
Versión
Python 2.7
Curso online 👨💻
¡Ya lanzamos el curso oficial de Recursos Python en Udemy!
Un curso moderno para aprender Python desde cero con programación orientada a objetos, SQL y tkinter
en 2024.
Consultoría 💡
Ofrecemos servicios profesionales de desarrollo y capacitación en Python a personas y empresas. Consultanos por tu proyecto.