Développer des extensions de Worker Python pour Azure Functions

Avec Azure Functions vous intégrez des comportements personnalisés dans le cadre de l’exécution d’une fonction Python. Cette fonctionnalité vous permet de créer une logique métier que les clients peuvent facilement utiliser dans leurs propres applications de fonction. Pour en savoir plus, consultez les Informations de référence pour les développeurs Python. Les extensions du Worker sont prises en charge dans les modèles de programmation Python v1 et v2.

Ce didacticiel vous montre comment effectuer les opérations suivantes :

  • Créer une extension de Worker Python au niveau de l’application pour Azure Functions.
  • Consommer votre extension dans une application de la même façon que vos clients.
  • Empaqueter et publier une extension pour la consommation.

Prérequis

Avant de commencer, vous devez satisfaire aux exigences suivantes :

Créer l’extension de Worker Python

L’extension que vous créez signale le temps écoulé d’un appel de déclencheur HTTP dans les journaux de la console et dans le corps de la réponse HTTP.

Structure de dossiers

Le dossier de votre extension de projet doit suivre la structure suivante :

<python_worker_extension_root>/
 | - .venv/
 | - python_worker_extension_timer/
 | | - __init__.py
 | - setup.py
 | - readme.md
Dossier\fichier Description
.venv/ (Facultatif) Contient un environnement virtuel Python utilisé pour le développement local.
python_worker_extension/ Contient le code source de l’extension de Worker Python. Ce dossier contient le module Python principal à publier dans PyPI.
setup.py Contient les métadonnées du package d’extension de Worker Python.
readme.md Contient l’instruction et l’utilisation de votre extension. Ce contenu est affiché comme descriptif dans la page d’accueil de votre projet PyPI.

Configurer les métadonnées du projet

Tout d’abord, vous devez créer setup.py, qui fournit des informations essentielles sur votre package. Pour vérifier que votre extension est distribuée et intégrée correctement dans les applications de fonction de votre client, vérifiez que 'azure-functions >= 1.7.0, < 2.0.0' figure dans la section install_requires.

Dans le modèle suivant, vous devez modifier les champs author, author_email, install_requires, license, packages et url en fonction des besoins.

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,
)

Ensuite, vous implémentez votre code d’extension dans l’étendue du niveau application.

Implémenter l’extension de minuterie

Ajoutez le code suivant dans python_worker_extension_timer/__init__.py pour implémenter l’extension au niveau de l’application :

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()

Ce code hérite de AppExtensionBase, afin que l’extension s’applique à chaque fonction de l’application. Vous pouvez également implémenter l’extension sur une étendue au niveau fonction en héritant de FuncExtensionBase.

La méthode init est une méthode de classe appelée par le Worker lors de l’importation de la classe d’extension. Vous pouvez effectuer des actions d’initialisation ici pour l’extension. Dans ce cas, un mappage de hachage est initialisé pour l’enregistrement de l’heure de début d’appel de chaque fonction.

La méthode configure est destinée aux clients. Dans votre fichier readme, vous pouvez indiquer à vos clients le moment où ils doivent appeler Extension.configure(). Ce fichier doit également documenter les fonctionnalités de l’extension, la configuration possible et l’utilisation de votre extension. Dans cet exemple, les clients peuvent choisir si le temps écoulé est signalé dans HttpResponse.

La méthode pre_invocation_app_level est appelée par le Worker Python avant l’exécution de la fonction. Elle fournit des informations sur la fonction, telles que les arguments et le contexte de la fonction. Dans cet exemple, l’extension journalise un message et enregistre l’heure de début d’un appel en fonction de son invocation_id.

De même, post_invocation_app_level est appelé après l’exécution de la fonction. Cet exemple calcule le temps écoulé en fonction de l’heure de début et de l’heure actuelle. La valeur de retour de la réponse HTTP est également remplacée.

Créer un fichier README.md

Créez un fichier README.md à la racine de votre projet d’extension. Ce fichier contient les instructions et le guide d’utilisation de votre extension. Ce contenu du fichier README.md s’affiche en tant que description dans la page d’accueil de votre projet 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.

Utiliser votre extension localement

À présent que vous avez créé une extension, vous pouvez l’utiliser dans un projet d’application pour vérifier qu’elle fonctionne comme prévu.

Créer une fonction de déclencheur HTTP

  1. Créez un dossier pour votre projet d’application et accédez-y.

  2. À partir de l’interpréteur de commandes approprié, tel que Bash, exécutez la commande suivante pour initialiser le projet :

    func init --python
    
  3. Utilisez la commande suivante pour créer une nouvelle fonction de déclencheur HTTP qui autorise l’accès anonyme :

    func new -t HttpTrigger -n HttpTrigger -a anonymous
    

Activer un environnement virtuel

  1. Créez un environnement virtuel Python basé sur le système d’exploitation comme suit :

    python3 -m venv .venv
    
  2. Activez l’environnement virtuel Python selon le système d’exploitation comme suit :

    source .venv/bin/activate
    

Configurer l’extension

  1. Installez les packages distants de votre projet d’application de fonction à l’aide de la commande suivante :

    pip install -r requirements.txt
    
  2. Installez l’extension à partir de votre chemin de fichier local, en mode modifiable comme suit :

    pip install -e <PYTHON_WORKER_EXTENSION_ROOT>
    

    Dans cet exemple, remplacez <PYTHON_WORKER_EXTENSION_ROOT> par l’emplacement du fichier racine de votre projet d’extension.

    Lorsqu’un client utilise votre extension, il ajoute à la place l’emplacement de votre package d’extension au fichier requirements.txt, comme dans les exemples suivants :

    # requirements.txt
    python_worker_extension_timer==1.0.0
    
  3. Ouvrez le fichier de projet local.settings.json et ajoutez le champ suivant à Values :

    "PYTHON_ENABLE_WORKER_EXTENSIONS": "1" 
    

    Lors de l’exécution dans Azure, vous ajoutez à la place PYTHON_ENABLE_WORKER_EXTENSIONS=1 aux paramètres de l’application dans l’application de fonction.

  4. Ajoutez les deux lignes suivantes avant la fonction main dans le fichier __init.py__ pour le modèle de programmation v1, ou dans le fichier function_app.py pour le modèle de programmation v2 :

    from python_worker_extension_timer import TimerExtension
    TimerExtension.configure(append_to_http_response=True)
    

    Ce code importe le module TimerExtension et définit la valeur de configuration append_to_http_response.

Vérifier l’extension

  1. À partir du dossier racine de votre projet d’application, démarrez l’hôte de la fonction à l’aide de func host start --verbose. Vous devez voir le point de terminaison local de votre fonction dans la sortie, sous la forme https://localhost:7071/api/HttpTrigger.

  2. Dans le navigateur, envoyez une requête GET à https://localhost:7071/api/HttpTrigger. Vous devez voir une réponse semblable à la suivante, avec les données TimeElapsed de la requête ajoutée.

    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)
    

Publier votre extension

Une fois que vous avez créé et vérifié votre extension, vous devez encore effectuer ces tâches de publication restantes :

  • Choisir une licence.
  • Créer un fichier readme.md et d’autres documentations.
  • Publier la bibliothèque d’extensions dans un registre de packages Python ou un système de contrôle de version (VCS).

Pour publier votre extension sur PyPI :

  1. Exécutez la commande suivante pour installer twine et wheel dans votre environnement Python par défaut ou dans un environnement virtuel :

    pip install twine wheel
    
  2. Supprimez l’ancien dossier dist/ de votre référentiel d’extensions.

  3. Exécutez la commande suivante pour générer un nouveau package à l’intérieur de dist/ :

    python setup.py sdist bdist_wheel
    
  4. Exécutez la commande suivante pour charger le package sur PyPI :

    twine upload dist/*
    

    Vous devrez peut-être fournir les informations d’identification de votre compte PyPI lors du chargement. Vous pouvez également tester le chargement de votre package avec twine upload -r testpypi dist/*. Pour plus d’informations, consultez la documentation Twine.

Après ces étapes, les clients peuvent utiliser votre extension en incluant le nom de votre package dans leur fichier requirements.txt.

Pour plus d’informations, consultez le tutoriel officiel Python sur l’empaquetage.

Exemples

  • Vous pouvez afficher l’exemple du projet d’extension terminé à partir de cet article dans l’exemple de dépôt python_worker_extension_timer.

  • L’intégration de OpenCensus est un projet open source qui utilise l’interface d’extension pour intégrer le suivi de télémétrie dans les applications Python sur Azure Functions. Consultez le dépôt opencensus-python-extensions-azure pour vérifier l’implémentation de cette extension de Worker Python.

Étapes suivantes

Pour plus d’informations sur le développement Python Azure Functions, consultez les ressources suivantes :