Share via


Guida per sviluppatori Python per Funzioni di Azure

Questa guida è un'introduzione allo sviluppo di Funzioni di Azure usando Python. L'articolo presuppone che tu abbia già letto la guida per sviluppatori Funzioni di Azure.

Importante

Questo articolo supporta sia il modello di programmazione v1 che v2 per Python in Funzioni di Azure. Il modello Python v1 usa un file functions.json per definire le funzioni e il nuovo modello v2 consente invece di usare un approccio basato su decorator. Questo nuovo approccio comporta una struttura di file più semplice ed è più incentrato sul codice. Scegliere il selettore v2 nella parte superiore dell'articolo per informazioni su questo nuovo modello di programmazione.

Gli sviluppatori Python potrebbero anche essere interessati a uno degli articoli seguenti:

Opzioni di sviluppo

Entrambi i modelli di programmazione di Funzioni Python supportano lo sviluppo locale in uno degli ambienti seguenti:

Modello di programmazione Python v2:

Modello di programmazione Python v1:

È anche possibile creare funzioni Python v1 nel portale di Azure.

Suggerimento

Sebbene sia possibile sviluppare funzioni di Azure basate su Python in locale in Windows, Python è supportato solo in un piano di hosting basato su Linux quando è in esecuzione in Azure. Per altre informazioni, vedere l'elenco delle combinazioni di sistema operativo/runtime supportate.

Modello di programmazione

Funzioni di Azure si aspetta che una funzione sia un metodo senza stato nello script di Python che elabora l'input e genera l'output. Per impostazione predefinita, il runtime prevede che il metodo venga implementato come metodo globale chiamato main() nel file __init__.py . È anche possibile specificare un punto di ingresso alternativo.

I dati vengono associati alla funzione da trigger e associazioni tramite attributi del metodo che usano la name proprietà definita nel file function.json . Ad esempio, il file di function.json seguente descrive una funzione semplice attivata da una richiesta HTTP denominata req:

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

In base a questa definizione, il file __init__.py che contiene il codice della funzione potrebbe essere simile all'esempio seguente:

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

È anche possibile dichiarare in modo esplicito i tipi di attributo e il tipo restituito nella funzione usando le annotazioni dei tipi Python. In questo modo è possibile usare IntelliSense e le funzionalità di completamento automatico fornite da molti editor di codice Python.

import azure.functions

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

Usare le annotazioni Python incluse nel pacchetto azure.functions.* per associare l'input e gli output ai metodi.

Funzioni di Azure si aspetta che una funzione sia un metodo senza stato nello script di Python che elabora l'input e genera l'output. Per impostazione predefinita, il runtime prevede che il metodo venga implementato come metodo globale nel file function_app.py .

I trigger e le associazioni possono essere dichiarati e usati in una funzione in un approccio basato su decorator. Sono definiti nello stesso file, function_app.py, come le funzioni. Ad esempio, il file di function_app.py seguente rappresenta un trigger di funzione da una richiesta HTTP.

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

È anche possibile dichiarare in modo esplicito i tipi di attributo e il tipo restituito nella funzione usando le annotazioni dei tipi Python. In questo modo è possibile usare IntelliSense e le funzionalità di completamento automatico fornite da molti editor di codice Python.

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

Per informazioni sulle limitazioni note con il modello v2 e le relative soluzioni alternative, vedere Risolvere gli errori Python in Funzioni di Azure.

Punto di ingresso alternativo

È possibile cambiare il comportamento predefinito di una funzione specificando facoltativamente le proprietà scriptFile e entryPoint nel file function.json. Ad esempio, il function.json seguente indica al runtime di usare il customentry() metodo nel file main.py come punto di ingresso per la funzione di Azure.

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

Il punto di ingresso si trova solo nel file function_app.py . Tuttavia, è possibile fare riferimento a funzioni all'interno del progetto in function_app.py usando progetti o importando.

Struttura delle cartelle

La struttura di cartelle consigliata per un progetto di funzioni Python è simile all'esempio seguente:

 <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

La cartella principale del progetto, <project_root>, può contenere i file seguenti:

  • local.settings.json: usato per archiviare le impostazioni e le stringa di connessione dell'app durante l'esecuzione in locale. Questo file non viene pubblicato in Azure. Per altre informazioni, vedere local.settings.file.
  • requirements.txt: contiene l'elenco dei pacchetti Python installati dal sistema durante la pubblicazione in Azure.
  • host.json: contiene opzioni di configurazione che influiscono su tutte le funzioni in un'istanza dell'app per le funzioni. Questo file viene pubblicato in Azure. Non tutte le opzioni sono supportate durante l'esecuzione in locale. Per altre informazioni, vedere host.json.
  • .vscode/: (facoltativo) Contiene la configurazione di Visual Studio Code archiviata. Per altre informazioni, vedere Impostazioni di Visual Studio Code.
  • .venv/: (facoltativo) Contiene un ambiente virtuale Python usato dallo sviluppo locale.
  • Dockerfile: (facoltativo) Usato durante la pubblicazione del progetto in un contenitore personalizzato.
  • tests/: (Facoltativo) Contiene i test case dell'app per le funzioni.
  • .funcignore: (facoltativo) Dichiara i file che non devono essere pubblicati in Azure. In genere, questo file contiene vscode/ per ignorare l'impostazione dell'editor, venv/ per ignorare l'ambiente virtuale Python locale, i test/ per ignorare i test case e local.settings.json per impedire la pubblicazione delle impostazioni dell'app locale.

Ogni funzione ha un proprio file di codice e un file di configurazione dell'associazione, function.json.

La struttura di cartelle consigliata per un progetto di funzioni Python è simile all'esempio seguente:

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

La cartella principale del progetto, <project_root>, può contenere i file seguenti:

  • .venv/: (facoltativo) Contiene un ambiente virtuale Python usato dallo sviluppo locale.
  • .vscode/: (facoltativo) Contiene la configurazione di Visual Studio Code archiviata. Per altre informazioni, vedere Impostazioni di Visual Studio Code.
  • function_app.py: percorso predefinito per tutte le funzioni e i relativi trigger e associazioni correlati.
  • additional_functions.py: (facoltativo) Qualsiasi altro file Python che contiene funzioni (in genere per il raggruppamento logico) a cui viene fatto riferimento in function_app.py tramite progetti.
  • tests/: (Facoltativo) Contiene i test case dell'app per le funzioni.
  • .funcignore: (facoltativo) Dichiara i file che non devono essere pubblicati in Azure. In genere, questo file contiene vscode/ per ignorare l'impostazione dell'editor, venv/ per ignorare l'ambiente virtuale Python locale, i test/ per ignorare i test case e local.settings.json per impedire la pubblicazione delle impostazioni dell'app locale.
  • host.json: contiene opzioni di configurazione che influiscono su tutte le funzioni in un'istanza dell'app per le funzioni. Questo file viene pubblicato in Azure. Non tutte le opzioni sono supportate durante l'esecuzione in locale. Per altre informazioni, vedere host.json.
  • local.settings.json: usato per archiviare le impostazioni e i stringa di connessione dell'app quando è in esecuzione in locale. Questo file non viene pubblicato in Azure. Per altre informazioni, vedere local.settings.file.
  • requirements.txt: contiene l'elenco dei pacchetti Python installati dal sistema durante la pubblicazione in Azure.
  • Dockerfile: (facoltativo) Usato durante la pubblicazione del progetto in un contenitore personalizzato.

Quando si distribuisce il progetto in un'app per le funzioni in Azure, l'intero contenuto della cartella principale del progetto, <project_root>, deve essere incluso nel pacchetto, ma non nella cartella stessa, il che significa che host.json deve trovarsi nella radice del pacchetto. È consigliabile gestire i test in una cartella insieme ad altre funzioni (in questo esempio, test/). Per ulteriori informazioni, consultare Testing unità.

Connessione al database

Funzioni di Azure si integra bene con Azure Cosmos DB per molti casi d'uso, tra cui IoT, e-commerce, giochi e così via.

Ad esempio, per l'origine degli eventi, i due servizi sono integrati per alimentare le architetture guidate dagli eventi usando la funzionalità del feed di modifiche di Azure Cosmos DB. Il feed di modifiche offre ai microservizi downstream la possibilità di leggere in modo affidabile e incrementale gli inserimenti e gli aggiornamenti ,ad esempio gli eventi di ordinamento. Questa funzionalità può essere usata per fornire un archivio eventi persistente come broker di messaggi per gli eventi che cambiano lo stato e guidare il flusso di lavoro di elaborazione degli ordini tra molti microservizi (che possono essere implementati come Funzioni di Azure serverless).

Architettura di riferimento per la pipeline degli ordini di Azure Cosmos DB

Per connettersi ad Azure Cosmos DB, creare prima di tutto un account, un database e un contenitore. È quindi possibile connettere il codice della funzione ad Azure Cosmos DB usando trigger e associazioni, come in questo esempio.

Per implementare una logica di app più complessa, è anche possibile usare la libreria Python per Cosmos DB. Un'implementazione di I/O asincrona è simile alla seguente:

pip install azure-cosmos
pip install aiohttp

from azure.cosmos.aio import CosmosClient
from azure.cosmos import exceptions
from azure.cosmos.partition_key import PartitionKey
import asyncio

# Replace these values with your Cosmos DB connection information
endpoint = "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 container
database_throughput = 1000

# Singleton CosmosClient instance
client = CosmosClient(endpoint, credential=key)

# Helper function to get or create database and container
async 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 items

async 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 items

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

Blueprints

Il modello di programmazione Python v2 introduce il concetto di progetti. Un progetto è una nuova classe di cui viene creata un'istanza per registrare funzioni all'esterno dell'applicazione di funzione principale. Le funzioni registrate nelle istanze del progetto non vengono indicizzate direttamente dal runtime della funzione. Per ottenere queste funzioni di progetto indicizzate, l'app per le funzioni deve registrare le funzioni dalle istanze del progetto.

L'uso dei progetti offre i vantaggi seguenti:

  • Consente di suddividere l'app per le funzioni in componenti modulari, che consente di definire funzioni in più file Python e di dividerle in componenti diversi per ogni file.
  • Fornisce interfacce estendibili di app per le funzioni pubbliche per compilare e riutilizzare le proprie API.

L'esempio seguente illustra come usare i progetti:

Prima di tutto, in un file di http_blueprint.py , viene definita una funzione attivata tramite HTTP e aggiunta a un oggetto progetto.

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 
        ) 

Successivamente, nel file function_app.py , l'oggetto progetto viene importato e le relative funzioni vengono registrate nell'app per le funzioni.

import azure.functions as func 
from http_blueprint import bp

app = func.FunctionApp() 

app.register_functions(bp) 

Nota

Durable Functions supporta anche i progetti. Per creare progetti per le app durable Functions, registrare trigger di orchestrazione, attività ed entità e associazioni client usando la azure-functions-durableBlueprint classe , come illustrato di seguito. Il progetto risultante può quindi essere registrato normalmente. Per un esempio, vedere l'esempio.

Importare il comportamento

È possibile importare moduli nel codice della funzione usando riferimenti assoluti e relativi. In base alla struttura di cartelle descritta in precedenza, le importazioni seguenti funzionano dall'interno del file <di funzione 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)

Nota

Quando si usa la sintassi di importazione assoluta, la cartella shared_code/ deve contenere un file __init__.py per contrassegnarlo come pacchetto Python.

Di seguito __app__ importare e oltre l'importazione relativa di primo livello sono deprecate, perché non sono supportate dal correttore dei tipi statici e non supportate dai framework di test 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)

Trigger e input

Gli input sono suddivisi in due categorie in Funzioni di Azure: input del trigger e altro input. Anche se sono diversi nel file function.json , l'utilizzo è identico nel codice Python. Connessione stringhe o segreti per trigger e origini di input eseguono il mapping ai valori nel file local.settings.json quando vengono eseguiti in locale ed eseguono il mapping alle impostazioni dell'applicazione quando sono in esecuzione in Azure.

Ad esempio, il codice seguente illustra la differenza tra i due input:

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

Quando viene richiamata questa funzione, la richiesta HTTP viene passata alla funzione come req. Una voce viene recuperata dall'account Archiviazione BLOB di Azure in base all'ID nell'URL di route e reso disponibile come obj nel corpo della funzione. In questo caso, l'account di archiviazione specificato è il stringa di connessione presente nell'impostazione dell'appCONNECTION_STRING.

Gli input sono suddivisi in due categorie in Funzioni di Azure: input del trigger e altro input. Anche se sono definiti usando elementi Decorator diversi, l'utilizzo è simile nel codice Python. Connessione stringhe o segreti per trigger e origini di input eseguono il mapping ai valori nel file local.settings.json quando vengono eseguiti in locale ed eseguono il mapping alle impostazioni dell'applicazione quando sono in esecuzione in Azure.

Ad esempio, il codice seguente illustra come definire un'associazione di input Archiviazione 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()}')

Quando viene richiamata questa funzione, la richiesta HTTP viene passata alla funzione come req. Una voce viene recuperata dall'account Archiviazione BLOB di Azure in base all'ID nell'URL di route e reso disponibile come obj nel corpo della funzione. In questo caso, l'account di archiviazione specificato è il stringa di connessione presente nell'impostazione dell'appSTORAGE_CONNECTION_STRING.

Per le operazioni di data binding a elevato utilizzo di dati, è consigliabile usare un account di archiviazione separato. Per altre informazioni, vedere Archiviazione indicazioni sull'account.

Associazioni di tipi SDK (anteprima)

Per selezionare trigger e associazioni, è possibile usare i tipi di dati implementati dagli SDK e dai framework di Azure sottostanti. Queste associazioni di tipi di SDK consentono di interagire con i dati di associazione come se si usasse l'SDK del servizio sottostante.

Importante

Il supporto per le associazioni dei tipi di SDK richiede il modello di programmazione Python v2.

Funzioni supporta le associazioni di tipi di Python SDK per l'archiviazione BLOB di Azure, che consente di usare i dati BLOB usando il tipo sottostante BlobClient .

Importante

Il supporto delle associazioni dei tipi di SDK per Python è attualmente in anteprima:

  • È necessario usare il modello di programmazione Python v2.
  • Attualmente sono supportati solo i tipi SDK sincroni.

Prerequisiti

Abilitare le associazioni dei tipi di SDK per l'estensione di archiviazione BLOB

  1. Aggiungere il azurefunctions-extensions-bindings-blob pacchetto di estensione al requirements.txt file nel progetto, che deve includere almeno questi pacchetti:

    azure-functions
    azurefunctions-extensions-bindings-blob
    
  2. Aggiungere questo codice al function_app.py file nel progetto, che importa le associazioni dei tipi di SDK:

    import azurefunctions.extensions.bindings.blob as blob
    

Esempi di associazioni di tipi SDK

Questo esempio illustra come ottenere da BlobClient un trigger di archiviazione BLOB (blob_trigger) e dall'associazione di input in un trigger HTTP (blob_input):

import logging

import azure.functions as func
import azurefunctions.extensions.bindings.blob as blob

app = func.FunctionApp(http_auth_level=func.AuthLevel.ANONYMOUS)

@app.blob_trigger(
    arg_name="client", path="PATH/TO/BLOB", connection="AzureWebJobsStorage"
)
def blob_trigger(client: blob.BlobClient):
    logging.info(
        f"Python blob trigger function processed blob \n"
        f"Properties: {client.get_blob_properties()}\n"
        f"Blob content head: {client.download_blob().read(size=1)}"
    )


@app.route(route="file")
@app.blob_input(
    arg_name="client", path="PATH/TO/BLOB", connection="AzureWebJobsStorage"
)
def blob_input(req: func.HttpRequest, client: blob.BlobClient):
    logging.info(
        f"Python blob input function processed blob \n"
        f"Properties: {client.get_blob_properties()}\n"
        f"Blob content head: {client.download_blob().read(size=1)}"
    )
    return "ok"

È possibile visualizzare altri esempi di associazioni dei tipi di SDK per l'archiviazione BLOB nel repository delle estensioni Python:

Flussi HTTP (anteprima)

I flussi HTTP consentono di accettare e restituire dati dagli endpoint HTTP usando le API di richiesta e risposta FastAPI abilitate nelle funzioni. Queste API consentono all'host di elaborare dati di grandi dimensioni nei messaggi HTTP come blocchi anziché leggere un intero messaggio in memoria.

Questa funzionalità consente di gestire flussi di dati di grandi dimensioni, integrazioni OpenAI, distribuire contenuto dinamico e supportare altri scenari HTTP principali che richiedono interazioni in tempo reale su HTTP. È anche possibile usare i tipi di risposta FastAPI con flussi HTTP. Senza flussi HTTP, le dimensioni delle richieste e delle risposte HTTP sono limitate dalle restrizioni di memoria che possono essere rilevate durante l'elaborazione di interi payload di messaggi in memoria.

Importante

Il supporto per i flussi HTTP richiede il modello di programmazione Python v2.

Importante

Il supporto dei flussi HTTP per Python è attualmente in anteprima e richiede l'uso del modello di programmazione Python v2.

Prerequisiti

Abilitare i flussi HTTP

I flussi HTTP sono disabilitati per impostazione predefinita. È necessario abilitare questa funzionalità nelle impostazioni dell'applicazione e aggiornare anche il codice per usare il pacchetto FastAPI.

  1. Aggiungere il azurefunctions-extensions-http-fastapi pacchetto di estensione al requirements.txt file nel progetto, che deve includere almeno questi pacchetti:

    azure-functions
    azurefunctions-extensions-http-fastapi
    
  2. Aggiungere questo codice al function_app.py file nel progetto, che importa l'estensione FastAPI:

    from azurefunctions.extensions.http.fastapi import Request, StreamingResponse
    
  3. Quando si esegue la distribuzione in Azure, aggiungere queste impostazioni dell'applicazione nell'app per le funzioni:

    • "PYTHON_ISOLATE_WORKER_DEPENDENCIES": "1"
    • "PYTHON_ENABLE_INIT_INDEXING": "1"

    Quando si esegue localmente, è anche necessario aggiungere queste stesse impostazioni al file di local.settings.json progetto.

Esempi di flussi HTTP

Dopo aver abilitato la funzionalità di streaming HTTP, è possibile creare funzioni che eseguino lo streaming dei dati tramite HTTP.

Questo esempio è una funzione attivata da HTTP che trasmette i dati di risposta HTTP. È possibile usare queste funzionalità per supportare scenari come l'invio di dati degli eventi tramite una pipeline per la visualizzazione in tempo reale o il rilevamento di anomalie in grandi set di dati e l'invio di notifiche istantanee.

import time

import azure.functions as func
from azurefunctions.extensions.http.fastapi import Request, StreamingResponse

app = func.FunctionApp(http_auth_level=func.AuthLevel.ANONYMOUS)


def generate_sensor_data():
    """Generate real-time sensor data."""
    for i in range(10):
        # Simulate temperature and humidity readings
        temperature = 20 + i
        humidity = 50 + i
        yield f"data: {{'temperature': {temperature}, 'humidity': {humidity}}}\n\n"
        time.sleep(1)


@app.route(route="stream", methods=[func.HttpMethod.GET])
async def stream_sensor_data(req: Request) -> StreamingResponse:
    """Endpoint to stream real-time sensor data."""
    return StreamingResponse(generate_sensor_data(), media_type="text/event-stream")

Questo esempio è una funzione attivata da HTTP che riceve ed elabora i dati di streaming da un client in tempo reale. Illustra le funzionalità di caricamento in streaming che possono essere utili per scenari come l'elaborazione di flussi di dati continui e la gestione dei dati degli eventi dai dispositivi IoT.

import azure.functions as func
from azurefunctions.extensions.http.fastapi import JSONResponse, Request

app = func.FunctionApp(http_auth_level=func.AuthLevel.ANONYMOUS)


@app.route(route="streaming_upload", methods=[func.HttpMethod.POST])
async def streaming_upload(req: Request) -> JSONResponse:
    """Handle streaming upload requests."""
    # Process each chunk of data as it arrives
    async for chunk in req.stream():
        process_data_chunk(chunk)

    # Once all data is received, return a JSON response indicating successful processing
    return JSONResponse({"status": "Data uploaded and processed successfully"})


def process_data_chunk(chunk: bytes):
    """Process each data chunk."""
    # Add custom processing logic here
    pass

Chiamata di flussi HTTP

È necessario usare una libreria client HTTP per effettuare chiamate di streaming agli endpoint FastAPI di una funzione. Lo strumento client o il browser in uso potrebbero non supportare in modo nativo lo streaming o restituire solo il primo blocco di dati.

È possibile usare uno script client simile al seguente per inviare dati di streaming a un endpoint HTTP:

import httpx # Be sure to add 'httpx' to 'requirements.txt'
import asyncio

async def stream_generator(file_path):
    chunk_size = 2 * 1024  # Define your own chunk size
    with open(file_path, 'rb') as file:
        while chunk := file.read(chunk_size):
            yield chunk
            print(f"Sent chunk: {len(chunk)} bytes")

async def stream_to_server(url, file_path):
    timeout = httpx.Timeout(60.0, connect=60.0)
    async with httpx.AsyncClient(timeout=timeout) as client:
        response = await client.post(url, content=stream_generator(file_path))
        return response

async def stream_response(response):
    if response.status_code == 200:
        async for chunk in response.aiter_raw():
            print(f"Received chunk: {len(chunk)} bytes")
    else:
        print(f"Error: {response}")

async def main():
    print('helloworld')
    # Customize your streaming endpoint served from core tool in variable 'url' if different.
    url = 'http://localhost:7071/api/streaming_upload'
    file_path = r'<file path>'

    response = await stream_to_server(url, file_path)
    print(response)

if __name__ == "__main__":
    asyncio.run(main())

Output

Gli output possono essere espressi sia nel valore restituito che nei parametri di output. Se è presente un solo output, è consigliabile usare il valore restituito. Per più output, è necessario usare i parametri di output.

Per usare il valore restituito di una funzione come valore di un'associazione di output, la name proprietà dell'associazione deve essere impostata su $return nel file function.json .

Per generare più output, usare il metodo set() fornito dall'interfaccia azure.functions.Out per assegnare un valore al binding. Ad esempio, la funzione seguente può eseguire il push di un messaggio in una coda e anche restituire una risposta 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

Gli output possono essere espressi sia nel valore restituito che nei parametri di output. Se è presente un solo output, è consigliabile usare il valore restituito. Per più output, è necessario usare invece i parametri di output.

Per generare più output, usare il metodo set() fornito dall'interfaccia azure.functions.Out per assegnare un valore al binding. Ad esempio, la funzione seguente può eseguire il push di un messaggio in una coda e anche restituire una risposta 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

Registrazione

L'accesso al logger di runtime di Funzioni di Azure è disponibile tramite un gestore radice logging nell'app per le funzioni. Questo logger è associato ad Application Insights e consente di contrassegnare avvisi ed errori che si verificano durante l'esecuzione della funzione.

L'esempio seguente registra un messaggio informativo quando la funzione viene richiamata tramite un trigger HTTP.

import logging

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

Sono disponibili altri metodi di registrazione che consentono di scrivere nella console a livelli di traccia diversi:

metodo Descrizione
critical(_message_) Scrive un messaggio con livello critico nel logger radice.
error(_message_) Scrive un messaggio con livello errore nel logger radice.
warning(_message_) Scrive un messaggio con livello avviso nel logger radice.
info(_message_) Scrive un messaggio con livello informativo nel logger radice.
debug(_message_) Scrive un messaggio con livello debug nel logger radice.

Per altre informazioni sulla registrazione, vedere Monitorare Funzioni di Azure.

Registrazione da thread creati

Per visualizzare i log provenienti dai thread creati, includere l'argomento context nella firma della funzione. Questo argomento contiene un attributo thread_local_storage che archivia un oggetto locale invocation_id. Questa opzione può essere impostata sull'oggetto corrente invocation_id della funzione per assicurarsi che il contesto venga modificato.

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

Registrare dati di telemetria personalizzati

Per impostazione predefinita, il runtime di Funzioni raccoglie i log e altri dati di telemetria generati dalle funzioni. Questi dati di telemetria finiscono come tracce in Application Insights. I dati di telemetria delle richieste e delle dipendenze per determinati servizi di Azure vengono raccolti anche per impostazione predefinita dai trigger e dalle associazioni.

Per raccogliere dati di telemetria delle dipendenze personalizzati e richieste personalizzate al di fuori delle associazioni, è possibile usare le estensioni Python OpenCensus. Questa estensione invia dati di telemetria personalizzati all'istanza di Application Insights. È possibile trovare un elenco delle estensioni supportate nel repository OpenCensus.

Nota

Per usare le estensioni OpenCensus Python, è necessario abilitare le estensioni del ruolo di lavoro Python nell'app per le funzioni impostando su PYTHON_ENABLE_WORKER_EXTENSIONS1. È anche necessario passare all'uso del stringa di connessione di Application Insights aggiungendo l'impostazione APPLICATIONINSIGHTS_CONNECTION_STRING alle impostazioni dell'applicazione, se non è già presente.

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

Trigger HTTP

Il trigger HTTP viene definito nel file function.json . Il valore name del binding deve corrispondere al parametro denominato nella funzione. Negli esempi precedenti viene usato il nome di binding req. Questo parametro è un oggetto HttpRequest e viene restituito un oggetto HttpResponse.

Dall'oggetto HttpRequest è possibile ottenere intestazioni di richieste, parametri di query, parametri di route e il corpo del messaggio.

L'esempio seguente fa riferimento al modello di trigger HTTP per 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 questa funzione si ottiene il valore del parametro di name query dal params parametro dell'oggetto HttpRequest . Il corpo del messaggio con codifica JSON viene letto usando il get_json metodo .

Allo stesso modo, è possibile impostare status_code e headers per il messaggio di risposta nell'oggetto HttpResponse restituito.

Il trigger HTTP viene definito come metodo che accetta un parametro di associazione denominato, ovvero un oggetto HttpRequest e restituisce un oggetto HttpResponse . Applicare l'elemento function_name Decorator al metodo per definire il nome della funzione, mentre l'endpoint HTTP viene impostato applicando l'elemento route Decorator.

Questo esempio proviene dal modello di trigger HTTP per il modello di programmazione Python v2, dove il nome del parametro di associazione è req. Si tratta del codice di esempio fornito quando si crea una funzione usando Funzioni di Azure Core Tools o 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
        )

Dall'oggetto HttpRequest è possibile ottenere intestazioni di richieste, parametri di query, parametri di route e il corpo del messaggio. In questa funzione si ottiene il valore del parametro di name query dal params parametro dell'oggetto HttpRequest . Il corpo del messaggio con codifica JSON viene letto usando il get_json metodo .

Allo stesso modo, è possibile impostare status_code e headers per il messaggio di risposta nell'oggetto HttpResponse restituito.

Per passare un nome in questo esempio, incollare l'URL fornito quando si esegue la funzione e quindi aggiungerlo con "?name={name}".

Framework Web

È possibile usare framework compatibili con Web Server Gateway Interface (WSGI) e ASGI (Asynchronous Server Gateway Interface), ad esempio Flask e FastAPI, con le funzioni Python attivate da HTTP. Questa sezione illustra come modificare le funzioni per supportare questi framework.

Prima di tutto, il file function.json deve essere aggiornato per includere un route nel trigger HTTP, come illustrato nell'esempio seguente:

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

Il file host.json deve essere aggiornato anche per includere un routePrefixhttp , come illustrato nell'esempio seguente:

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

Aggiornare il file di codice Python init.py, a seconda dell'interfaccia usata dal framework. L'esempio seguente illustra un approccio del gestore ASGI o un approccio wrapper WSGI per Flask:

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)

È possibile usare framework compatibili con ASGI (Asynchronous Server Gateway Interface) e WSGI (Web Server Gateway Interface), ad esempio Flask e FastAPI, con le funzioni Python attivate da HTTP. È prima necessario aggiornare il file host.json per includere un routePrefixhttp , come illustrato nell'esempio seguente:

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

Il codice del framework è simile all'esempio seguente:

AsgiFunctionApp è la classe di app per le funzioni di primo livello per la costruzione di funzioni HTTP ASGI.

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

Scalabilità e prestazioni

Per le procedure consigliate per la scalabilità e le prestazioni per le app per le funzioni Python, vedere l'articolo Relativo al ridimensionamento e alle prestazioni di Python.

Contesto

Per ottenere il contesto di chiamata di una funzione quando è in esecuzione, includere l'argomento context nella relativa firma.

Ad esempio:

import azure.functions


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

La Context classe ha gli attributi stringa seguenti:

Attributo Descrizione
function_directory Directory in cui la funzione è in esecuzione.
function_name Nome della funzione.
invocation_id ID della chiamata alla funzione corrente.
thread_local_storage Archiviazione locale del thread della funzione. Contiene un elemento locale invocation_id per la registrazione da thread creati.
trace_context Contesto per la traccia distribuita. Per ulteriori informazioni, vedere Trace Context.
retry_context Contesto per i tentativi alla funzione. Per ulteriori informazioni, vedere retry-policies.

Variabili globali

Non è garantito che lo stato dell'app venga mantenuto per le esecuzioni future. Tuttavia, il runtime di Funzioni di Azure spesso riutilizza lo stesso processo per più esecuzioni della stessa app. Per memorizzare nella cache i risultati di un calcolo costoso, dichiararlo come variabile globale.

CACHED_DATA = None


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

    # ... use CACHED_DATA in code

Variabili di ambiente

In Funzioni di Azure, le impostazioni dell'applicazione, ad esempio le stringa di connessione del servizio, vengono esposte come variabili di ambiente durante l'esecuzione. Esistono due modi principali per accedere a queste impostazioni nel codice.

metodo Descrizione
os.environ["myAppSetting"] Prova a ottenere l'impostazione dell'applicazione in base al nome della chiave e genera un errore in caso di esito negativo.
os.getenv("myAppSetting") Prova a ottenere l'impostazione dell'applicazione in base al nome della chiave e restituisce null quando ha esito negativo.

Entrambi questi modi richiedono di dichiarare import os.

L'esempio seguente usa os.environ["myAppSetting"] per ottenere l'impostazione dell'applicazione, con la chiave denominata 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}')

Per lo sviluppo locale, le impostazioni dell'applicazione vengono mantenute nel file local.settings.json.

In Funzioni di Azure, le impostazioni dell'applicazione, ad esempio le stringa di connessione del servizio, vengono esposte come variabili di ambiente durante l'esecuzione. Esistono due modi principali per accedere a queste impostazioni nel codice.

metodo Descrizione
os.environ["myAppSetting"] Prova a ottenere l'impostazione dell'applicazione in base al nome della chiave e genera un errore in caso di esito negativo.
os.getenv("myAppSetting") Prova a ottenere l'impostazione dell'applicazione in base al nome della chiave e restituisce null quando ha esito negativo.

Entrambi questi modi richiedono di dichiarare import os.

L'esempio seguente usa os.environ["myAppSetting"] per ottenere l'impostazione dell'applicazione, con la chiave denominata 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}')

Per lo sviluppo locale, le impostazioni dell'applicazione vengono mantenute nel file local.settings.json.

Quando si usa il nuovo modello di programmazione, abilitare l'impostazione dell'app seguente nel file local.settings.json , come illustrato di seguito:

"AzureWebJobsFeatureFlags": "EnableWorkerIndexing"

Quando si distribuisce la funzione, questa impostazione non viene creata automaticamente. È necessario creare in modo esplicito questa impostazione nell'app per le funzioni in Azure perché venga eseguita usando il modello v2.

Versione di Python

Funzioni di Azure supporta le versioni di Python seguenti:

Versione di Funzioni Versioni di Python*
4.x 3.11
3.10
3.9
3.8
3.7
3.x 3.9
3.8
3.7

* Distribuzioni ufficiali di Python

Per richiedere una versione specifica di Python quando si crea l'app per le funzioni in Azure, usare l'opzione --runtime-version del comando az functionapp create. La versione del runtime di Funzioni è impostata dall'opzione --functions-version. La versione di Python viene impostata quando viene creata l'app per le funzioni e non può essere modificata per le app in esecuzione in un piano a consumo.

Il runtime usa la versione di Python disponibile quando viene eseguita in locale.

Modifica della versione di Python

Per impostare un'app per le funzioni Python su una versione specifica del linguaggio, è necessario specificare la lingua e la versione del linguaggio nel LinuxFxVersion campo nella configurazione del sito. Ad esempio, per modificare l'app Python per usare Python 3.8, impostare su linuxFxVersionpython|3.8.

Per informazioni su come visualizzare e modificare l'impostazione del linuxFxVersion sito, vedere Come impostare come destinazione Funzioni di Azure versioni di runtime.

Per altre informazioni generali, vedere i criteri di supporto del runtime di Funzioni di Azure e i linguaggi supportati in Funzioni di Azure.

Gestione pacchetti

Quando si sviluppa in locale usando Core Tools o Visual Studio Code, aggiungere i nomi e le versioni dei pacchetti necessari al file requirements.txt e quindi installarli usando pip.

Ad esempio, è possibile usare il file di requirements.txt seguente e pip il comando per installare il requests pacchetto da PyPI.

requests==2.19.1
pip install -r requirements.txt

Quando si eseguono le funzioni in un piano di servizio app, le dipendenze definite in requirements.txt hanno la precedenza sui moduli Python predefiniti, ad esempio logging. Questa precedenza può causare conflitti quando i moduli predefiniti hanno gli stessi nomi delle directory nel codice. Quando si esegue in un piano a consumo o in un piano Elastic Premium, i conflitti sono meno probabili perché le dipendenze non sono classificate in ordine di priorità per impostazione predefinita.

Per evitare problemi in esecuzione in un piano di servizio app, non assegnare alle directory lo stesso nome dei moduli nativi Python e non includere librerie native Python nel file requirements.txt del progetto.

Pubblicazione in Azure

Quando si è pronti per la pubblicazione, assicurarsi che tutte le dipendenze disponibili pubblicamente siano elencate nel file requirements.txt . È possibile individuare questo file nella radice della directory del progetto.

È possibile trovare i file di progetto e le cartelle esclusi dalla pubblicazione, inclusa la cartella dell'ambiente virtuale, nella directory radice del progetto.

Sono supportate tre azioni di compilazione per la pubblicazione del progetto Python in Azure: compilazione remota, compilazione locale e compilazione usando dipendenze personalizzate.

È anche possibile usare Azure Pipelines per compilare le dipendenze e pubblicare usando il recapito continuo (CD). Per altre informazioni, vedere Recapito continuo con Azure Pipelines.

Compilazione remota

Quando si usa la compilazione remota, le dipendenze ripristinate nel server e le dipendenze native corrispondono all'ambiente di produzione. In questo modo si genera un pacchetto di distribuzione più piccolo da caricare. Usare la compilazione remota quando si sviluppano app Python in Windows. Se il progetto ha dipendenze personalizzate, è possibile usare la compilazione remota con URL di indice aggiuntivo.

Le dipendenze vengono ottenute in modalità remota in base al contenuto del file requirements.txt . La compilazione remota è il metodo di compilazione consigliato. Per impostazione predefinita, Core Tools richiede una compilazione remota quando si usa il comando seguente func azure functionapp publish per pubblicare il progetto Python in Azure.

func azure functionapp publish <APP_NAME>

Ricordare di sostituire <APP_NAME> con il nome dell'app per le funzioni in Azure.

Anche l'estensione Funzioni di Azure per Visual Studio Code richiede una compilazione remota per impostazione predefinita.

Compilazione locale

Le dipendenze vengono ottenute localmente in base al contenuto del file di requirements.txt . È possibile impedire l'esecuzione di una compilazione remota usando il comando seguente func azure functionapp publish per pubblicare con una compilazione locale:

func azure functionapp publish <APP_NAME> --build local

Ricordare di sostituire <APP_NAME> con il nome dell'app per le funzioni in Azure.

Quando si usa l'opzione --build local , le dipendenze del progetto vengono lette dal file di requirements.txt e tali pacchetti dipendenti vengono scaricati e installati localmente. I file e le dipendenze del progetto vengono distribuiti dal computer locale in Azure. Questo comporta il caricamento di un pacchetto di distribuzione di dimensioni maggiori in Azure. Se per qualche motivo non è possibile ottenere il file requirements.txt usando Core Tools, è necessario usare l'opzione dipendenze personalizzate per la pubblicazione.

Non è consigliabile usare build locali quando si sviluppa in locale in Windows.

Dipendenze personalizzate

Quando il progetto presenta dipendenze non trovate nell'indice dei pacchetti Python, esistono due modi per compilare il progetto. Il primo modo, il metodo di compilazione , dipende dalla modalità di compilazione del progetto.

Compilazione remota con URL di indice aggiuntivo

Quando i pacchetti sono disponibili da un indice di pacchetto personalizzato accessibile, usare una compilazione remota. Prima di pubblicare, assicurarsi di creare un'impostazione dell'app denominataPIP_EXTRA_INDEX_URL. Il valore di questa impostazione è l'URL dell'indice del pacchetto personalizzato. L'uso di questa impostazione indica alla compilazione remota di eseguire pip install usando l'opzione --extra-index-url . Per altre informazioni, vedere la documentazione di Pythonpip install.

È anche possibile usare le credenziali di autenticazione di base con gli URL di indice del pacchetto aggiuntivi. Per altre informazioni, vedere Credenziali di autenticazione di base nella documentazione di Python.

Installare i pacchetti locali

Se il progetto usa pacchetti non disponibili pubblicamente per gli strumenti, puoi renderli disponibili per la tua app inserendoli nella directory __app__/.python_packages . Prima di pubblicare, eseguire il comando seguente per installare le dipendenze in locale:

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

Quando si usano dipendenze personalizzate, è consigliabile usare l'opzione --no-build di pubblicazione, perché le dipendenze sono già state installate nella cartella del progetto.

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

Ricordare di sostituire <APP_NAME> con il nome dell'app per le funzioni in Azure.

Unit test

Le funzioni scritte in Python possono essere testate come altro codice Python usando framework di test standard. Per la maggior parte dei binding, è possibile creare un oggetto di input fittizio creando un'istanza di una classe appropriata dal pacchetto azure.functions. Poiché il azure.functions pacchetto non è immediatamente disponibile, assicurarsi di installarlo tramite il file requirements.txt come descritto nella sezione di gestione dei pacchetti precedente.

Con my_second_function come esempio, di seguito è riportato un test fittizio di una funzione attivata tramite HTTP:

Creare prima di tutto un <file project_root>/my_second_function/function.json e quindi definire questa funzione come trigger HTTP.

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

Successivamente, è possibile implementare my_second_function e shared_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

È possibile iniziare a scrivere test case per il trigger 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',)

All'interno della .venv cartella dell'ambiente virtuale Python installare il framework di test Python preferito, ad esempio pip install pytest. pytest tests Eseguire quindi per controllare il risultato del test.

Creare prima di tutto il <file project_root>/function_app.py e implementare la my_second_function funzione come trigger HTTP e 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

È possibile iniziare a scrivere test case per il trigger 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',
    )

All'interno della cartella dell'ambiente virtuale .venv Python installare il framework di test Python preferito, ad esempio pip install pytest. pytest tests Eseguire quindi per controllare il risultato del test.

File temporanei

Il tempfile.gettempdir() metodo restituisce una cartella temporanea, che in Linux è /tmp. L'applicazione può usare questa directory per archiviare i file temporanei generati e usati dalle funzioni quando sono in esecuzione.

Importante

Non è garantito che i file scritti nella directory temporanea vengano mantenuti tra le chiamate. Durante l'aumento di istanze, i file temporanei non vengono condivisi tra le istanze.

Nell'esempio seguente viene creato un file temporaneo denominato nella directory temporanea (/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)

È consigliabile gestire i test in una cartella separata dalla cartella del progetto. Questa azione impedisce di distribuire il codice di test con l'app.

Librerie preinstallate

Alcune librerie sono disponibili con il runtime delle funzioni Python.

Libreria standard Python

La libreria standard Python contiene un elenco di moduli Python predefiniti forniti con ogni distribuzione python. La maggior parte di queste librerie consente di accedere alle funzionalità di sistema, ad esempio input/output dei file (I/O). Nei sistemi Windows queste librerie vengono installate con Python. Nei sistemi basati su Unix, vengono forniti dalle raccolte di pacchetti.

Per visualizzare la libreria per la versione di Python, passare a:

Funzioni di Azure dipendenze del ruolo di lavoro Python

Il ruolo di lavoro Python Funzioni di Azure richiede un set specifico di librerie. È anche possibile usare queste librerie nelle funzioni, ma non fanno parte dello standard Python. Se le funzioni si basano su una di queste librerie, potrebbero non essere disponibili per il codice quando è in esecuzione all'esterno di Funzioni di Azure. Un elenco dettagliato delle dipendenze è disponibile nella sezione "install_requires" del file di setup.py .

Nota

Se il file di requirements.txt dell'app per le funzioni contiene una azure-functions-worker voce, rimuoverlo. Il ruolo di lavoro delle funzioni viene gestito automaticamente dalla piattaforma Funzioni di Azure e lo aggiorniamo regolarmente con nuove funzionalità e correzioni di bug. L'installazione manuale di una versione precedente del ruolo di lavoro nel file requirements.txt potrebbe causare problemi imprevisti.

Nota

Se il pacchetto contiene alcune librerie che potrebbero essere in conflitto con le dipendenze del ruolo di lavoro (ad esempio protobuf, tensorflow o grpcio), configurare PYTHON_ISOLATE_WORKER_DEPENDENCIES su 1 nelle impostazioni dell'app per impedire all'applicazione di fare riferimento alle dipendenze del ruolo di lavoro.

Libreria Python di Funzioni di Azure

Ogni aggiornamento del ruolo di lavoro Python include una nuova versione della libreria Python Funzioni di Azure (azure.functions). Questo approccio semplifica l'aggiornamento continuo delle app per le funzioni Python, perché ogni aggiornamento è compatibile con le versioni precedenti. Per un elenco delle versioni di questa libreria, passare ad azure-functions PyPi.

La versione della libreria di runtime è corretta da Azure e non può essere sottoposta a override da requirements.txt. La azure-functions voce in requirements.txt è solo per linting e customer awareness.

Usare il codice seguente per tenere traccia della versione effettiva della libreria di funzioni Python nel runtime:

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

Librerie di sistema di runtime

Per un elenco delle librerie di sistema preinstallate nelle immagini Docker del ruolo di lavoro Python, vedere quanto segue:

Runtime di Funzioni Versione Debian Versioni di Python
Versione 3.x Buster Python 3.7
Python 3.8
Python 3.9

Estensioni del ruolo di lavoro Python

Il processo di lavoro Python eseguito in Funzioni di Azure consente di integrare librerie di terze parti nell'app per le funzioni. Queste librerie di estensioni fungono da middleware in grado di inserire operazioni specifiche durante il ciclo di vita dell'esecuzione della funzione.

Le estensioni vengono importate nel codice della funzione in modo analogo a un modulo di libreria Python standard. Le estensioni vengono eseguite in base agli ambiti seguenti:

Ambito Descrizione
Livello applicazione Quando viene importato in qualsiasi trigger di funzione, l'estensione si applica a ogni esecuzione di funzione nell'app.
Livello di funzione L'esecuzione è limitata solo al trigger di funzione specifico in cui viene importato.

Esaminare le informazioni per ogni estensione per altre informazioni sull'ambito in cui viene eseguita l'estensione.

Le estensioni implementano un'interfaccia di estensione del ruolo di lavoro Python. Questa azione consente al processo di lavoro Python di chiamare il codice di estensione durante il ciclo di vita di esecuzione della funzione. Per altre informazioni, vedere Creare estensioni.

Uso delle estensioni

È possibile usare una libreria di estensioni del ruolo di lavoro Python nelle funzioni Python seguendo questa procedura:

  1. Aggiungere il pacchetto di estensione nel file requirements.txt per il progetto.
  2. Installare la libreria nell'app.
  3. Aggiungere le impostazioni dell'applicazione seguenti:
    • Localmente: immettere "PYTHON_ENABLE_WORKER_EXTENSIONS": "1" nella Values sezione del file local.settings.json.
    • Azure: immettere PYTHON_ENABLE_WORKER_EXTENSIONS=1 le impostazioni dell'app.
  4. Importare il modulo di estensione nel trigger di funzione.
  5. Configurare l'istanza dell'estensione, se necessario. I requisiti di configurazione devono essere indicati nella documentazione dell'estensione.

Importante

Le librerie di estensioni del ruolo di lavoro Python di terze parti non sono supportate o garanzie da Microsoft. È necessario assicurarsi che tutte le estensioni usate nell'app per le funzioni siano attendibili e che si rischia di usare un'estensione dannosa o scritta male.

Terze parti devono fornire documentazione specifica su come installare e usare le estensioni nell'app per le funzioni. Per un esempio di base su come usare un'estensione, vedere Uso dell'estensione.

Ecco alcuni esempi di uso delle estensioni in un'app per le funzioni, in base all'ambito:

# <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

Creazione di estensioni

Le estensioni vengono create da sviluppatori di librerie di terze parti che hanno creato funzionalità che possono essere integrate in Funzioni di Azure. Uno sviluppatore di estensioni progetta, implementa e rilascia pacchetti Python che contengono logica personalizzata progettata specificamente per l'esecuzione nel contesto dell'esecuzione della funzione. Queste estensioni possono essere pubblicate nel registro PyPI o nei repository GitHub.

Per informazioni su come creare, creare pacchetti, pubblicare e usare un pacchetto di estensione del ruolo di lavoro Python, vedere Sviluppare estensioni del ruolo di lavoro Python per Funzioni di Azure.

Estensioni a livello di applicazione

Estensione ereditata dalle AppExtensionBase esecuzioni in un ambito dell'applicazione .

AppExtensionBase espone i seguenti metodi di classe astratta da implementare:

metodo Descrizione
init Chiamato dopo l'importazione dell'estensione.
configure Chiamato dal codice della funzione quando è necessario configurare l'estensione.
post_function_load_app_level Chiamato subito dopo il caricamento della funzione. Il nome della funzione e la directory della funzione vengono passati all'estensione. Tenere presente che la directory della funzione è di sola lettura e qualsiasi tentativo di scrittura in un file locale in questa directory ha esito negativo.
pre_invocation_app_level Chiamato subito prima dell'attivazione della funzione. Il contesto della funzione e gli argomenti di chiamata di funzione vengono passati all'estensione. In genere è possibile passare altri attributi nell'oggetto contesto per il codice della funzione da utilizzare.
post_invocation_app_level Chiamato subito dopo il completamento dell'esecuzione della funzione. Il contesto della funzione, gli argomenti di chiamata della funzione e l'oggetto restituito della chiamata vengono passati all'estensione. Questa implementazione è un buon posto per verificare se l'esecuzione degli hook del ciclo di vita è riuscita.

Estensioni a livello di funzione

Un'estensione che eredita da FuncExtensionBase viene eseguita in un trigger di funzione specifico.

FuncExtensionBase espone i seguenti metodi di classe astratta per le implementazioni:

metodo Descrizione
__init__ Costruttore dell'estensione. Viene chiamato quando un'istanza di estensione viene inizializzata in una funzione specifica. Quando si implementa questo metodo astratto, è possibile accettare un filename parametro e passarlo al metodo super().__init__(filename) dell'elemento padre per la registrazione corretta dell'estensione.
post_function_load Chiamato subito dopo il caricamento della funzione. Il nome della funzione e la directory della funzione vengono passati all'estensione. Tenere presente che la directory della funzione è di sola lettura e qualsiasi tentativo di scrittura in un file locale in questa directory ha esito negativo.
pre_invocation Chiamato subito prima dell'attivazione della funzione. Il contesto della funzione e gli argomenti di chiamata di funzione vengono passati all'estensione. In genere è possibile passare altri attributi nell'oggetto contesto per il codice della funzione da utilizzare.
post_invocation Chiamato subito dopo il completamento dell'esecuzione della funzione. Il contesto della funzione, gli argomenti di chiamata della funzione e l'oggetto restituito della chiamata vengono passati all'estensione. Questa implementazione è un buon posto per verificare se l'esecuzione degli hook del ciclo di vita è riuscita.

Condivisione di risorse tra le origini

Funzioni di Azure supporta la condivisione di risorse tra origini (CORS, Cross-Origin Resource Sharing). La funzionalità CORS è configurata nel portale e tramite l'interfaccia della riga di comando di Azure. L'elenco delle origini consentite per CORS si applica a livello di app per le funzioni. Con la funzionalità CORS abilitata, le risposte includono l'intestazione Access-Control-Allow-Origin. Per altre informazioni, vedere Utilizzare la condivisione di risorse tra origini.

La condivisione di risorse tra le origini (CORS) è completamente supportata per le app per le funzioni Python.

Async

Per impostazione predefinita, un'istanza host per Python può elaborare una sola chiamata di funzione alla volta. Questo perché Python è un runtime a thread singolo. Per un'app per le funzioni che elabora un numero elevato di eventi di I/O o che è associata l'I/O, è possibile migliorare significativamente le prestazioni eseguendo funzioni in modo asincrono. Per altre informazioni, vedere Migliorare le prestazioni delle app Python in Funzioni di Azure.

Memoria condivisa (anteprima)

Per migliorare la velocità effettiva, Funzioni di Azure consente al ruolo di lavoro del linguaggio Python out-of-process di condividere la memoria con il processo host di Funzioni. Quando l'app per le funzioni raggiunge i colli di bottiglia, è possibile abilitare la memoria condivisa aggiungendo un'impostazione dell'applicazione denominata FUNCTIONS_WORKER_SHARED_MEMORY_DATA_TRANSFER_ENABLED con il valore 1. Con la memoria condivisa abilitata, è quindi possibile usare l'impostazione DOCKER_SHM_SIZE per impostare la memoria condivisa su un valore simile 268435456a , che equivale a 256 MB.

Ad esempio, è possibile abilitare la memoria condivisa per ridurre i colli di bottiglia quando si usano associazioni blob Archiviazione per trasferire payload superiori a 1 MB.

Questa funzionalità è disponibile solo per le app per le funzioni in esecuzione nei piani Premium e Dedicato (app Azure Service). Per altre informazioni, vedere Memoria condivisa.

Problemi noti e domande frequenti

Ecco due guide alla risoluzione dei problemi comuni:

Di seguito sono riportate due guide alla risoluzione dei problemi noti relativi al modello di programmazione v2:

Tutti i problemi noti e le richieste di funzionalità vengono rilevati in un elenco di problemi di GitHub. Se si verifica un problema e non è possibile trovare il problema in GitHub, aprire un nuovo problema e includere una descrizione dettagliata del problema.

Passaggi successivi

Per ulteriori informazioni, vedi le seguenti risorse:

Problemi con l'uso di Python? Dicci cosa sta succedendo.