This browser is no longer supported.
Upgrade to Microsoft Edge to take advantage of the latest features, security updates, and technical support.
Note
Access to this page requires authorization. You can trysigning in orchanging directories.
Access to this page requires authorization. You can trychanging directories.
This guide is an introduction to developing Azure Functions by using Python. The article assumes that you've already read theAzure Functions developers guide.
Important
This article supports both the v1 and v2 programming model for Python in Azure Functions.The Python v1 model uses afunctions.json file to define functions, and the new v2 model lets you instead use a decorator-based approach. This new approach results in a simpler file structure, and it's more code-centric. Choose thev2 selector at the top of the article to learn about this new programming model.
As a Python developer, you might also be interested in these topics:
Both Python Functions programming models support local development in one of the following environments:
Python v2 programming model:
Python v1 programming model:
You can also create Python functions in the Azure portal.
Tip
Although you can develop your Python-based Azure functions locally on Windows, Python is supported only on a Linux-based hosting plan when it's running in Azure. For more information, see thelist of supported operating system/runtime combinations.
Azure Functions expects a function to be a stateless method in your Python script that processes input and produces output. By default, the runtime expects the method to be implemented as a global method calledmain()
in the__init__.py file. You can alsospecify an alternative entry point.
You bind data to the function from triggers and bindings via method attributes that use thename
property that's defined in thefunction.json file. For example, the followingfunction.json file describes a simple function that's triggered by an HTTP request namedreq
:
{ "scriptFile": "__init__.py", "bindings": [ { "authLevel": "function", "type": "httpTrigger", "direction": "in", "name": "req", "methods": [ "get", "post" ] }, { "type": "http", "direction": "out", "name": "$return" } ]}
Based on this definition, the__init__.py file that contains the function code might look like the following example:
def main(req): user = req.params.get('user') return f'Hello, {user}!'
You can also explicitly declare the attribute types and return type in the function by using Python type annotations. Doing so helps you to use the IntelliSense and autocomplete features that are provided by many Python code editors.
import azure.functionsdef main(req: azure.functions.HttpRequest) -> str: user = req.params.get('user') return f'Hello, {user}!'
Use the Python annotations that are included in theazure.functions.* package to bind the input and outputs to your methods.
Azure Functions expects a function to be a stateless method in your Python script that processes input and produces output. By default, the runtime expects the method to be implemented as a global method in thefunction_app.py file.
Triggers and bindings can be declared and used in a function in a decorator based approach. They're defined in the same file,function_app.py, as the functions. As an example, the followingfunction_app.py file represents a function trigger by an HTTP request.
@app.function_name(name="HttpTrigger1")@app.route(route="req")def main(req): user = req.params.get("user") return f"Hello, {user}!"
You can also explicitly declare the attribute types and return type in the function by using Python type annotations. Doing so helps you use the IntelliSense and autocomplete features that are provided by many Python code editors.
import azure.functions as funcapp = func.FunctionApp()@app.function_name(name="HttpTrigger1")@app.route(route="req")def main(req: func.HttpRequest) -> str: user = req.params.get("user") return f"Hello, {user}!"
To learn about known limitations with the v2 model and their workarounds, seeTroubleshoot Python errors in Azure Functions.
You can change the default behavior of a function by optionally specifying thescriptFile
andentryPoint
properties in thefunction.json file. For example, the followingfunction.json tells the runtime to use thecustomentry()
method in themain.py file as the entry point for your Azure function.
{ "scriptFile": "main.py", "entryPoint": "customentry", "bindings": [ ... ]}
The entry point is only in thefunction_app.py file. However, you can reference functions within the project infunction_app.py by usingblueprints or by importing.
The recommended folder structure for a Python functions project looks like the following example:
<project_root>/ | - .venv/ | - .vscode/ | - my_first_function/ | | - __init__.py | | - function.json | | - example.py | - my_second_function/ | | - __init__.py | | - function.json | - shared_code/ | | - __init__.py | | - my_first_helper_function.py | | - my_second_helper_function.py | - tests/ | | - test_my_second_function.py | - .funcignore | - host.json | - local.settings.json | - requirements.txt | - Dockerfile
The main project folder,<project_root>, can contain the following files:
Each function has its own code file and binding configuration file,function.json.
The recommended folder structure for a Python functions project looks like the following example:
<project_root>/ | - .venv/ | - .vscode/ | - function_app.py | - additional_functions.py | - tests/ | | - test_my_function.py | - .funcignore | - host.json | - local.settings.json | - requirements.txt | - Dockerfile
The main project folder,<project_root>, can contain the following files:
When you deploy your project to a function app in Azure, the entire contents of the main project folder,<project_root>, should be included in the package, but not the folder itself, which means thathost.json should be in the package root. We recommend that you maintain your tests in a folder along with other functions (in this example,tests/). For more information, seeUnit testing.
Azure Functions integrates well withAzure Cosmos DB for manyuse cases, including IoT, ecommerce, gaming, etc.
For example, forevent sourcing, the two services are integrated to power event-driven architectures using Azure Cosmos DB'schange feed functionality. The change feed provides downstream microservices the ability to reliably and incrementally read inserts and updates (for example, order events). This functionality can be used to provide a persistent event store as a message broker for state-changing events and drive order processing workflow between many microservices (which can be implemented asserverless Azure Functions).
To connect to Azure Cosmos DB, firstcreate an account, database, and container. Then you can connect your function code to Azure Cosmos DB usingtrigger and bindings, like thisexample.
To implement more complex app logic, you can also use the Python library for Cosmos DB. An asynchronous I/O implementation looks like this:
pip install azure-cosmospip install aiohttpfrom azure.cosmos.aio import CosmosClientfrom azure.cosmos import exceptionsfrom azure.cosmos.partition_key import PartitionKeyimport asyncio# Replace these values with your Cosmos DB connection informationendpoint = "https://azure-cosmos-nosql.documents.azure.com:443/"key = "master_key"database_id = "cosmicwerx"container_id = "cosmicontainer"partition_key = "/partition_key"# Set the total throughput (RU/s) for the database and containerdatabase_throughput = 1000# Singleton CosmosClient instanceclient = CosmosClient(endpoint, credential=key)# Helper function to get or create database and containerasync def get_or_create_container(client, database_id, container_id, partition_key): database = await client.create_database_if_not_exists(id=database_id) print(f'Database "{database_id}" created or retrieved successfully.') container = await database.create_container_if_not_exists(id=container_id, partition_key=PartitionKey(path=partition_key)) print(f'Container with id "{container_id}" created') return container async def create_products(): container = await get_or_create_container(client, database_id, container_id, partition_key) for i in range(10): await container.upsert_item({ 'id': f'item{i}', 'productName': 'Widget', 'productModel': f'Model {i}' }) async def get_products(): items = [] container = await get_or_create_container(client, database_id, container_id, partition_key) async for item in container.read_all_items(): items.append(item) return itemsasync def query_products(product_name): container = await get_or_create_container(client, database_id, container_id, partition_key) query = f"SELECT * FROM c WHERE c.productName = '{product_name}'" items = [] async for item in container.query_items(query=query, enable_cross_partition_query=True): items.append(item) return itemsasync def main(): await create_products() all_products = await get_products() print('All Products:', all_products) queried_products = await query_products('Widget') print('Queried Products:', queried_products)if __name__ == "__main__": asyncio.run(main())
The Python v2 programming model introduces the concept ofblueprints. A blueprint is a new class that's instantiated to register functions outside of the core function application. The functions registered in blueprint instances aren't indexed directly by the function runtime. To get these blueprint functions indexed, the function app needs to register the functions from blueprint instances.
Using blueprints provides the following benefits:
The following example shows how to use blueprints:
First, in anhttp_blueprint.py file, an HTTP-triggered function is first defined and added to a blueprint object.
import logging import azure.functions as func bp = func.Blueprint() @bp.route(route="default_template") def default_template(req: func.HttpRequest) -> func.HttpResponse: logging.info('Python HTTP trigger function processed a request.') name = req.params.get('name') if not name: try: req_body = req.get_json() except ValueError: pass else: name = req_body.get('name') if name: return func.HttpResponse( f"Hello, {name}. This HTTP-triggered function " f"executed successfully.") else: return func.HttpResponse( "This HTTP-triggered function executed successfully. " "Pass a name in the query string or in the request body for a" " personalized response.", status_code=200 )
Next, in thefunction_app.py file, the blueprint object is imported and its functions are registered to the function app.
import azure.functions as func from http_blueprint import bpapp = func.FunctionApp() app.register_functions(bp)
Note
Durable Functions also supports blueprints. To create blueprints for Durable Functions apps, register your orchestration, activity, and entity triggers and client bindings using theazure-functions-durable
Blueprint
class, asshownhere. The resulting blueprint can then be registered as normal. See oursample for an example.
You can import modules in your function code by using both absolute and relative references. Based on the previously described folder structure, the following imports work from within the function file<project_root>\my_first_function\__init__.py:
from shared_code import my_first_helper_function #(absolute)
import shared_code.my_second_helper_function #(absolute)
from . import example #(relative)
Note
When you're using absolute import syntax, theshared_code/ folder needs to contain an__init__.py file to mark it as a Python package.
The following __app__ import and beyond top-level relative import are deprecated, because they're not supported by the static type checker and not supported by Python test frameworks:
from __app__.shared_code import my_first_helper_function #(deprecated __app__ import)
from ..shared_code import my_first_helper_function #(deprecated beyond top-level relative import)
Inputs are divided into two categories in Azure Functions: trigger input and other input. Although they're different in thefunction.json file, their usage is identical in Python code. Connection strings or secrets for trigger and input sources map to values in thelocal.settings.json file when they're running locally, and they map to the application settings when they're running in Azure.
For example, the following code demonstrates the difference between the two inputs:
// function.json{ "scriptFile": "__init__.py", "bindings": [ { "name": "req", "direction": "in", "type": "httpTrigger", "authLevel": "anonymous", "route": "items/{id}" }, { "name": "obj", "direction": "in", "type": "blob", "path": "samples/{id}", "connection": "STORAGE_CONNECTION_STRING" } ]}
// local.settings.json{ "IsEncrypted": false, "Values": { "FUNCTIONS_WORKER_RUNTIME": "python", "STORAGE_CONNECTION_STRING": "<AZURE_STORAGE_CONNECTION_STRING>", "AzureWebJobsStorage": "<azure-storage-connection-string>" }}
# __init__.pyimport azure.functions as funcimport loggingdef main(req: func.HttpRequest, obj: func.InputStream): logging.info(f'Python HTTP-triggered function processed: {obj.read()}')
When the function is invoked, the HTTP request is passed to the function asreq
. An entry is retrieved from the Azure Blob Storage account based on theID in the route URL and made available asobj
in the function body. Here, the specified storage account is the connection string that's found in the<*_CONNECTION_STRING>
app setting. For more information, see For more information, seeConnections.
Inputs are divided into two categories in Azure Functions: trigger input and other input. Although they're defined using different decorators, their usage is similar in Python code. Connection strings or secrets for trigger and input sources map to values in thelocal.settings.json file when they're running locally, and they map to the application settings when they're running in Azure.
As an example, the following code demonstrates how to define a Blob Storage input binding:
// local.settings.json{ "IsEncrypted": false, "Values": { "FUNCTIONS_WORKER_RUNTIME": "python", "STORAGE_CONNECTION_STRING": "<AZURE_STORAGE_CONNECTION_STRING>", "AzureWebJobsStorage": "<azure-storage-connection-string>" }}
# function_app.pyimport azure.functions as funcimport loggingapp = func.FunctionApp()@app.route(route="req")@app.blob_input(arg_name="obj", path="samples/{id}", connection="STORAGE_CONNECTION_STRING")def main(req: func.HttpRequest, obj: func.InputStream): logging.info(f'Python HTTP-triggered function processed: {obj.read()}')
When the function is invoked, the HTTP request is passed to the function asreq
. An entry is retrieved from the Azure Blob Storage account based on theID in the route URL and made available asobj
in the function body. Here, the specified storage account is the connection string found in the<*_CONNECTION_STRING>
app setting. For more information, see For more information, seeConnections.
For data intensive binding operations, you may want to use a separate storage account. For more information, seeStorage account guidance.
For select triggers and bindings, you can work with data types implemented by the underlying Azure SDKs and frameworks. TheseSDK type bindings let you interact binding data as if you were using the underlying service SDK.
`> [!IMPORTANT]
Using SDK type bindings requires thePython v2 programming model.
Important
SDK type bindings support for Python is only supported in the Python v2 programming model.
Service | Trigger | Input binding | Output binding | Samples |
---|---|---|---|---|
Azure Blobs | Generally available | Generally available | SDK types not recommended.1 | Quickstart,BlobClient ,ContainerClient ,StorageStreamDownloader |
Azure Cosmos DB | SDK types not used2 | Preview | SDK types not recommended.1 | Quickstart,ContainerProxy ,CosmosClient ,DatabaseProxy |
Azure Event Hubs | Preview | Input binding doesn't exist | SDK types not recommended.1 | Quickstart,EventData |
Azure Service Bus | Preview | Input binding doesn't exist | SDK types not recommended.1 | Quickstart,ServiceBusReceivedMessage |
1 For output scenarios in which you would use an SDK type, you should create and work with SDK clients directly instead of using an output binding.2 The Cosmos DB trigger uses theAzure Cosmos DB change feed and exposes change feed items as JSON-serializable types. The absence of SDK types is by-design for this scenario.
HTTP streams lets you accept and return data from your HTTP endpoints using FastAPI request and response APIs enabled in your functions. These APIs lets the host process large data in HTTP messages as chunks instead of reading an entire message into memory.
This feature makes it possible to handle large data stream, OpenAI integrations, deliver dynamic content, and support other core HTTP scenarios requiring real-time interactions over HTTP. You can also use FastAPI response types with HTTP streams. Without HTTP streams, the size of your HTTP requests and responses are limited by memory restrictions that can be encountered when processing entire message payloads all in memory.
To learn more, including how to enable HTTP streams in your project, seeHTTP Streams.
Important
Support for HTTP streams requires thePython v2 programming model.
Important
HTTP streams support for Python is generally available and requires you to use the Python v2 programming model.
Output can be expressed both in return value and output parameters. If there's only one output, we recommend using the return value. For multiple outputs, you must use output parameters.
To use the return value of a function as the value of an output binding, thename
property of the binding should be set to$return
in thefunction.json file.
To produce multiple outputs, use theset()
method provided by theazure.functions.Out
interface to assign a value to the binding. For example, the following function can push a message to a queue and also return an HTTP response.
{ "scriptFile": "__init__.py", "bindings": [ { "name": "req", "direction": "in", "type": "httpTrigger", "authLevel": "anonymous" }, { "name": "msg", "direction": "out", "type": "queue", "queueName": "outqueue", "connection": "STORAGE_CONNECTION_STRING" }, { "name": "$return", "direction": "out", "type": "http" } ]}
import azure.functions as funcdef main(req: func.HttpRequest, msg: func.Out[func.QueueMessage]) -> str: message = req.params.get('body') msg.set(message) return message
Output can be expressed both in return value and output parameters. If there's only one output, we recommend using the return value. For multiple outputs, you'll have to use output parameters.
To produce multiple outputs, use theset()
method provided by theazure.functions.Out
interface to assign a value to the binding. For example, the following function can push a message to a queue and also return an HTTP response.
# function_app.pyimport azure.functions as funcapp = func.FunctionApp()@app.write_blob(arg_name="msg", path="output-container/{name}", connection="CONNECTION_STRING")def test_function(req: func.HttpRequest, msg: func.Out[str]) -> str: message = req.params.get('body') msg.set(message) return message
Access to the Azure Functions runtime logger is available via a rootlogging
handler in your function app. This logger is tied to Application Insights and allows you to flag warnings and errors that occur during the function execution.
The following example logs an info message when the function is invoked via an HTTP trigger.
import loggingdef main(req): logging.info('Python HTTP trigger function processed a request.')
More logging methods are available that let you write to the console at different trace levels:
Method | Description |
---|---|
critical(_message_) | Writes a message with level CRITICAL on the root logger. |
error(_message_) | Writes a message with level ERROR on the root logger. |
warning(_message_) | Writes a message with level WARNING on the root logger. |
info(_message_) | Writes a message with level INFO on the root logger. |
debug(_message_) | Writes a message with level DEBUG on the root logger. |
To learn more about logging, seeMonitor Azure Functions.
To see logs coming from your created threads, include thecontext
argument in the function's signature. This argument contains an attributethread_local_storage
that stores a localinvocation_id
. This can be set to the function's currentinvocation_id
to ensure the context is changed.
import azure.functions as funcimport loggingimport threadingdef main(req, context): logging.info('Python HTTP trigger function processed a request.') t = threading.Thread(target=log_function, args=(context,)) t.start()def log_function(context): context.thread_local_storage.invocation_id = context.invocation_id logging.info('Logging from thread.')
By default, the Functions runtime collects logs and other telemetry data that are generated by your functions. This telemetry ends up as traces in Application Insights. Request and dependency telemetry for certain Azure services are also collected by default bytriggers and bindings.
To collect custom request and custom dependency telemetry outside of bindings, you can use theOpenCensus Python Extensions. This extension sends custom telemetry data to your Application Insights instance. You can find a list of supported extensions at theOpenCensus repository.
Note
To use the OpenCensus Python extensions, you need to enablePython worker extensions in your function app by settingPYTHON_ENABLE_WORKER_EXTENSIONS
to1
. You also need to switch to using the Application Insights connection string by adding theAPPLICATIONINSIGHTS_CONNECTION_STRING
setting to yourapplication settings, if it's not already there.
// requirements.txt...opencensus-extension-azure-functionsopencensus-ext-requests
import jsonimport loggingimport requestsfrom opencensus.extension.azure.functions import OpenCensusExtensionfrom opencensus.trace import config_integrationconfig_integration.trace_integrations(['requests'])OpenCensusExtension.configure()def main(req, context): logging.info('Executing HttpTrigger with OpenCensus extension') # You must use context.tracer to create spans with context.tracer.span("parent"): response = requests.get(url='http://example.com') return json.dumps({ 'method': req.method, 'response': response.status_code, 'ctx_func_name': context.function_name, 'ctx_func_dir': context.function_directory, 'ctx_invocation_id': context.invocation_id, 'ctx_trace_context_Traceparent': context.trace_context.Traceparent, 'ctx_trace_context_Tracestate': context.trace_context.Tracestate, 'ctx_retry_context_RetryCount': context.retry_context.retry_count, 'ctx_retry_context_MaxRetryCount': context.retry_context.max_retry_count, })
The HTTP trigger is defined in thefunction.json file. Thename
of the binding must match the named parameter in the function.In the previous examples, a binding namereq
is used. This parameter is anHttpRequest object, and anHttpResponse object is returned.
From theHttpRequest object, you can get request headers, query parameters, route parameters, and the message body.
The following example is from theHTTP trigger template for Python.
def main(req: func.HttpRequest) -> func.HttpResponse: headers = {"my-http-header": "some-value"} name = req.params.get('name') if not name: try: req_body = req.get_json() except ValueError: pass else: name = req_body.get('name') if name: return func.HttpResponse(f"Hello {name}!", headers=headers) else: return func.HttpResponse( "Please pass a name on the query string or in the request body", headers=headers, status_code=400 )
In this function, you obtain the value of thename
query parameter from theparams
parameter of theHttpRequest object. You read the JSON-encoded message body by using theget_json
method.
Likewise, you can set thestatus_code
andheaders
for the response message in the returnedHttpResponse object.
The HTTP trigger is defined as a method that takes a named binding parameter, which is anHttpRequest object, and returns anHttpResponse object. You apply thefunction_name
decorator to the method to define the function name, while the HTTP endpoint is set by applying theroute
decorator.
This example is from the HTTP trigger template for the Python v2 programming model, where the binding parameter name isreq
. It's the sample code that's provided when you create a function by using Azure Functions Core Tools or Visual Studio Code.
@app.function_name(name="HttpTrigger1")@app.route(route="hello")def test_function(req: func.HttpRequest) -> func.HttpResponse: logging.info('Python HTTP trigger function processed a request.') name = req.params.get('name') if not name: try: req_body = req.get_json() except ValueError: pass else: name = req_body.get('name') if name: return func.HttpResponse(f"Hello, {name}. This HTTP-triggered function executed successfully.") else: return func.HttpResponse( "This HTTP-triggered function executed successfully. Pass a name in the query string or in the request body for a personalized response.", status_code=200 )
From theHttpRequest object, you can get request headers, query parameters, route parameters, and the message body. In this function, you obtain the value of thename
query parameter from theparams
parameter of theHttpRequest object. You read the JSON-encoded message body by using theget_json
method.
Likewise, you can set thestatus_code
andheaders
for the response message in the returnedHttpResponse object.
To pass in a name in this example, paste the URL that's provided when you're running the function, and then append it with"?name={name}"
.
You can use Web Server Gateway Interface (WSGI)-compatible and Asynchronous Server Gateway Interface (ASGI)-compatible frameworks, such as Flask and FastAPI, with your HTTP-triggered Python functions. This section shows how to modify your functions to support these frameworks.
First, thefunction.json file must be updated to include aroute
in the HTTP trigger, as shown in the following example:
{ "scriptFile": "__init__.py", "bindings": [ { "authLevel": "anonymous", "type": "httpTrigger", "direction": "in", "name": "req", "methods": [ "get", "post" ], "route": "{*route}" }, { "type": "http", "direction": "out", "name": "$return" } ]}
Thehost.json file must also be updated to include an HTTProutePrefix
, as shown in the following example:
{ "version": "2.0", "logging": { "applicationInsights": { "samplingSettings": { "isEnabled": true, "excludedTypes": "Request" } } }, "extensionBundle": { "id": "Microsoft.Azure.Functions.ExtensionBundle", "version": "[4.*, 5.0.0)" }, "extensions": { "http": { "routePrefix": "" } }}
Update the Python code fileinit.py, depending on the interface that's used by your framework. The following example shows either an ASGI handler approach or a WSGI wrapper approach for Flask:
You can use Asynchronous Server Gateway Interface (ASGI)-compatible and Web Server Gateway Interface (WSGI)-compatible frameworks, such as Flask and FastAPI, with your HTTP-triggered Python functions. You must first update thehost.json file to include an HTTProutePrefix
, as shown in the following example:
{ "version": "2.0", "logging": { "applicationInsights": { "samplingSettings": { "isEnabled": true, "excludedTypes": "Request" } } }, "extensionBundle": { "id": "Microsoft.Azure.Functions.ExtensionBundle", "version": "[4.*, 5.0.0)" }, "extensions": { "http": { "routePrefix": "" } }}
The framework code looks like the following example:
AsgiFunctionApp
is the top-level function app class for constructing ASGI HTTP functions.
# function_app.pyimport azure.functions as func from fastapi import FastAPI, Request, Response fast_app = FastAPI() @fast_app.get("/return_http_no_body") async def return_http_no_body(): return Response(content="", media_type="text/plain") app = func.AsgiFunctionApp(app=fast_app, http_auth_level=func.AuthLevel.ANONYMOUS)
For scaling and performance best practices for Python function apps, see thePython scaling and performance article.
To get the invocation context of a function when it's running, include thecontext
argument in its signature.
For example:
import azure.functionsdef main(req: azure.functions.HttpRequest, context: azure.functions.Context) -> str: return f'{context.invocation_id}'
TheContext
class has the following string attributes:
Attribute | Description |
---|---|
function_directory | The directory in which the function is running. |
function_name | The name of the function. |
invocation_id | The ID of the current function invocation. |
thread_local_storage | The thread local storage of the function. Contains a localinvocation_id forlogging from created threads. |
trace_context | The context for distributed tracing. For more information, seeTrace Context . |
retry_context | The context for retries to the function. For more information, seeretry-policies . |
It isn't guaranteed that the state of your app will be preserved for future executions. However, the Azure Functions runtime often reuses the same process for multiple executions of the same app. To cache the results of an expensive computation, declare it as a global variable.
CACHED_DATA = Nonedef main(req): global CACHED_DATA if CACHED_DATA is None: CACHED_DATA = load_json() # ... use CACHED_DATA in code
In Azure Functions,application settings, such as service connection strings, are exposed as environment variables when they're running. There are two main ways to access these settings in your code.
Method | Description |
---|---|
os.environ["myAppSetting"] | Tries to get the application setting by key name, and raises an error when it's unsuccessful. |
os.getenv("myAppSetting") | Tries to get the application setting by key name, and returnsNone when it's unsuccessful. |
Both of these ways require you to declareimport os
.
The following example usesos.environ["myAppSetting"]
to get theapplication setting, with the key namedmyAppSetting
:
import loggingimport osimport azure.functions as funcdef main(req: func.HttpRequest) -> func.HttpResponse: # Get the setting named 'myAppSetting' my_app_setting_value = os.environ["myAppSetting"] logging.info(f'My app setting value:{my_app_setting_value}')
For local development, application settings aremaintained in thelocal.settings.json file.
In Azure Functions,application settings, such as service connection strings, are exposed as environment variables when they're running. There are two main ways to access these settings in your code.
Method | Description |
---|---|
os.environ["myAppSetting"] | Tries to get the application setting by key name, and raises an error when it's unsuccessful. |
os.getenv("myAppSetting") | Tries to get the application setting by key name, and returnsNone when it's unsuccessful. |
Both of these ways require you to declareimport os
.
The following example usesos.environ["myAppSetting"]
to get theapplication setting, with the key namedmyAppSetting
:
import loggingimport osimport azure.functions as funcapp = func.FunctionApp()@app.function_name(name="HttpTrigger1")@app.route(route="req")def main(req: func.HttpRequest) -> func.HttpResponse: # Get the setting named 'myAppSetting' my_app_setting_value = os.environ["myAppSetting"] logging.info(f'My app setting value:{my_app_setting_value}')
For local development, application settings aremaintained in thelocal.settings.json file.
Azure Functions supports the following Python versions:
Functions version | Python* versions |
---|---|
4.x | 3.12 3.11 3.10 |
* Official Python distributions
To request a specific Python version when you create your function app in Azure, use the--runtime-version
option of theaz functionapp create
command. The Functions runtime version is set by the--functions-version
option. The Python version is set when the function app is created, and it can't be changed for apps running in a Consumption plan.
The runtime uses the available Python version when you run it locally.
To set a Python function app to a specific language version, you need to specify the language and the version of the language in theLinuxFxVersion
field in the site configuration. For example, to change the Python app to use Python 3.12, setlinuxFxVersion
topython|3.12
.
To learn how to view and change thelinuxFxVersion
site setting, seeHow to target Azure Functions runtime versions.
For more general information, see theAzure Functions runtime support policy andSupported languages in Azure Functions.
When you're developing locally by using Core Tools or Visual Studio Code, add the names and versions of the required packages to therequirements.txt file, and then install them by usingpip
.
For example, you can use the followingrequirements.txt file andpip
command to install therequests
package from PyPI.
requests==2.19.1
pip install -r requirements.txt
When running your functions in anApp Service plan, dependencies that you define in requirements.txt are given precedence over built-in Python modules, such aslogging
. This precedence can cause conflicts when built-in modules have the same names as directories in your code. When running in aConsumption plan or anElastic Premium plan, conflicts are less likely because your dependencies aren't prioritized by default.
To prevent issues running in an App Service plan, don't name your directories the same as any Python native modules and don't include Python native libraries in your project's requirements.txt file.
When you're ready to publish, make sure that all your publicly available dependencies are listed in therequirements.txt file. You can locate this file at the root of your project directory.
You can find the project files and folders that are excluded from publishing, including the virtual environment folder, in the root directory of your project.
There are three build actions supported for publishing your Python project to Azure: remote build, local build, and builds using custom dependencies.
You can also use Azure Pipelines to build your dependencies and publish by using continuous delivery (CD). To learn more, seeContinuous delivery with Azure Pipelines.
When you use remote build, dependencies that are restored on the server and native dependencies match the production environment. This results in a smaller deployment package to upload. Use remote build when you're developing Python apps on Windows. If your project has custom dependencies, you canuse remote build with extra index URL.
Dependencies are obtained remotely based on the contents of therequirements.txt file.Remote build is the recommended build method. By default, Core Tools requests a remote build when you use the followingfunc azure functionapp publish
command to publish your Python project to Azure.
func azure functionapp publish <APP_NAME>
Remember to replace<APP_NAME>
with the name of your function app in Azure.
TheAzure Functions Extension for Visual Studio Code also requests a remote build by default.
Dependencies are obtained locally based on the contents of therequirements.txt file. You can prevent doing a remote build by using the followingfunc azure functionapp publish
command to publish with a local build:
func azure functionapp publish <APP_NAME> --build local
Remember to replace<APP_NAME>
with the name of your function app in Azure.
When you use the--build local
option, project dependencies are read from therequirements.txt file, and those dependent packages are downloaded and installed locally. Project files and dependencies are deployed from your local computer to Azure. This results in a larger deployment package being uploaded to Azure. If for some reason you can't get therequirements.txt file by using Core Tools, you must use the custom dependencies option for publishing.
We don't recommend using local builds when you're developing locally on Windows.
When your project has dependencies that aren't found in thePython Package Index, there are two ways to build the project. The first way, thebuild method, depends on how you build the project.
When your packages are available from an accessible custom package index, use a remote build. Before you publish, be sure tocreate an app setting namedPIP_EXTRA_INDEX_URL
. The value for this setting is the URL of your custom package index. Using this setting tells the remote build to runpip install
by using the--extra-index-url
option. To learn more, see thePythonpip install
documentation.
You can also use basic authentication credentials with your extra package index URLs. To learn more, seeBasic authentication credentials in the Python documentation.
If your project uses packages that aren't publicly available to our tools, you can make them available to your app by putting them in the__app__/.python_packages directory. Before you publish, run the following command to install the dependencies locally:
pip install --target="<PROJECT_DIR>/.python_packages/lib/site-packages" -r requirements.txt
When you're using custom dependencies, you should use the--no-build
publishing option, because you've already installed the dependencies into the project folder.
func azure functionapp publish <APP_NAME> --no-build
Remember to replace<APP_NAME>
with the name of your function app in Azure.
Functions that are written in Python can be tested like other Python code by using standard testing frameworks. For most bindings, it's possible to create a mock input object by creating an instance of an appropriate class from theazure.functions
package. Since theazure.functions
package isn't immediately available, be sure to install it via yourrequirements.txt file as described in thepackage management section above.
Withmy_second_function as an example, the following is a mock test of an HTTP-triggered function:
First, create a<project_root>/my_second_function/function.json file, and then define this function as an HTTP trigger.
{ "scriptFile": "__init__.py", "entryPoint": "main", "bindings": [ { "authLevel": "function", "type": "httpTrigger", "direction": "in", "name": "req", "methods": [ "get", "post" ] }, { "type": "http", "direction": "out", "name": "$return" } ]}
Next, you can implementmy_second_function
andshared_code.my_second_helper_function
.
# <project_root>/my_second_function/__init__.pyimport azure.functions as funcimport logging# Use absolute import to resolve shared_code modulesfrom shared_code import my_second_helper_function# Define an HTTP trigger that accepts the ?value=<int> query parameter# Double the value and return the result in HttpResponsedef main(req: func.HttpRequest) -> func.HttpResponse: logging.info('Executing my_second_function.') initial_value: int = int(req.params.get('value')) doubled_value: int = my_second_helper_function.double(initial_value) return func.HttpResponse( body=f"{initial_value} * 2 = {doubled_value}", status_code=200 )
# <project_root>/shared_code/__init__.py# Empty __init__.py file marks shared_code folder as a Python package
# <project_root>/shared_code/my_second_helper_function.pydef double(value: int) -> int: return value * 2
You can start writing test cases for your HTTP trigger.
# <project_root>/tests/test_my_second_function.pyimport unittestimport azure.functions as funcfrom my_second_function import mainclass TestFunction(unittest.TestCase): def test_my_second_function(self): # Construct a mock HTTP request. req = func.HttpRequest(method='GET', body=None, url='/api/my_second_function', params={'value': '21'}) # Call the function. resp = main(req) # Check the output. self.assertEqual(resp.get_body(), b'21 * 2 = 42',)
Inside your.venv
Python virtual environment folder, install your favorite Python test framework, such aspip install pytest
. Then runpytest tests
to check the test result.
First, create the<project_root>/function_app.py file and implement themy_second_function
function as the HTTP trigger andshared_code.my_second_helper_function
.
# <project_root>/function_app.pyimport azure.functions as funcimport logging# Use absolute import to resolve shared_code modulesfrom shared_code import my_second_helper_functionapp = func.FunctionApp()# Define the HTTP trigger that accepts the ?value=<int> query parameter# Double the value and return the result in HttpResponse@app.function_name(name="my_second_function")@app.route(route="hello")def main(req: func.HttpRequest) -> func.HttpResponse: logging.info('Executing my_second_function.') initial_value: int = int(req.params.get('value')) doubled_value: int = my_second_helper_function.double(initial_value) return func.HttpResponse( body=f"{initial_value} * 2 = {doubled_value}", status_code=200 )
# <project_root>/shared_code/__init__.py# Empty __init__.py file marks shared_code folder as a Python package
# <project_root>/shared_code/my_second_helper_function.pydef double(value: int) -> int: return value * 2
You can start writing test cases for your HTTP trigger.
# <project_root>/tests/test_my_second_function.pyimport unittestimport azure.functions as funcfrom function_app import mainclass TestFunction(unittest.TestCase): def test_my_second_function(self): # Construct a mock HTTP request. req = func.HttpRequest(method='GET', body=None, url='/api/my_second_function', params={'value': '21'}) # Call the function. func_call = main.build().get_user_function() resp = func_call(req) # Check the output. self.assertEqual( resp.get_body(), b'21 * 2 = 42', )
Inside your.venv Python virtual environment folder, install your favorite Python test framework, such aspip install pytest
. Then runpytest tests
to check the test result.
Withazure-functions >= 1.21.0
, functions can also be called directly using the Python interpreter. This example shows how to unit test an HTTP trigger using the v2 programming model:
# <project_root>/function_app.pyimport azure.functions as funcimport loggingapp = func.FunctionApp(http_auth_level=func.AuthLevel.ANONYMOUS)@app.route(route="http_trigger")def http_trigger(req: func.HttpRequest) -> func.HttpResponse: return "Hello, World!"print(http_trigger(None))
Note that with this approach, no additional package or setup is required. The function can be tested by callingpython function_app.py
, and it results inHello, World!
output in the terminal.
Note
Durable Functions require special syntax for unit testing. For more information, refer toUnit Testing Durable Functions in Python
Thetempfile.gettempdir()
method returns a temporary folder, which on Linux is/tmp. Your application can use this directory to store temporary files that are generated and used by your functions when they're running.
Important
Files written to the temporary directory aren't guaranteed to persist across invocations. During scale out, temporary files aren't shared between instances.
The following example creates a named temporary file in the temporary directory (/tmp):
import loggingimport azure.functions as funcimport tempfilefrom os import listdir#--- tempFilePath = tempfile.gettempdir() fp = tempfile.NamedTemporaryFile() fp.write(b'Hello world!') filesDirListInTemp = listdir(tempFilePath)
We recommend that you maintain your tests in a folder that's separate from the project folder. This action keeps you from deploying test code with your app.
A few libraries come with the Python functions runtime.
The Python standard library contains a list of built-in Python modules that are shipped with each Python distribution. Most of these libraries help you access system functionality, such as file input/output (I/O). On Windows systems, these libraries are installed with Python. On Unix-based systems, they're provided by package collections.
To view the library for your Python version, go to:
The Azure Functions Python worker requires a specific set of libraries. You can also use these libraries in your functions, but they aren't a part of the Python standard. If your functions rely on any of these libraries, they might be unavailable to your code when it's running outside of Azure Functions.
Note
If your function app'srequirements.txt file contains anazure-functions-worker
entry, remove it. The functions worker is automatically managed by the Azure Functions platform, and we regularly update it with new features and bug fixes. Manually installing an old version of worker in therequirements.txt file might cause unexpected issues.
Note
If your package contains certain libraries that might collide with worker's dependencies (for example, protobuf, tensorflow, or grpcio), configurePYTHON_ISOLATE_WORKER_DEPENDENCIES
to1
in app settings to prevent your application from referring to worker's dependencies.
Every Python worker update includes a new version of theAzure Functions Python library (azure.functions). This approach makes it easier to continuously update your Python function apps, because each update is backwards-compatible. For a list of releases of this library, go toazure-functions PyPi.
The runtime library version is fixed by Azure, and it can't be overridden byrequirements.txt. Theazure-functions
entry inrequirements.txt is only for linting and customer awareness.
Use the following code to track the actual version of the Python functions library in your runtime:
getattr(azure.functions, '__version__', '< 1.2.1')
For a list of preinstalled system libraries in Python worker Docker images, see the following:
Functions runtime | Debian version | Python versions |
---|---|---|
Version 3.x | Buster | Python 3.7 Python 3.8 Python 3.9 |
The Python worker process that runs in Azure Functions lets you integrate third-party libraries into your function app. These extension libraries act as middleware that can inject specific operations during the lifecycle of your function's execution.
Extensions are imported in your function code much like a standard Python library module. Extensions are run based on the following scopes:
Scope | Description |
---|---|
Application-level | When imported into any function trigger, the extension applies to every function execution in the app. |
Function-level | Execution is limited to only the specific function trigger into which it's imported. |
Review the information for each extension to learn more about the scope in which the extension runs.
Extensions implement a Python worker extension interface. This action lets the Python worker process call into the extension code during the function's execution lifecycle. To learn more, seeCreate extensions.
You can use a Python worker extension library in your Python functions by doing the following:
"PYTHON_ENABLE_WORKER_EXTENSIONS": "1"
in theValues
section of yourlocal.settings.json file.PYTHON_ENABLE_WORKER_EXTENSIONS=1
in yourapp settings.Important
Third-party Python worker extension libraries aren't supported or warrantied by Microsoft. You must make sure that any extensions that you use in your function app is trustworthy, and you bear the full risk of using a malicious or poorly written extension.
Third-parties should provide specific documentation on how to install and consume their extensions in your function app. For a basic example of how to consume an extension, seeConsuming your extension.
Here are examples of using extensions in a function app, by scope:
# <project_root>/requirements.txtapplication-level-extension==1.0.0
# <project_root>/Trigger/__init__.pyfrom application_level_extension import AppExtensionAppExtension.configure(key=value)def main(req, context): # Use context.app_ext_attributes here
Extensions are created by third-party library developers who have created functionality that can be integrated into Azure Functions. An extension developer designs, implements, and releases Python packages that contain custom logic designed specifically to be run in the context of function execution. These extensions can be published either to the PyPI registry or to GitHub repositories.
To learn how to create, package, publish, and consume a Python worker extension package, seeDevelop Python worker extensions for Azure Functions.
An extension that's inherited fromAppExtensionBase
runs in anapplication scope.
AppExtensionBase
exposes the following abstract class methods for you to implement:
Method | Description |
---|---|
init | Called after the extension is imported. |
configure | Called from function code when it's needed to configure the extension. |
post_function_load_app_level | Called right after the function is loaded. The function name and function directory are passed to the extension. Keep in mind that the function directory is read-only, and any attempt to write to a local file in this directory fails. |
pre_invocation_app_level | Called right before the function is triggered. The function context and function invocation arguments are passed to the extension. You can usually pass other attributes in the context object for the function code to consume. |
post_invocation_app_level | Called right after the function execution finishes. The function context, function invocation arguments, and invocation return object are passed to the extension. This implementation is a good place to validate whether execution of the lifecycle hooks succeeded. |
An extension that inherits fromFuncExtensionBase runs in a specific function trigger.
FuncExtensionBase
exposes the following abstract class methods for implementations:
Method | Description |
---|---|
__init__ | The constructor of the extension. It's called when an extension instance is initialized in a specific function. When you're implementing this abstract method, you might want to accept afilename parameter and pass it to the parent's methodsuper().__init__(filename) for proper extension registration. |
post_function_load | Called right after the function is loaded. The function name and function directory are passed to the extension. Keep in mind that the function directory is read-only, and any attempt to write to a local file in this directory fails. |
pre_invocation | Called right before the function is triggered. The function context and function invocation arguments are passed to the extension. You can usually pass other attributes in the context object for the function code to consume. |
post_invocation | Called right after the function execution finishes. The function context, function invocation arguments, and invocation return object are passed to the extension. This implementation is a good place to validate whether execution of the lifecycle hooks succeeded. |
Azure Functions supports cross-origin resource sharing (CORS). CORS is configuredin the portal and through theAzure CLI. The CORS allowed origins list applies at the function app level. With CORS enabled, responses include theAccess-Control-Allow-Origin
header. For more information, seeCross-origin resource sharing.
Cross-origin resource sharing (CORS) is fully supported for Python function apps.
By default, a host instance for Python can process only one function invocation at a time. This is because Python is a single-threaded runtime. For a function app that processes a large number of I/O events or is being I/O bound, you can significantly improve performance by running functions asynchronously. For more information, seeImprove throughout performance of Python apps in Azure Functions.
To improve throughput, Azure Functions lets your out-of-process Python language worker share memory with the Functions host process. When your function app is hitting bottlenecks, you can enable shared memory by adding an application setting namedFUNCTIONS_WORKER_SHARED_MEMORY_DATA_TRANSFER_ENABLED with a value of1
. With shared memory enabled, you can then use theDOCKER_SHM_SIZE setting to set the shared memory to something like268435456
, which is equivalent to 256 MB.
For example, you might enable shared memory to reduce bottlenecks when you're using Blob Storage bindings to transfer payloads larger than 1 MB.
This functionality is available only for function apps that are running in Premium and Dedicated (Azure App Service) plans. To learn more, seeShared memory.
Here are two troubleshooting guides for common issues:
Here are two troubleshooting guides for known issues with the v2 programming model:
All known issues and feature requests are tracked in aGitHub issues list. If you run into a problem and can't find the issue in GitHub, open a new issue, and include a detailed description of the problem.
For more information, see the following resources:
Was this page helpful?
Was this page helpful?