Cloud Functions de HTTP en Python

1. Introducción

b158ce75c3cccd6d.png

Python es un lenguaje de programación de código abierto popular que usan los científicos de datos, los desarrolladores de aplicaciones web, los administradores de sistemas y muchos más.

Cloud Functions es una plataforma de computación sin servidores controlada por eventos. Cloud Functions te permite escribir código sin preocuparte por aprovisionar recursos ni escalar para controlar los requisitos cambiantes.

Existen dos tipos de funciones de Cloud Functions:

  • Las funciones de HTTP responden a las solicitudes HTTP. En este codelab, compilarás un par de ellos.
  • Las funciones en segundo plano se activan mediante eventos, como la publicación de un mensaje en Cloud Pub/Sub o la carga de un archivo en Cloud Storage. No abordamos este tema en este lab, pero puedes obtener más información en la documentación.

efb3268e3b74ed4f.png

En este codelab, se te guiará para que crees tus propias Cloud Functions en Python.

Qué compilarás

En este codelab, publicarás una Cloud Function que, cuando se invoque a través de HTTP, mostrará el logotipo de "Python Powered":

a7aaf656b78050fd.png

Qué aprenderás

  • Cómo escribir una Cloud Function de HTTP
  • Cómo escribir una función de Cloud Functions de HTTP que tome argumentos
  • Cómo probar una Cloud Function de HTTP
  • Cómo ejecutar un servidor HTTP local de Python para probar la función
  • Cómo escribir una Cloud Function de HTTP que devuelva una imagen

2. Configuración y requisitos

Configuración del entorno de autoaprendizaje

  1. Accede a la consola de Google Cloud y crea un proyecto nuevo o reutiliza uno existente. Si aún no tienes una cuenta de Gmail o de Google Workspace, debes crear una.

fbef9caa1602edd0.pnga99b7ace416376c4.png5e3ff691252acf41.png

  • El Nombre del proyecto es el nombre visible de los participantes de este proyecto. Es una cadena de caracteres que no se utiliza en las APIs de Google. Puedes actualizarla cuando quieras.
  • El ID del proyecto es único en todos los proyectos de Google Cloud y es inmutable (no se puede cambiar después de configurarlo). La consola de Cloud genera automáticamente una cadena única. Por lo general, no importa cuál sea. En la mayoría de los codelabs, deberás hacer referencia al ID de tu proyecto (suele identificarse como PROJECT_ID). Si no te gusta el ID que se generó, podrías generar otro aleatorio. También puedes probar uno propio y ver si está disponible. No se puede cambiar después de este paso y se usa el mismo durante todo el proyecto.
  • Recuerda que hay un tercer valor, un número de proyecto, que usan algunas APIs. Obtén más información sobre estos tres valores en la documentación.
  1. A continuación, deberás habilitar la facturación en la consola de Cloud para usar las APIs o los recursos de Cloud. Ejecutar este codelab no costará mucho, tal vez nada. Para cerrar recursos y evitar que se generen cobros más allá de este instructivo, puedes borrar los recursos que creaste o borrar el proyecto. Los usuarios nuevos de Google Cloud son aptos para participar en el programa Prueba gratuita de $300.

Inicie Cloud Shell

Si bien Google Cloud se puede operar de manera remota desde tu laptop, en este codelab usarás Cloud Shell, un entorno de línea de comandos que se ejecuta en la nube.

Activar Cloud Shell

  1. En la consola de Cloud, haz clic en Activar Cloud Shell853e55310c205094.png.

3c1dabeca90e44e5.png

Si es la primera vez que inicias Cloud Shell, aparecerá una pantalla intermedia en la que se describirá qué es. Si aparece esta pantalla, haz clic en Continuar.

9c92662c6a846a5c.png

El aprovisionamiento y la conexión a Cloud Shell solo tomará unos minutos.

9f0e51b578fecce5.png

Esta máquina virtual está cargada con todas las herramientas de desarrollo necesarias. Ofrece un directorio principal persistente de 5 GB y se ejecuta en Google Cloud, lo que permite mejorar considerablemente el rendimiento de la red y la autenticación. Gran parte de tu trabajo en este codelab, si no todo, se puede hacer con un navegador.

Una vez que te conectes a Cloud Shell, deberías ver que ya te autenticaste y que el proyecto ya se configuró con el ID de tu proyecto.

  1. En Cloud Shell, ejecuta el siguiente comando para confirmar que tienes la autenticación:
gcloud auth list

Resultado del comando

 Credentialed Accounts
ACTIVE  ACCOUNT
*       <my_account>@<my_domain.com>

To set the active account, run:
    $ gcloud config set account `ACCOUNT`
  1. En Cloud Shell, ejecuta el siguiente comando para confirmar que el comando gcloud conoce tu proyecto:
gcloud config list project

Resultado del comando

[core]
project = <PROJECT_ID>

De lo contrario, puedes configurarlo con el siguiente comando:

gcloud config set project <PROJECT_ID>

Resultado del comando

Updated property [core/project].

Asegúrate de que las APIs de Cloud Functions y Cloud Build estén habilitadas

Ejecuta el siguiente comando desde Cloud Shell para asegurarte de que las APIs de Cloud Functions y Cloud Build estén habilitadas:

gcloud services enable \
  cloudfunctions.googleapis.com \
  cloudbuild.googleapis.com

Nota: El comando gcloud functions deploy llamará a Cloud Build, que compilará automáticamente tu código en una imagen de contenedor.

Descarga el código fuente

En la terminal de Cloud Shell, ejecuta los siguientes comandos:

REPO_NAME="codelabs"
REPO_URL="https://github.com/GoogleCloudPlatform/$REPO_NAME"
SOURCE_DIR="cloud-functions-python-http"

git clone --no-checkout --filter=blob:none --depth=1 $REPO_URL
cd $REPO_NAME
git sparse-checkout set $SOURCE_DIR
git checkout
cd $SOURCE_DIR

Revisa el contenido del directorio del código fuente:

ls

Deberías tener los siguientes archivos:

main.py  python-powered.png  test_main.py  web_app.py

3. Introducción a las funciones HTTP de Cloud Functions

Las funciones de Cloud Run de HTTP en Python se escriben como funciones de Python normales. La función debe aceptar un solo argumento flask.Request, que suele llamarse request.

main.py

import flask


def hello_world(request: flask.Request) -> flask.Response:
    """HTTP Cloud Function.

    Returns:
    - "Hello World! 👋"
    """
    response = "Hello World! 👋"

    return flask.Response(response, mimetype="text/plain")

# ...

Puedes abrir el archivo con tu editor de línea de comandos preferido (nano, vim o emacs). También puedes abrirlo en el editor de Cloud Shell después de configurar el directorio del código fuente como un lugar de trabajo:

cloudshell open-workspace .

Implementemos esta función como una Cloud Function de HTTP con el comando gcloud functions deploy:

FUNCTION_NAME="hello_world"

gcloud functions deploy $FUNCTION_NAME \
  --runtime python312 \
  --trigger-http \
  --allow-unauthenticated

Resultado del comando:

...
Deploying function (may take a while - up to 2 minutes)...done.
availableMemoryMb: 256
...
entryPoint: FUNCTION_NAME
httpsTrigger:
  url: https://REGION-PROJECT_ID.cloudfunctions.net/FUNCTION_NAME
...

Notas sobre las opciones de gcloud functions deploy:

  • --runtime: Especifica el entorno de ejecución del lenguaje. En Python, actualmente puede ser python37, python38, python39, python310 o python312. Consulta Entornos de ejecución.
  • --trigger-http: Se asignará un extremo a la función. Las solicitudes HTTP (POST, PUT, GET, DELETE y OPTIONS) al extremo activarán la ejecución de la función.
  • --allow-unauthenticated: La función será pública, lo que permitirá que todos los emisores la llamen sin verificar la autenticación.
  • Para obtener más información, consulta gcloud functions deploy.

Para probar la función, puedes hacer clic en la URL de httpsTrigger.url que se muestra en el resultado del comando anterior. También puedes recuperar de manera programática la URL y llamar a la función con los siguientes comandos:

URL=$(gcloud functions describe $FUNCTION_NAME --format "value(httpsTrigger.url)")
curl -w "\n" $URL

Deberías obtener el siguiente resultado:

Hello World! 👋

4. Escribe una Cloud Function de HTTP que tome argumentos

Las funciones son más versátiles cuando aceptan argumentos. Definamos una nueva función hello_name que admita un parámetro name:

main.py

# ...

def hello_name(request: flask.Request) -> flask.Response:
    """HTTP Cloud Function.

    Returns:
    - "Hello {NAME}! 🚀" if "name=NAME" is defined in the GET request
    - "Hello World! 🚀" otherwise
    """
    name = request.args.get("name", "World")
    response = f"Hello {name}! 🚀"

    return flask.Response(response, mimetype="text/plain")

# ...

Implementemos esta nueva función:

FUNCTION_NAME="hello_name"

gcloud functions deploy $FUNCTION_NAME \
  --runtime python312 \
  --trigger-http \
  --allow-unauthenticated

Resultado del comando:

...
Deploying function (may take a while - up to 2 minutes)...done.
availableMemoryMb: 256
...
entryPoint: FUNCTION_NAME
httpsTrigger:
  url: https://REGION-PROJECT_ID.cloudfunctions.net/FUNCTION_NAME
...

Para probar la función, puedes hacer clic en la URL de httpsTrigger.url que se muestra en el resultado del comando anterior. También puedes recuperar de manera programática la URL y llamar a la función con los siguientes comandos:

URL=$(gcloud functions describe $FUNCTION_NAME --format "value(httpsTrigger.url)")
curl -w "\n" $URL

Deberías obtener el resultado predeterminado:

Hello World! 🚀

Obtienes el resultado predeterminado porque no se estableció el argumento name. Agrega un parámetro a la URL:

curl -w "\n" $URL?name=YOUR%20NAME

Esta vez, obtendrás tu respuesta personalizada:

Hello YOUR NAME! 🚀

El siguiente paso es agregar pruebas de unidades para asegurarte de que tus funciones sigan funcionando como se espera cuando se actualice el código fuente.

5. Escribir pruebas

Las funciones de Cloud Functions de HTTP en Python se prueban con el módulo unittest de la biblioteca estándar. No es necesario ejecutar un emulador ni otras simulaciones para probar tu función, solo necesitas código Python estándar.

Así se ve una prueba para las funciones hello_world y hello_name:

test_main.py

import unittest
import unittest.mock

import main


class TestHello(unittest.TestCase):
    def test_hello_world(self):
        request = unittest.mock.Mock()

        response = main.hello_world(request)
        assert response.status_code == 200
        assert response.get_data(as_text=True) == "Hello World! 👋"

    def test_hello_name_no_name(self):
        request = unittest.mock.Mock(args={})

        response = main.hello_name(request)
        assert response.status_code == 200
        assert response.get_data(as_text=True) == "Hello World! 🚀"

    def test_hello_name_with_name(self):
        name = "FirstName LastName"
        request = unittest.mock.Mock(args={"name": name})

        response = main.hello_name(request)
        assert response.status_code == 200
        assert response.get_data(as_text=True) == f"Hello {name}! 🚀"
  1. Las pruebas de Python se escriben de la misma manera que otros archivos de Python. Comienzan con un conjunto de importaciones y, luego, definen clases y funciones.
  2. La declaración de prueba tiene la forma class TestHello(TestCase). Debe ser una clase que herede de unittest.TestCase.
  3. La clase de prueba tiene métodos, cada uno de los cuales debe comenzar con test_, que representan casos de prueba individuales.
  4. Cada caso de prueba prueba una de nuestras funciones simulando el parámetro request (es decir, reemplazándolo por un objeto falso con los datos específicos requeridos para la prueba).
  5. Después de invocar cada función, la prueba verifica la respuesta HTTP para asegurarse de que sea la esperada.

Como main.py depende de flask, asegúrate de que el framework de Flask esté instalado en tu entorno de pruebas:

pip install flask

La instalación de Flask genera un resultado similar al siguiente:

Collecting flask
...
Successfully installed ... flask-3.0.2 ...

Ejecuta estas pruebas de forma local:

python -m unittest

Las tres pruebas de unidades deberían aprobarse:

...
----------------------------------------------------------------------
Ran 3 tests in 0.001s

OK

A continuación, crearás una función nueva que devuelva el logotipo "Python Powered".

6. Escribe la Cloud Function de HTTP "Python Powered"

Creemos una función nueva un poco más entretenida que devuelva la imagen "Python Powered" para cada solicitud:

a7aaf656b78050fd.png

En la siguiente lista, se muestra el código para lograrlo:

main.py

# ...

def python_powered(request: flask.Request) -> flask.Response:
    """HTTP Cloud Function.

    Returns:
    - The official "Python Powered" logo
    """
    return flask.send_file("python-powered.png")

Implementa una nueva función python_powered:

FUNCTION_NAME="python_powered"

gcloud functions deploy $FUNCTION_NAME \
  --runtime python312 \
  --trigger-http \
  --allow-unauthenticated

Resultado del comando:

...
Deploying function (may take a while - up to 2 minutes)...done.
availableMemoryMb: 256
...
entryPoint: FUNCTION_NAME
httpsTrigger:
  url: https://REGION-PROJECT_ID.cloudfunctions.net/FUNCTION_NAME
...

Para probar la función, haz clic en la URL de httpsTrigger.url que se muestra en el resultado del comando anterior. Si todo funciona correctamente, verás el logotipo "Python Powered" en una nueva pestaña del navegador.

A continuación, crearás una app para poder ejecutar y probar tu función de forma local antes de implementarla.

7. Ejecuta la función de forma local

Para ejecutar una función de HTTP de forma local, crea una app web y llama a tu función en una ruta. Puedes agregarlo en el mismo directorio que tu función. El archivo llamado web_app.py tiene el siguiente contenido:

web_app.py

import flask

import main

app = flask.Flask(__name__)


@app.get("/")
def index():
    return main.python_powered(flask.request)


if __name__ == "__main__":
    # Local development only
    # Run "python web_app.py" and open http://localhost:8080
    app.run(host="localhost", port=8080, debug=True)
  1. Este archivo crea una aplicación de Flask.
  2. Registra una ruta en la URL base que se controla con una función llamada index().
  3. Luego, la función index() llama a nuestra función python_powered y le pasa la solicitud actual.

Asegúrate de que el framework de Flask esté instalado en tu entorno de desarrollo:

pip install flask

La instalación de Flask genera un resultado similar al siguiente:

Collecting flask
...
Successfully installed ... flask-3.0.2 ...

Para ejecutar esta aplicación de forma local, ejecuta el siguiente comando:

python web_app.py

Ahora, usa la vista previa en la Web de Cloud Shell para probar la app web en tu navegador. En Cloud Shell, haz clic en el botón "Vista previa en la Web" y selecciona "Vista previa en el puerto 8080":

6c9ff9e5c692c58e.gif

Cloud Shell abre la URL de vista previa en una ventana nueva del navegador, en su servicio de proxy. La vista previa en la Web restringe el acceso a través de HTTPS solo para tu cuenta de usuario. Si todo funciona correctamente, deberías ver el logotipo "Python Powered".

8e5c3ead11cfd103.png

8. ¡Felicitaciones!

b158ce75c3cccd6d.png

Implementaste funciones de Cloud Functions de HTTP con funciones idiomáticas que controlan solicitudes web con el framework de Flask.

Los precios de Cloud Functions se basan en la frecuencia con la que se invoca tu función, incluido un nivel gratuito para las funciones que no se ejecutan con frecuencia. Cuando termines de probar tus funciones de Cloud Functions, puedes borrarlas con gcloud:

gcloud functions delete hello_world --quiet
gcloud functions delete hello_name --quiet
gcloud functions delete python_powered --quiet

También puedes borrar las funciones desde la consola de Google Cloud.

Esperamos que disfrutes de usar Cloud Functions en Python.