Azure Functions Python 开发人员指南

本指南介绍如何使用 Python 开发 Azure Functions。 本文假设你已阅读 Azure Functions 开发人员指南

重要

本文支持 Azure Functions 中 Python 的 v1 和 v2 编程模型。 Python v1 模型使用 functions.json 文件来定义函数,而新的 v2 模型允许改用基于修饰器的方法。 这种新方法可以简化文件结构,并更注重代码。 请选择文章顶部的“v2”选择器来了解这种新的编程模型。

作为 Python 开发人员,你可能还会对下列某篇文章感兴趣:

开发选项

这两个 Python 函数编程模型都支持在以下任一环境中进行本地开发:

Python v2 编程模型:

请注意,Python v2 编程模型仅在 4.x 函数运行时中受支持。 有关详细信息,请参阅 Azure Functions 运行时版本概述

Python v1 编程模型:

此外,还可以在 Azure 门户中创建 Python v1 函数。

提示

尽管可以在 Windows 上本地开发基于 Python 的 Azure Functions,但仅在 Azure 中运行时,基于 Linux 的托管计划才支持 Python。 有关详细信息,请参阅支持的操作系统/运行时组合列表

编程模型

Azure Functions 要求函数是 Python 脚本中处理输入并生成输出的无状态方法。 默认情况下,运行时预期该方法在 __init__.py 文件中作为名为 main() 的全局方法实现。 还可以指定替代入口点

来自触发器和绑定的数据使用在 function.json 文件中定义的 name 属性,通过方法特性绑定到函数。 例如,以下 function.json 文件描述一个由名为 req 的 HTTP 请求触发的简单函数:

{
    "scriptFile": "__init__.py",
    "bindings": [
        {
            "authLevel": "function",
            "type": "httpTrigger",
            "direction": "in",
            "name": "req",
            "methods": [
                "get",
                "post"
            ]
        },
        {
            "type": "http",
            "direction": "out",
            "name": "$return"
        }
    ]
}

根据这一定义,包含函数代码的 __init__.py 文件可能类似于以下示例:

def main(req):
    user = req.params.get('user')
    return f'Hello, {user}!'

还可以使用 Python 类型注释在函数中显式声明属性类型和返回类型。 此操作有助于使用许多 Python 代码编辑器提供的 IntelliSense 和自动完成功能。

import azure.functions

def main(req: azure.functions.HttpRequest) -> str:
    user = req.params.get('user')
    return f'Hello, {user}!'

使用 azure.functions.* 包中附带的 Python 注释将输入和输出绑定到方法。

Azure Functions 要求函数是 Python 脚本中处理输入并生成输出的无状态方法。 默认情况下,运行时期望方法在 function_app.py 文件中作为全局方法实现。

可以使用基于修饰器的方法声明触发器和绑定,并函数中使用它们。 它们在与函数相同的文件 function_app.py 中定义。 例如,以下 function_app.py 文件表示 HTTP 请求的函数触发器。

@app.function_name(name="HttpTrigger1")
@app.route(route="req")
def main(req):
    user = req.params.get("user")
    return f"Hello, {user}!"

还可以使用 Python 类型注释在函数中显式声明属性类型和返回类型。 此操作有助于使用许多 Python 代码编辑器提供的 IntelliSense 和自动完成功能。

import azure.functions as func

app = 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}!"

若要了解 v2 模型的已知限制及其解决方法,请参阅排查 Azure Functions 中的 Python 错误

替代入口点

可以选择在 function.json 文件中指定 scriptFileentryPoint 属性来更改函数的默认行为。 例如,下面的 function.json 指示运行时使用 main.py 文件中的 customentry() 方法作为 Azure 函数的入口点。

{
  "scriptFile": "main.py",
  "entryPoint": "customentry",
  "bindings": [
      ...
  ]
}

入口点只能在 function_app.py 文件中。 但是,可以使用蓝图或通过导入在 function_app.py 中引用项目中的函数。

文件夹结构

Python 函数项目的建议文件夹结构如以下示例所示:

 <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

主项目文件夹 <project_root> 可以包含以下文件:

  • local.settings.json:用于在本地运行时存储应用设置和连接字符串。 此文件不会被发布到 Azure。 若要了解详细信息,请参阅 local.settings.file
  • requirements.txt:包含在发布到 Azure 时系统安装的 Python 包列表。
  • host.json:包含在函数应用实例中影响所有函数的配置选项。 此文件会被发布到 Azure。 本地运行时,并非所有选项都受支持。 若要了解详细信息,请参阅 host.json
  • .vscode/:(可选)包含存储的 Visual Studio Code 配置。 若要了解详细信息,请参阅 Visual Studio Code 设置
  • .venv/:(可选)包含本地开发使用的 Python 虚拟环境。
  • Dockerfile:(可选)在自定义容器中发布项目时使用。
  • tests/:(可选)包含函数应用的测试用例。
  • .funcignore:(可选)声明不应发布到 Azure 的文件。 通常,此文件包含 .vscode/ 以忽略编辑器设置,包含 .venv/ 以忽略本地 Python 虚拟环境,包含 tests/ 以忽略测试用例,包含 local.settings.json 以阻止发布本地应用设置。

每个函数都有自己的代码文件和绑定配置文件 function.json。

Python 函数项目的建议文件夹结构如以下示例所示:

 <project_root>/
 | - .venv/
 | - .vscode/
 | - function_app.py
 | - additional_functions.py
 | - tests/
 | | - test_my_function.py
 | - .funcignore
 | - host.json
 | - local.settings.json
 | - requirements.txt
 | - Dockerfile

主项目文件夹 <project_root> 可以包含以下文件:

  • .venv/:(可选)包含本地开发使用的 Python 虚拟环境。
  • .vscode/:(可选)包含存储的 Visual Studio Code 配置。 若要了解详细信息,请参阅 Visual Studio Code 设置
  • function_app.py:所有函数及其相关触发器和绑定的默认位置。
  • additional_functions.py:(可选)包含通过蓝图在 function_app.py 中引用的函数(通常用于逻辑分组)的任何其他 Python 文件。
  • tests/:(可选)包含函数应用的测试用例。
  • .funcignore:(可选)声明不应发布到 Azure 的文件。 通常,此文件包含 .vscode/ 以忽略编辑器设置,包含 .venv/ 以忽略本地 Python 虚拟环境,包含 tests/ 以忽略测试用例,包含 local.settings.json 以阻止发布本地应用设置。
  • host.json:包含在函数应用实例中影响所有函数的配置选项。 此文件会被发布到 Azure。 本地运行时,并非所有选项都受支持。 若要了解详细信息,请参阅 host.json
  • local.settings.json:用于在本地运行时存储应用设置和连接字符串。 此文件不会被发布到 Azure。 若要了解详细信息,请参阅 local.settings.file
  • requirements.txt:包含在发布到 Azure 时系统安装的 Python 包列表。
  • Dockerfile:(可选)在自定义容器中发布项目时使用。

在 Azure 中将项目部署到函数应用时,主项目文件夹<project_root> 的整个内容应包含在包中,但不包含该文件夹本身,这意味着 host.json 应位于包根目录中。 建议你在一个文件夹中维护测试以及其他函数,在此示例中为 tests/。 有关详细信息,请参阅单元测试

连接到数据库

Azure Cosmos DB 是一个完全托管的 NoSQL 和关系数据库,适用于新式应用开发,包括 AI、数字商业、物联网、预订管理和其他类型的解决方案的开发。 它提供个位数的毫秒响应时间、自动和即时可伸缩性,并可在任何规模下保证速度。 其各种 API 可以适用于所有操作数据模型,包括关系、文档、矢量、键值、图形和表。

若要连接到 Cosmos DB,请首先创建帐户、数据库和容器。 然后就可以使用触发器和绑定将 Functions 连接到 Cosmos DB,就像此示例一样。 还可以使用 Cosmos DB 的 Python 库,如下所示:

pip install azure-cosmos

from azure.cosmos import CosmosClient, exceptions
from azure.cosmos.partition_key import PartitionKey

# Replace these values with your Cosmos DB connection information
endpoint = "https://azure-cosmos-nosql.documents.azure.cn:443/"
key = "master_key"
database_id = "cosmicwerx"
container_id = "cosmicontainer"
partition_key = "/partition_key"

# Set the total throughput (RU/s) for the database and container
database_throughput = 1000

# Initialize the Cosmos client
client = CosmosClient(endpoint, key)

# Create or get a reference to a database
try:
    database = client.create_database_if_not_exists(id=database_id)
    print(f'Database "{database_id}" created or retrieved successfully.')

except exceptions.CosmosResourceExistsError:
    database = client.get_database_client(database_id)
    print('Database with id \'{0}\' was found'.format(database_id))

# Create or get a reference to a container
try:
    container = database.create_container(id=container_id, partition_key=PartitionKey(path='/partitionKey'))
    print('Container with id \'{0}\' created'.format(container_id))

except exceptions.CosmosResourceExistsError:
    container = database.get_container_client(container_id)
    print('Container with id \'{0}\' was found'.format(container_id))

# Sample document data
sample_document = {
    "id": "1",
    "name": "Doe Smith",
    "city": "New York",
    "partition_key": "NY"
}

# Insert a document
container.create_item(body=sample_document)

# Query for documents
query = "SELECT * FROM c where c.id = 1"
items = list(container.query_items(query, enable_cross_partition_query=True))

蓝图

Python v2 编程模型引入了蓝图的概念。 蓝图是一个实例化的新类,用于在核心函数应用程序之外注册函数。 蓝图实例中注册的函数不直接由函数运行时编制索引。 若要为这些蓝图函数编制索引,函数应用需要从蓝图实例注册这些函数。

使用蓝图可获得以下好处:

  • 可以将函数应用分解为模块化组件,从而可以在多个 Python 文件中定义函数,并将其划分为每个文件的不同组件。
  • 提供可扩展的公共函数应用接口用于生成和重用你自己的 API。

以下示例演示了如何使用蓝图:

首先,在 http_blueprint.py 文件中定义 HTTP 触发的函数并将其添加到蓝图对象。

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 
        ) 

接下来,在 function_app.py 文件中,导入蓝图对象并将其函数注册到函数应用。

import azure.functions as func 
from http_blueprint import bp

app = func.FunctionApp() 

app.register_functions(bp) 

注意

Durable Functions 还支持蓝图。 若要为 Durable Functions 应用创建蓝图,请使用 azure-functions-durableBlueprint 类注册业务流程、活动、实体触发器和客户端绑定,如此处所示。 然后,生成的蓝图便可以正常注册。 有关示例,请参阅我们的示例

导入行为

可以使用绝对引用和相对引用在函数代码中导入模块。 根据上面所述的文件夹结构,以下导入在函数文件 <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)

注意

使用绝对导入语法时,shared_code/ 文件夹需要包含 __init__.py 文件以将其标记为 Python 包。

以下 __app__ 导入和 beyond top-level 相对导入已弃用,因为它们不受静态类型检查器支持,且不受 Python 测试框架支持:

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)

触发器和输入

在 Azure Functions 中,输入分为两种类别:触发器输入和其他输入。 虽然它们在 function.json 文件中并不相同,但它们在 Python 代码中的使用方法却是相同的。 在本地运行时,触发器和输入源的连接字符串或机密映射到 local.settings.json 文件中的值;在 Azure 中运行时,它们映射到应用程序设置。

例如,以下代码演示了两个输入之间的差异:

// 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__.py
import azure.functions as func
import logging

def main(req: func.HttpRequest, obj: func.InputStream):
    logging.info(f'Python HTTP-triggered function processed: {obj.read()}')

调用函数时,HTTP 请求作为 req 传递给函数。 将基于路由 URL 中的 ID 从 Azure Blob 存储帐户检索一个条目,并在函数体中将其用作 obj。 在这里,指定的存储帐户是在 CONNECTION_STRING 应用设置中找到的连接字符串。

在 Azure Functions 中,输入分为两种类别:触发器输入和其他输入。 尽管它们是使用不同的修饰器定义的,但在 Python 代码中的用法相似。 在本地运行时,触发器和输入源的连接字符串或机密映射到 local.settings.json 文件中的值;在 Azure 中运行时,它们映射到应用程序设置。

例如,以下代码演示了如何定义 Blob 存储输入绑定:

// local.settings.json
{
  "IsEncrypted": false,
  "Values": {
    "FUNCTIONS_WORKER_RUNTIME": "python",
    "STORAGE_CONNECTION_STRING": "<AZURE_STORAGE_CONNECTION_STRING>",
    "AzureWebJobsStorage": "<azure-storage-connection-string>",
    "AzureWebJobsFeatureFlags": "EnableWorkerIndexing"
  }
}
# function_app.py
import azure.functions as func
import logging

app = func.FunctionApp()

@app.route(route="req")
@app.read_blob(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()}')

调用函数时,HTTP 请求作为 req 传递给函数。 将基于路由 URL 中的 ID 从 Azure Blob 存储帐户检索一个条目,并在函数体中将其用作 obj。 在这里,指定的存储帐户是在 STORAGE_CONNECTION_STRING 应用设置中找到的连接字符串。

对于数据密集型绑定操作,你可能希望使用单独的存储帐户。 有关详细信息,请参阅存储帐户指导

Outputs

输出可以在返回值和输出参数中进行表示。 如果只有一个输出,则建议使用返回值。 对于多个输出,必须使用输出参数。

若要使用函数的返回值作为输出绑定的值,则绑定的 name 属性应在 function.json 文件中设置为 $return

若要生成多个输出,请使用 azure.functions.Out 接口提供的 set() 方法将值分配给绑定。 例如,以下函数可以将消息推送到队列,还可返回 HTTP 响应。

{
  "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 func

def main(req: func.HttpRequest,
         msg: func.Out[func.QueueMessage]) -> str:

    message = req.params.get('body')
    msg.set(message)
    return message

输出可以在返回值和输出参数中进行表示。 如果只有一个输出,则建议使用返回值。 对于多个输出,必须使用输出参数。

若要生成多个输出,请使用 azure.functions.Out 接口提供的 set() 方法将值分配给绑定。 例如,以下函数可以将消息推送到队列,还可返回 HTTP 响应。

# function_app.py
import azure.functions as func

app = 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

日志记录

可在函数应用中通过根 logging 处理程序来访问 Azure Functions 运行时记录器。 此记录器绑定到 Application Insights,并允许标记在函数执行期间遇到的警告和错误。

下面的示例在通过 HTTP 触发器调用函数时记录信息消息。

import logging

def main(req):
    logging.info('Python HTTP trigger function processed a request.')

有更多日志记录方法可用于在不同跟踪级别向控制台进行写入:

方法 说明
critical(_message_) 在根记录器中写入具有 CRITICAL 级别的消息。
error(_message_) 在根记录器中写入具有 ERROR 级别的消息。
warning(_message_) 在根记录器中写入具有 WARNING 级别的消息。
info(_message_) 在根记录器中写入具有 INFO 级别的消息。
debug(_message_) 在根记录器中写入具有 DEBUG 级别的消息。

若要详细了解日志记录,请参阅监视 Azure Functions

来自所创建线程的日志记录

要查看来自所创建线程的日志,请在函数的签名中包含 context 参数。 此参数包含属性 thread_local_storage,该属性可存储本地 invocation_id。 可将其设置为函数的当前 invocation_id,以确保更改上下文。

import azure.functions as func
import logging
import threading


def 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.')

记录自定义遥测数据

默认情况下,函数运行时会收集函数生成的日志和其他遥测数据。 此遥测最终会作为 Application Insights 中的跟踪。 默认情况下,某些 Azure 服务的请求和依赖项遥测还通过触发器和绑定进行收集。

若要收集自定义请求和自定义依赖项遥测(在绑定之外),可以使用 OpenCensus Python 扩展。 此扩展将自定义遥测数据发送到应用程序 Insights 实例。 可以在 OpenCensus 存储库中找到受支持的扩展的列表。

注意

若要使用 OpenCensus Python 扩展,需要通过将 PYTHON_ENABLE_WORKER_EXTENSIONS 设置为 1,在函数应用中启用 Python 辅助角色扩展。 还需要切换为使用 Application Insights 连接字符串,方法是将 APPLICATIONINSIGHTS_CONNECTION_STRING 设置添加到应用程序设置(如果不存在)。

// requirements.txt
...
opencensus-extension-azure-functions
opencensus-ext-requests
import json
import logging

import requests
from opencensus.extension.azure.functions import OpenCensusExtension
from opencensus.trace import config_integration

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

HTTP 触发器

HTTP 触发器在 function.json 文件中定义。 绑定的 name 必须与函数中的命名参数匹配。 前面的示例中使用了绑定名称 req。 此参数是 HttpRequest 对象,并返回 HttpResponse 对象。

HttpRequest 对象中,可以获取请求标头、查询参数、路由参数和消息正文。

以下示例来自适用于 Python 的 HTTP 触发器模版

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
        )

在此函数中,从 HttpRequest 对象的 params 参数获取 name 查询参数的值。 使用 get_json 方法读取 JSON 编码的消息正文。

同样,你可以在返回的 HttpResponse 对象中为响应消息设置 status_codeheaders

HTTP 触发器定义为一种方法,该方法采用命名绑定参数(即 HttpRequest 对象)并返回 HttpResponse 对象。 请将 function_name 装饰器应用于方法来定义函数名称,同时通过应用 route 装饰器来设置 HTTP 终结点。

以下示例取自 Python v2 编程模型的 HTTP 触发器模板,其中的绑定参数名称为 req。 它是使用 Azure Functions Core Tools 或 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
        )

HttpRequest 对象中,可以获取请求标头、查询参数、路由参数和消息正文。 在此函数中,从 HttpRequest 对象的 params 参数获取 name 查询参数的值。 使用 get_json 方法读取 JSON 编码的消息正文。

同样,你可以在返回的 HttpResponse 对象中为响应消息设置 status_codeheaders

若要在此示例中传入名称,请粘贴运行函数时提供的 URL,并在其后面追加 "?name={name}"

Web 框架

可以将 Web 服务器网关接口 (WSGI) 兼容的框架和异步服务器网关接口 (ASGI) 兼容的框架(例如 Flask 和 FastAPI)用于 HTTP 触发的 Python 函数。 本部分介绍如何修改函数以支持这些框架。

首先,必须更新 function.json 文件,使其在 HTTP 触发器中包含 route,如以下示例中所示:

{
  "scriptFile": "__init__.py",
  "bindings": [
    {
       "authLevel": "anonymous",
       "type": "httpTrigger",
       "direction": "in",
       "name": "req",
       "methods": [
           "get",
           "post"
       ],
       "route": "{*route}"
    },
    {
       "type": "http",
       "direction": "out",
       "name": "$return"
    }
  ]
}

还必须更新 host.json 文件,使其包含 HTTP routePrefix,如以下示例中所示:

{
  "version": "2.0",
  "logging": 
  {
    "applicationInsights": 
    {
      "samplingSettings": 
      {
        "isEnabled": true,
        "excludedTypes": "Request"
      }
    }
  },
  "extensionBundle": 
  {
    "id": "Microsoft.Azure.Functions.ExtensionBundle",
    "version": "[3.*, 4.0.0)"
  },
  "extensions": 
  {
    "http": 
    {
        "routePrefix": ""
    }
  }
}

根据框架使用的接口,更新 Python 代码文件 init.py。 下面的示例展示了 ASGI 处理程序方法或 Flask 的 WSGI 包装器方法:

app = fastapi.FastAPI()

@app.get("hello/{name}")
async def get_name(name: str):
  return {"name": name}

def main(req: func.HttpRequest, context: func.Context) -> func.HttpResponse:
    return func.AsgiMiddleware(app).handle(req, context)

可以将异步服务器网关接口 (ASGI) 兼容的框架和 Web 服务器网关接口 (WSGI) 兼容的框架(例如 Flask 和 FastAPI)用于 HTTP 触发的 Python 函数。 必须先更新 host.json 文件以包含 HTTP routePrefix,如以下示例所示:

{
  "version": "2.0",
  "logging": 
  {
    "applicationInsights": 
    {
      "samplingSettings": 
      {
        "isEnabled": true,
        "excludedTypes": "Request"
      }
    }
  },
  "extensionBundle": 
  {
    "id": "Microsoft.Azure.Functions.ExtensionBundle",
    "version": "[2.*, 3.0.0)"
  },
  "extensions": 
  {
    "http": 
    {
        "routePrefix": ""
    }
  }
}

框架代码如以下示例所示:

AsgiFunctionApp 是用于构造 ASGI HTTP 函数的顶级函数应用类。

# function_app.py

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

缩放和性能

有关 Python 函数应用的缩放和性能最佳做法,请参阅 Python 缩放和性能一文。

上下文

若要在运行某个函数时获取其调用上下文,请在其签名中包含 context 参数。

例如:

import azure.functions


def main(req: azure.functions.HttpRequest,
         context: azure.functions.Context) -> str:
    return f'{context.invocation_id}'

Context 类具有以下字符串属性:

属性 说明
function_directory 在其中运行函数的目录。
function_name 函数的名称。
invocation_id 当前函数调用的 ID。
thread_local_storage 函数的线程本地存储。 包含可用于invocation_id来自所创建线程的日志记录的本地
trace_context 分布式跟踪的上下文。 有关详细信息,请参阅 Trace Context
retry_context 函数的重试上下文。 有关详细信息,请参阅 retry-policies

全局变量

应用的状态是否将保留,以供将来执行使用,这一点不能保证。 但是,Azure Functions 运行时通常会为同一应用的多次执行重复使用同一进程。 若要缓存高开销计算的结果,请将其声明为全局变量。

CACHED_DATA = None


def main(req):
    global CACHED_DATA
    if CACHED_DATA is None:
        CACHED_DATA = load_json()

    # ... use CACHED_DATA in code

环境变量

在 Azure Functions 中,服务连接字符串等应用程序设置在运行时将公开为环境变量。 在代码中访问这些设置有两种主要方法。

方法 说明
os.environ["myAppSetting"] 尝试按键名称获取应用程序设置,失败时会引发错误。
os.getenv("myAppSetting") 尝试按键名称获取应用程序设置,失败时会返回 null

这两种方法都需要声明 import os

以下示例使用名为 myAppSetting 的键通过 os.environ["myAppSetting"] 获取应用程序设置

import logging
import os

import azure.functions as func

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}')

对于本地开发,应用程序设置在 local.settings.json 文件中维护

在 Azure Functions 中,服务连接字符串等应用程序设置在运行时将公开为环境变量。 在代码中访问这些设置有两种主要方法。

方法 说明
os.environ["myAppSetting"] 尝试按键名称获取应用程序设置,失败时会引发错误。
os.getenv("myAppSetting") 尝试按键名称获取应用程序设置,失败时会返回 null

这两种方法都需要声明 import os

以下示例使用名为 myAppSetting 的键通过 os.environ["myAppSetting"] 获取应用程序设置

import logging
import os

import azure.functions as func

app = 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}')

对于本地开发,应用程序设置在 local.settings.json 文件中维护

使用新的编程模型时,请在 local.settings.json 文件中启用以下应用设置,如下所示:

"AzureWebJobsFeatureFlags": "EnableWorkerIndexing"

部署函数时不会自动创建此设置。 必须在 Azure 上的函数应用中显式创建此设置,才能使用 v2 模型运行此设置。

Python 版本

Azure Functions 支持以下 Python 版本:

Functions 版本 Python* 版本
4.x 3.11
3.10
3.9
3.8
3.7
3.x 3.9
3.8
3.7

* 官方 Python 发行版

若要在 Azure 中创建函数应用时请求特定的 Python 版本,请使用 az functionapp create 命令的 --runtime-version 选项。 函数运行时版本由 --functions-version 选项设置。 Python 版本是在创建函数应用时设置的,不能为在消耗计划中运行的应用更改它。

运行时在本地运行时使用可用的 Python 版本。

更改 Python 版本

若要将 Python 函数应用设置为特定的语言版本,需要在站点配置中的 LinuxFxVersion 字段内指定语言以及语言版本。 例如,若要将 Python 应用更改为使用 Python 3.8,请将 linuxFxVersion 设置为 python|3.8

若要了解如何查看和更改 linuxFxVersion 站点设置,请参阅如何面向 Azure Functions 运行时版本

有关更多一般信息,请参阅 Azure Functions 运行时支持策略Azure Functions 中支持的语言

包管理

在使用 Core Tools 或 Visual Studio Code 进行本地开发时,将所需包的名称和版本添加到 requirements.txt 文件,然后使用 pip 安装它们。

例如,可以使用以下 requirements.txt 文件和 pip 命令从 PyPI 安装 requests 包。

requests==2.19.1
pip install -r requirements.txt

应用服务计划中运行函数时,在 requirements.txt 中定义的依赖项优先于内置 Python 模块,例如 logging。 当内置模块与代码中的目录具有相同名称时,此优先顺序可能会导致冲突。 在消耗计划弹性高级计划中运行时,发生冲突的可能性较小,因为默认情况下依赖项没有优先级。

为了防止在应用服务计划中运行时出现问题,请不要将目录命名为与任何 Python 原生模块相同的名称,并且不要在项目的 requirements.txt 文件中包含 Python 原生库。

发布到 Azure

准备好进行发布时,确保所有公开发布的依赖项都在 requirements.txt 文件中列出。 可以在项目目录的根目录中找到此文件。

可以在项目的根目录中找到从发布中排除的项目文件和文件夹,包括虚拟环境文件夹。

将 Python 项目发布到 Azure 时,支持三种生成操作:远程生成、本地生成以及使用自定义依赖项生成。

还可使用 Azure Pipelines 生成依赖项并使用持续交付 (CD) 发布。

远程生成

使用远程生成时,服务器上还原的依赖项和本机依赖项与生产环境匹配。 这导致要上传的部署包较小。 在 Windows 上开发 Python 应用时使用远程生成。 如果你的项目具有自定义依赖项,可使用具有额外索引 URL 的远程生成

依赖项根据 requirements.txt 文件的内容远程获取。 远程生成是推荐的生成方法。 默认情况下,使用下面的 func azure functionapp publish 命令将 Python 项目发布到 Azure 时,Core Tools 会请求远程生成。

func azure functionapp publish <APP_NAME>

请记住将 <APP_NAME> 替换为 Azure 中的函数应用名称。

默认情况下,适用于 Visual Studio Code 的 Azure Functions 扩展还会请求远程生成。

本地生成

依赖项根据 requirements.txt 文件的内容在本地获取。 可以使用下面的 func azure functionapp publish 命令通过本地生成来发布,从而防止执行远程生成:

func azure functionapp publish <APP_NAME> --build local

请记住将 <APP_NAME> 替换为 Azure 中的函数应用名称。

使用 --build local 选项时,将从 requirements.txt 文件中读取项目依赖项,并在本地下载和安装这些依赖包。 项目文件和依赖项从本地计算机部署到 Azure。 这会将较大的部署包上传到 Azure。 如果由于某种原因无法使用 Core Tools 获取 requirements.txt 文件中的依赖项,则必须使用自定义依赖项选项进行发布。

在 Windows 上进行本地开发时,不建议使用本地生成。

自定义依赖项

如果项目的依赖项在 Python 包索引中找不到,可通过两种方式生成该项目。 第一种生成方法取决于你生成项目的方式。

具有额外索引 URL 的远程生成

如果包可以从可访问的自定义包索引中获取,请使用远程生成。 在发布之前,请务必创建一个名为 PIP_EXTRA_INDEX_URL 的应用设置。 此设置的值是自定义包索引的 URL。 使用此设置可告诉远程生成使用 --extra-index-url 选项运行 pip install。 若要了解详细信息,请参阅 Python pip install 文档

还可将基本身份验证凭据与额外的包索引 URL 结合使用。 若要了解详细信息,请参阅 Python 文档中的基本身份验证凭据

安装本地包

如果你的项目使用未向我们的工具公开发布的包,则可以通过将包放在 __app__/.python_packages 目录中,使其可供应用使用。 发布之前,运行以下命令以在本地安装依赖项:

pip install  --target="<PROJECT_DIR>/.python_packages/lib/site-packages"  -r requirements.txt

使用自定义依赖项时,应使用 --no-build 发布选项,因为你已将依赖项安装到项目文件夹中。

func azure functionapp publish <APP_NAME> --no-build

请记住将 <APP_NAME> 替换为 Azure 中的函数应用名称。

单元测试

可以使用标准测试框架,像测试其他 Python 代码一样测试以 Python 编写的函数。 对于大多数绑定,可以通过从 azure.functions 包创建适当类的实例来创建 mock 输入对象。 由于 azure.functions 包不可供立即可用,请务必通过 requirements.txt 文件安装该包,如上文包管理部分所述。

以 my_second_function 为例,下面是 HTTP 触发的函数的模拟测试:

首先创建 <project_root>/my_second_function/function.json 文件并将此函数定义为 HTTP 触发器。

{
  "scriptFile": "__init__.py",
  "entryPoint": "main",
  "bindings": [
    {
      "authLevel": "function",
      "type": "httpTrigger",
      "direction": "in",
      "name": "req",
      "methods": [
        "get",
        "post"
      ]
    },
    {
      "type": "http",
      "direction": "out",
      "name": "$return"
    }
  ]
}

接下来,可以实现 my_second_functionshared_code.my_second_helper_function

# <project_root>/my_second_function/__init__.py
import azure.functions as func
import logging

# Use absolute import to resolve shared_code modules
from 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 HttpResponse
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.py

def double(value: int) -> int:
  return value * 2

可以开始为 HTTP 触发器编写测试用例。

# <project_root>/tests/test_my_second_function.py
import unittest

import azure.functions as func
from my_second_function import main

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

在 .venv Python 虚拟环境文件夹中安装你偏好的 Python 测试框架,例如 pip install pytest。 然后运行 pytest tests 即可检查测试结果。

首先创建 <project_root>/function_app.py 文件,并将 my_second_function 函数实现为 HTTP 触发器和 shared_code.my_second_helper_function

# <project_root>/function_app.py
import azure.functions as func
import logging

# Use absolute import to resolve shared_code modules
from shared_code import my_second_helper_function

app = 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.py

def double(value: int) -> int:
  return value * 2

可以开始为 HTTP 触发器编写测试用例。

# <project_root>/tests/test_my_second_function.py
import unittest
import azure.functions as func

from function_app import main

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

在 .venv Python 虚拟环境文件夹中安装你偏好的 Python 测试框架,例如 pip install pytest。 然后运行 pytest tests 即可检查测试结果。

临时文件

tempfile.gettempdir() 方法返回一个临时文件夹,在 Linux 上为 /tmp。 应用程序可使用此目录存储函数在运行时生成和使用的临时文件。

重要

不能保证写入临时目录的文件会在调用之间保留。 在横向扩展期间,临时文件不会在实例之间进行共享。

以下示例在临时目录 (/tmp) 中创建一个命名的临时文件:

import logging
import azure.functions as func
import tempfile

from os import listdir

#---
   tempFilePath = tempfile.gettempdir()
   fp = tempfile.NamedTemporaryFile()
   fp.write(b'Hello world!')
   filesDirListInTemp = listdir(tempFilePath)

建议在独立于项目文件夹的文件夹中维护测试。 此操作可防止对应用部署测试代码。

预安装的库

Python 函数运行时附带了一些库。

Python 标准库

Python 标准库包含每个 Python 发行版附带的内置 Python 模块列表。 其中的大多数库可帮助你访问系统功能,例如文件输入/输出 (I/O)。 在 Windows 系统上,这些库随 Python 一起安装。 在基于 Unix 的系统上,它们由包集合提供。

若要查看适用于你的 Python 版本的库,请转到:

Azure Functions Python 辅助角色依赖项

Azure Functions Python 工作进程需要一组特定的库。 你也可以在函数中使用这些库,但它们并不属于 Python 标准库。 如果你的函数依赖于这些库中的任何一个库,则在 Azure Functions 之外运行时,这些函数可能无法用于代码。 可在 setup.py 文件中的“install_requires”部分找到依赖项的详细列表。

注意

如果函数应用的 requirements.txt 文件包含 azure-functions-worker 条目,请将其删除。 函数工作进程由 Azure Functions 平台自动管理,我们会定期更新新功能和 Bug 修补程序。 在 requirements.txt 文件中手动安装旧版本的工作进程可能会导致意外问题。

注意

如果你的包包含可能与工作进程的依赖项冲突的某些库(例如 protobuf、tensorflow 或 grpcio),请在应用设置中将 PYTHON_ISOLATE_WORKER_DEPENDENCIES 配置为 1,以防止应用程序引用该工作进程的依赖项。

Azure Functions Python 库

每次 Python 工作进程更新都包含一个新版本的 Azure Functions Python 库 (azure.functions)。 这种方法使持续更新 Python 函数应用变得更容易,因为每次更新都是向后兼容的。 有关此库的版本列表,请参阅 azure-functions PyPi

运行时库版本由 Azure 修复,不能通过 requirements.txt 替代。 requirements.txt 中的 azure-functions 条目仅供 Lint 分析和客户认知。

使用以下代码在运行时中跟踪 Python 函数库的实际版本:

getattr(azure.functions, '__version__', '< 1.2.1')

运行时系统库

有关 Python 工作进程 Docker 映像中预装的系统库列表,请参阅以下主题:

Functions 运行时 Debian 版本 Python 版本
3\.x 版 Buster Python 3.7
Python 3.8
Python 3.9

Python 辅助角色扩展

在 Azure Functions 中运行的 Python 工作进程允许将第三方库集成到函数应用中。 这些扩展库充当中间件,可以在执行函数的生命周期内注入特定操作。

在与标准 Python 库模块类似的函数代码中导入扩展。 扩展基于以下范围运行:

范围 说明
应用程序级 导入到任何函数触发器中时,扩展适用于应用中的每个函数执行。
函数级 执行仅限于导入到其中的特定函数触发器。

查看每个扩展的信息,以详细了解扩展的运行范围。

扩展实现 Python 辅助角色扩展接口。 此操作允许 Python 工作进程在函数执行生命周期内调入扩展代码。 若要了解详细信息,请参阅创建扩展

使用扩展

可以通过执行以下操作在 Python 函数中使用 Python 工作进程扩展库:

  1. 在项目的 requirements.txt 文件中添加扩展包。
  2. 将库安装到应用中。
  3. 添加以下应用程序设置:
  4. 将扩展模块导入到函数触发器中。
  5. 配置扩展实例(如果需要)。 应在扩展文档中标注配置要求。

重要

Microsoft 不支持或保证第三方 Python 工作进程扩展库。 必须确保在函数应用中使用的任何扩展都是可信的,并且你承担使用恶意扩展或编写不当的扩展的全部风险。

第三方应会提供有关如何在函数应用中安装和使用其扩展的具体文档。 有关如何使用扩展的基本示例,请参阅使用扩展

下面是在函数应用中按范围使用扩展的示例:

# <project_root>/requirements.txt
application-level-extension==1.0.0
# <project_root>/Trigger/__init__.py

from application_level_extension import AppExtension
AppExtension.configure(key=value)

def main(req, context):
  # Use context.app_ext_attributes here

创建扩展

扩展是由已创建可集成到 Azure Functions 中的功能的第三方库开发人员创建的。 扩展开发人员设计、实现和发布 Python 包,其中包含专为在函数执行上下文中运行而设计的自定义逻辑。 这些扩展可以发布到 PyPI 注册表或 GitHub 存储库。

若要了解如何创建、打包、发布和使用 Python 辅助角色扩展包,请参阅为 Azure Functions 开发 Python 辅助角色扩展

应用程序级扩展

继承自 AppExtensionBase 的扩展在应用程序范围内运行。

AppExtensionBase 公开以下抽象类方法以供你实现:

方法 说明
init 在导入扩展后调用。
configure 根据需要从函数代码中调用以配置扩展。
post_function_load_app_level 在加载函数后立即调用。 函数名称和函数目录传递给扩展。 请记住,函数目录是只读目录,尝试写入此目录中的本地文件的任何操作都将失败。
pre_invocation_app_level 在触发函数前立即调用。 函数上下文和函数调用参数传递给扩展。 通常可以传递上下文对象中的其他属性,以供函数代码使用。
post_invocation_app_level 在函数执行完成后立即调用。 函数上下文、函数调用参数和调用返回对象将传递给扩展。 此实现是验证是否成功执行了生命周期挂钩的好方法。

函数级扩展

继承自特定函数触发器中的 FuncExtensionBase 运行的扩展。

FuncExtensionBase 公开以下抽象类方法以进行实现:

方法 说明
__init__ 扩展的构造函数。 当在特定函数中初始化扩展实例时,将调用此方法。 实现此抽象方法时,可能需要接受 filename 参数,并将其传递给父级的方法 super().__init__(filename) 以进行适当的扩展注册。
post_function_load 在加载函数后立即调用。 函数名称和函数目录传递给扩展。 请记住,函数目录是只读目录,尝试写入此目录中的本地文件的任何操作都将失败。
pre_invocation 在触发函数前立即调用。 函数上下文和函数调用参数传递给扩展。 通常可以传递上下文对象中的其他属性,以供函数代码使用。
post_invocation 在函数执行完成后立即调用。 函数上下文、函数调用参数和调用返回对象将传递给扩展。 此实现是验证是否成功执行了生命周期挂钩的好方法。

跨域资源共享

Azure Functions 支持跨域资源共享 (CORS)。 CORS 在门户中以及通过 Azure CLI 进行配置。 CORS 允许的来源列表应用于函数应用级别。 启用 CORS 后,响应包含 Access-Control-Allow-Origin 标头。 有关详细信息,请参阅 跨域资源共享

Python 函数应用完全支持跨源资源共享 (CORS)。

异步

默认情况下,Python 的主机实例一次只能处理一个函数调用。 这是因为 Python 是单线程运行时。 对于处理大量 I/O 事件或受 I/O 约束的函数应用,可以通过异步运行函数来显著提高性能。 有关详细信息,请参阅在 Azure Functions 中提高 Python 应用的吞吐量性能

共享内存(预览版)

为了提高吞吐量,Azure Functions 可以让你的进程外 Python 语言工作器与 Azure Functions 主机进程共享内存。 当函数应用遇到瓶颈时,可以通过添加名为 FUNCTIONS_WORKER_SHARED_MEMORY_DATA_TRANSFER_ENABLED 且值为 1 的应用程序设置来启用共享内存。 启用共享内存后,可以使用 DOCKER_SHM_SIZE 设置将共享内存设置为 268435456 之类的值,即相当于 256 MB。

例如,在使用 Blob 存储绑定传输大于 1 MB 的有效负载时,你可以启用共享内存以减少瓶颈。

此功能仅适用于在高级和专用(Azure 应用服务)计划中运行的函数应用。 要了解详细信息,请参阅共享内存

已知问题和常见问题解答

下面是针对常见问题的两篇故障排除指南:

下面是针对 v2 编程模型已知问题的两篇故障排除指南:

所有已知问题和功能请求都将在 GitHub 问题列表进行跟踪。 如果你遇到 GitHub 中未列出的问题,请创建新问题并提供问题的详细说明。

后续步骤

有关详细信息,请参阅以下资源:

使用 Python 时遇到问题? 请告诉我们发生了什么情况。