title | description | ms.topic | ms.date | ms.custom |
---|---|---|---|---|
Develop Python worker extensions for Azure Functions |
Learn how to create and publish worker extensions that let you inject middleware behavior into Python functions running in Azure. |
how-to |
6/1/2021 |
devx-track-python |
Azure Functions lets you integrate custom behaviors as part of Python function execution. This feature enables you to create business logic that customers can easily use in their own function apps. To learn more, see the Python developer reference.
In this tutorial, you'll learn how to:
[!div class="checklist"]
- Create an application-level Python worker extension for Azure Functions.
- Consume your extension in an app the way your customers do.
- Package and publish an extension for consumption.
Before you start, you must meet these requirements:
-
Python 3.6.x or above. To check the full list of supported Python versions in Azure Functions, see the Python developer guide.
-
The Azure Functions Core Tools, version 3.0.3568 or later.
-
Visual Studio Code installed on one of the supported platforms.
The extension you create reports the elapsed time of an HTTP trigger invocation in the console logs and in the HTTP response body.
The folder for your extension project should be like the following structure:
<python_worker_extension_root>/
| - .venv/
| - python_worker_extension_timer/
| | - __init__.py
| - setup.py
| - readme.md
Folder/file | Description |
---|---|
.venv/ | (Optional) Contains a Python virtual environment used for local development. |
python_worker_extension/ | Contains the source code of the Python worker extension. This folder contains the main Python module to be published into PyPI. |
setup.py | Contains the metadata of the Python worker extension package. |
readme.md | (Optional) Contains the instruction and usage of your extension. This content is displayed as the description in the home page in your PyPI project. |
First you create setup.py
, which provides essential information about your package. To make sure that your extension is distributed and integrated into your customer's function apps properly, confirm that 'azure-functions >= 1.7.0, < 2.0.0'
is in the install_requires
section.
In the following template, you should change author
, author_email
, install_requires
, license
, packages
, and url
fields as needed.
:::code language="python" source="~/azure-functions-python-worker-extension/setup.py":::
Next, you'll implement your extension code in the application-level scope.
Add the following code in python_worker_extension_timer/__init__.py
to implement the application-level extension:
:::code language="python" source="~/azure-functions-python-worker-extension/python_worker_extension_timer/init.py":::
This code inherits from AppExtensionBase so that the extension applies to every function in the app. You could have also implemented the extension on a function-level scope by inheriting from FuncExtensionBase.
The init
method is a class method that's called by the worker when the extension class is imported. You can do initialization actions here for the extension. In this case, a hash map is initialized for recording the invocation start time for each function.
The configure
method is customer-facing. In your readme file, you can 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. In this example, customers can choose whether the elapsed time is reported in the HttpResponse
.
The pre_invocation_app_level
method is called by the Python worker before the function runs. It provides the information from the function, such as function context and arguments. In this example, the extension logs a message and records the start time of an invocation based on its invocation_id.
Similarly, the post_invocation_app_level
is called after function execution. This example calculates the elapsed time based on the start time and current time. It also overwrites the return value of the HTTP response.
Now that you've created an extension, you can use it in an app project to verify it works as intended.
-
Create a new folder for your app project and navigate to it.
-
From the appropriate shell, such as Bash, run the following command to initialize the project:
func init --python
-
Use the following command to create a new HTTP trigger function that allows anonymous access:
func new -t HttpTrigger -n HttpTrigger -a anonymous
-
Create a Python virtual environment, based on OS as follows:
python3 -m venv .venv
py -m venv .venv
-
Activate the Python virtual environment, based on OS as follows:
source .venv/bin/activate
.venv\Scripts\Activate.ps1
-
Install remote packages for your function app project using the following command:
pip install -r requirements.txt
-
Install the extension from your local file path, in editable mode as follows:
pip install -e <PYTHON_WORKER_EXTENSION_ROOT>
In this example, replace
<PYTHON_WORKER_EXTENSION_ROOT>
with the file location of your extension project.
When a customer uses your extension, they'll instead add your extension package location to the requirements.txt file, as in the following examples:# requirements.txt python_worker_extension_timer==1.0.0
# requirements.txt git+https://github.com/Azure-Samples/python-worker-extension-timer@main
-
Open the local.settings.json project file and add the following field to
Values
:"PYTHON_ENABLE_WORKER_EXTENSIONS": "1"
When running in Azure, you instead add
PYTHON_ENABLE_WORKER_EXTENSIONS=1
to the app settings in the function app. -
Add following two lines before the
main
function in __init.py__:from python_worker_extension_timer import TimerExtension TimerExtension.configure(append_to_http_response=True)
This code imports the
TimerExtension
module and sets theappend_to_http_response
configuration value.
-
From your app project root folder, start the function host using
func host start --verbose
. You should see the local endpoint of your function in the output ashttps://localhost:7071/api/HttpTrigger
. -
In the browser, send a GET request to
https://localhost:7071/api/HttpTrigger
. You should see a response like the following, with the TimeElapsed data for the request appended.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)
After you've created and verified your extension, you still need to complete these remaining publishing tasks:
[!div class="checklist"]
- Choose a license.
- Create a readme.md and other documentation.
- Publish the extension library to a Python package registry or a version control system (VCS).
To publish your extension to PyPI:
-
Run the following command to install
twine
andwheel
in your default Python environment or a virtual environment:pip install twine wheel
-
Remove the old
dist/
folder from your extension repository. -
Run the following command to generate a new package inside
dist/
:python setup.py sdist bdist_wheel
-
Run the following command to upload the package to PyPI:
twine upload dist/*
You may need to provide your PyPI account credentials during upload.
After these steps, customers can use your extension by including your package name in their requirements.txt.
For more information, see the official Python packaging tutorial.
You can also publish the extension source code with the setup.py file to a GitHub repository, as shown in this sample repository.
For more information about VCS support in pip, see the official pip VCS support documentation.
-
You can view completed sample extension project from this article in the python_worker_extension_timer sample repository.
-
OpenCensus integration is an open-source project that uses the extension interface to integrate telemetry tracing in Azure Functions Python apps. See the opencensus-python-extensions-azure repository to review the implementation of this Python worker extension.
For more information about Azure Functions Python development, see the following resources: