Desarrollo de extensiones de trabajo de Python para Azure Functions
Azure Functions permite integrar comportamientos personalizados como parte de la ejecución de funciones de Python. Esta característica le permite crear lógica de negocios que los clientes pueden usar fácilmente en sus propias aplicaciones de funciones. Para obtener más información, vea la Referencia para desarrolladores de Python. Las extensiones de trabajo se admiten en los modelos de programación de Python v1 y v2.
En este tutorial, aprenderá a:
- Crear una extensión de trabajo de Python de nivel de aplicación para Azure Functions
- Consumir la extensión en una aplicación tal como lo hacen los clientes
- Empaquetar y publicar una extensión para su consumo
Requisitos previos
Antes de empezar, debe cumplir estos requisitos:
Python 3.7 o versiones posteriores. Para consultar la lista completa de las versiones compatibles de Python en Azure Functions, consulte la Guía para desarrolladores de Python.
El Azure Functions Core Tools, versión 4.0.5095 o posterior, que admite el uso de la extensión con el modelo de programación de Python v2. Compruebe su versión con
func --version
.Tener instalado Visual Studio Code en una de las plataformas compatibles.
Creación de la extensión de trabajo de Python
La extensión que cree informará del tiempo transcurrido de una invocación de desencadenador HTTP en los registros de la consola y en el cuerpo de la respuesta HTTP.
Estructura de carpetas
La carpeta del proyecto de extensión debería tener una estructura similar a la siguiente:
<python_worker_extension_root>/
| - .venv/
| - python_worker_extension_timer/
| | - __init__.py
| - setup.py
| - readme.md
Carpeta/Archivo | Descripción |
---|---|
.venv/ | (Opcional) Contiene un entorno virtual de Python usado para el desarrollo local. |
python_worker_extension/ | Contiene el código fuente de la extensión de trabajo de Python. Esta carpeta contiene el módulo principal de Python que se va a publicar en PyPI. |
setup.py | Contiene los metadatos del paquete de extensión de trabajo de Python. |
readme.md | Contiene la instrucción y el uso de la extensión. Este contenido se muestra como descripción en la página principal del proyecto de PyPI. |
Configuración de los metadatos del proyecto
Primero debe crear setup.py
, que proporciona información esencial sobre el paquete. Para asegurarse de que la extensión se distribuya y se integre correctamente en las aplicaciones de funciones del cliente, confirme que 'azure-functions >= 1.7.0, < 2.0.0'
se encuentra en la sección install_requires
.
En la plantilla siguiente, debe cambiar los camposauthor
, author_email
, install_requires
, license
, packages
y url
, según sea necesario.
from setuptools import find_packages, setup
setup(
name='python-worker-extension-timer',
version='1.0.0',
author='Your Name Here',
author_email='your@email.here',
classifiers=[
'Intended Audience :: End Users/Desktop',
'Development Status :: 5 - Production/Stable',
'Intended Audience :: End Users/Desktop',
'License :: OSI Approved :: Apache Software License',
'Programming Language :: Python',
'Programming Language :: Python :: 3.7',
'Programming Language :: Python :: 3.8',
'Programming Language :: Python :: 3.9',
'Programming Language :: Python :: 3.10',
],
description='Python Worker Extension Demo',
include_package_data=True,
long_description=open('readme.md').read(),
install_requires=[
'azure-functions >= 1.7.0, < 2.0.0',
# Any additional packages that will be used in your extension
],
extras_require={},
license='MIT',
packages=find_packages(where='.'),
url='https://your-github-or-pypi-link',
zip_safe=False,
)
A continuación, implementará el código de extensión en el ámbito de nivel de aplicación.
Implementación de la extensión de temporizador
Agregue el código siguiente en python_worker_extension_timer/__init__.py
para implementar la extensión de nivel de aplicación:
import typing
from logging import Logger
from time import time
from azure.functions import AppExtensionBase, Context, HttpResponse
class TimerExtension(AppExtensionBase):
"""A Python worker extension to record elapsed time in a function invocation
"""
@classmethod
def init(cls):
# This records the starttime of each function
cls.start_timestamps: typing.Dict[str, float] = {}
@classmethod
def configure(cls, *args, append_to_http_response:bool=False, **kwargs):
# Customer can use TimerExtension.configure(append_to_http_response=)
# to decide whether the elapsed time should be shown in HTTP response
cls.append_to_http_response = append_to_http_response
@classmethod
def pre_invocation_app_level(
cls, logger: Logger, context: Context,
func_args: typing.Dict[str, object],
*args, **kwargs
) -> None:
logger.info(f'Recording start time of {context.function_name}')
cls.start_timestamps[context.invocation_id] = time()
@classmethod
def post_invocation_app_level(
cls, logger: Logger, context: Context,
func_args: typing.Dict[str, object],
func_ret: typing.Optional[object],
*args, **kwargs
) -> None:
if context.invocation_id in cls.start_timestamps:
# Get the start_time of the invocation
start_time: float = cls.start_timestamps.pop(context.invocation_id)
end_time: float = time()
# Calculate the elapsed time
elapsed_time = end_time - start_time
logger.info(f'Time taken to execute {context.function_name} is {elapsed_time} sec')
# Append the elapsed time to the end of HTTP response
# if the append_to_http_response is set to True
if cls.append_to_http_response and isinstance(func_ret, HttpResponse):
func_ret._HttpResponse__body += f' (TimeElapsed: {elapsed_time} sec)'.encode()
Este código hereda de AppExtensionBase para que la extensión se aplique a todas las funciones de la aplicación. También podría implementar la extensión en un ámbito de nivel de función si hereda de FuncExtensionBase.
El método init
es un método de clase al que llama el trabajo cuando se importa la clase de extensión. Puede realizar acciones de inicialización aquí para la extensión. En este caso, se inicializa una asignación de códigos hash para registrar la hora de inicio de la invocación para cada función.
El método configure
está orientado al cliente. En el archivo Léame, puede indicar a los clientes cuándo necesitan llamar a Extension.configure()
. El archivo Léame también debe documentar las funcionalidades de la extensión, su posible configuración y su uso. En este ejemplo, los clientes pueden decidir si el tiempo transcurrido se notifica en HttpResponse
.
El trabajo de Python llama al método pre_invocation_app_level
antes de que se ejecute la función. Proporciona la información de la función, como su contexto y argumentos. En este ejemplo, la extensión registra un mensaje y guarda la hora de inicio de una invocación en función de su invocation_id.
De forma similar, se llama a post_invocation_app_level
después de la ejecución de la función. En este ejemplo se calcula el tiempo transcurrido en función de la hora de inicio y la hora actual. También sobrescribe el valor devuelto de la respuesta HTTP.
Crear un léame.md
Cree un archivo léame.md en la raíz del proyecto de extensión. Este archivo contiene las instrucciones y el uso de la extensión. El contenido de readme.md se muestra como descripción en la página principal del proyecto de PyPI.
# Python Worker Extension Timer
In this file, tell your customers when they need to call `Extension.configure()`.
The readme should also document the extension capabilities, possible configuration,
and usage of your extension.
Consumo local de la extensión
Ahora que ha creado una extensión, puede usarla en un proyecto de aplicación para comprobar que funciona según lo previsto.
Creación de una función desencadenada por HTTP
Cree una carpeta para el proyecto de la aplicación y vaya a ella.
Desde el shell adecuado, como Bash, ejecute el siguiente comando para inicializar el proyecto:
func init --python
Use el siguiente comando para crear una función de desencadenador HTTP que permita el acceso anónimo:
func new -t HttpTrigger -n HttpTrigger -a anonymous
Activación de un entorno virtual
Cree un entorno virtual de Python basado en el sistema operativo, como se muestra a continuación:
Active el entorno virtual de Python basado en el sistema operativo, como se muestra a continuación:
Configuración de la extensión
Instale paquetes remotos para el proyecto de aplicación de funciones con el siguiente comando:
pip install -r requirements.txt
Instale la extensión desde la ruta de acceso del archivo local en modo editable, como se muestra a continuación:
pip install -e <PYTHON_WORKER_EXTENSION_ROOT>
En este ejemplo, reemplace
<PYTHON_WORKER_EXTENSION_ROOT>
por la ubicación del archivo raíz del proyecto de extensión.Cuando un cliente use la extensión, en su lugar agregará la ubicación del paquete de extensión al archivo requirements.txt, como en los ejemplos siguientes:
Abra el archivo de proyecto local.settings.json y agregue el siguiente campo a
Values
:"PYTHON_ENABLE_WORKER_EXTENSIONS": "1"
Si lo ejecuta en Azure, en su lugar agregue
PYTHON_ENABLE_WORKER_EXTENSIONS=1
a la configuración de la aplicación en la aplicación de funciones.Agregue las dos líneas siguientes antes de la función
main
en el archivo __init.py__ para el modelo de programación v1 o en el archivo function_app.py para el modelo de programación v2:from python_worker_extension_timer import TimerExtension TimerExtension.configure(append_to_http_response=True)
Este código importa el módulo
TimerExtension
y establece el valor de configuraciónappend_to_http_response
.
Comprobación de la extensión
En la carpeta raíz del proyecto de aplicación, inicie el host de función mediante
func host start --verbose
. Debería ver el punto de conexión local de la función en la salida comohttps://localhost:7071/api/HttpTrigger
.En el explorador, envíe una solicitud GET a
https://localhost:7071/api/HttpTrigger
. Debería ver una respuesta como la siguiente, con los datos de TimeElapsed de la solicitud anexados.This HTTP triggered function executed successfully. Pass a name in the query string or in the request body for a personalized response. (TimeElapsed: 0.0009996891021728516 sec)
Publicación de la extensión
Después de crear y comprobar la extensión, debe completar las tareas de publicación siguientes:
- Elija una licencia.
- Cree un archivo readme.md y otra documentación.
- Publique la biblioteca de extensiones en un registro de paquetes de Python o en un sistema de control de versiones (VCS).
Para publicar la extensión en PyPI:
Ejecute el siguiente comando para instalar
twine
ywheel
en el entorno predeterminado de Python o en un entorno virtual:pip install twine wheel
Quite la carpeta
dist/
anterior del repositorio de extensiones.Ejecute el siguiente comando para generar un nuevo paquete en
dist/
:python setup.py sdist bdist_wheel
Ejecute el siguiente comando para cargar el paquete en PyPI:
twine upload dist/*
Es posible que tenga que proporcionar las credenciales de la cuenta de PyPI durante la carga. También puede probar la carga del paquete con
twine upload -r testpypi dist/*
. Para obtener más información, consulte la documentación de Twine.
Después de estos pasos, los clientes pueden usar la extensión si incluyen el nombre del paquete en el archivo requirements.txt.
Para obtener más información, consulte el tutorial oficial de empaquetado de Python.
Ejemplos
Puede ver cómo se ha completado el proyecto de extensión de ejemplo de este artículo en el repositorio de ejemplo python_worker_extension_timer.
La integración de OpenCensus es un proyecto de código abierto que usa la interfaz de extensión para integrar el seguimiento de telemetría en aplicaciones de Python para Azure Functions. Consulte el repositorio opencensus-python-extensions-azure para revisar la implementación de esta extensión de trabajo de Python.
Pasos siguientes
Para obtener más información sobre el desarrollo de Python de Azure Functions, consulte los siguientes recursos: