Azure Functions Python developer guide

This article is an introduction to developing Azure Functions using Python. The content below assumes that you've already read the Azure Functions developers guide.

Note

Python for Azure Functions is currently in preview. To receive important updates, subscribe to the Azure App Service announcements repository on GitHub.

Programming model

An Azure Function should be a stateless method in your Python script that processes input and produces output. By default, the runtime expects the method to be implemented as a global method called main() in the __init__.py file.

You can change the default configuration by specifying the scriptFile and entryPoint properties in the function.json file. For example, the function.json below tells the runtime to use the customentry() method in the main.py file, as the entry point for your Azure Function.

{
  "scriptFile": "main.py",
  "entryPoint": "customentry",
  ...
}

Data from triggers and bindings is bound to the function via method attributes using the name property defined in the function.json file. For example, the function.json below describes a simple function triggered by an HTTP request named req:

{
  "bindings": [
    {
      "name": "req",
      "direction": "in",
      "type": "httpTrigger",
      "authLevel": "anonymous"
    },
    {
      "name": "$return",
      "direction": "out",
      "type": "http"
    }
  ]
}

The __init__.py file contains the following function code:

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

Optionally, to leverage the intellisense and auto-complete features provided by your code editor, you can also declare the attribute types and return type in the function using Python type annotations.

import azure.functions


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

Use the Python annotations included in the azure.functions.* package to bind input and outputs to your methods.

Folder structure

The folder structure for a Python Functions project looks like the following:

 FunctionApp
 | - MyFirstFunction
 | | - __init__.py
 | | - function.json
 | - MySecondFunction
 | | - __init__.py
 | | - function.json
 | - SharedCode
 | | - myFirstHelperFunction.py
 | | - mySecondHelperFunction.py
 | - host.json
 | - requirements.txt

There's a shared host.json file that can be used to configure the function app. Each function has its own code file and binding configuration file (function.json).

Shared code should be kept in a separate folder. To reference modules in the SharedCode folder, you can use the following syntax:

from __app__.SharedCode import myFirstHelperFunction

When deploying a Function project to your function app in Azure, the entire content of the FunctionApp folder should be included in the package, but not the folder itself.

Triggers and Inputs

Inputs are divided into two categories in Azure Functions: trigger input and additional input. Although they are different in the function.json file, usage is identical in Python code. Connection strings or secrets for trigger and input sources map to values in the local.settings.json file when running locally, and the application settings when running in Azure.

For example, the following code demonstrates the difference between the two:

// 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": "AzureWebJobsStorage"
    }
  ]
}
// local.settings.json
{
  "IsEncrypted": false,
  "Values": {
    "FUNCTIONS_WORKER_RUNTIME": "python",
    "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()}')

When the function is invoked, the HTTP request is passed to the function as req. An entry will be retrieved from the Azure Blob Storage based on the ID in the route URL and made available as obj in the function body. Here the storage account specified is the connection string found in AzureWebJobsStorage which is the same storage account used by the function app.

Outputs

Output can be expressed both in return value and output parameters. If there's only one output, we recommend using the return value. For multiple outputs, you'll have to use output parameters.

To use the return value of a function as the value of an output binding, the name property of the binding should be set to $return in function.json.

To produce multiple outputs, use the set() method provided by the azure.functions.Out interface to assign a value to the binding. For example, the following function can push a message to a queue and also return an HTTP response.

{
  "scriptFile": "__init__.py",
  "bindings": [
    {
      "name": "req",
      "direction": "in",
      "type": "httpTrigger",
      "authLevel": "anonymous"
    },
    {
      "name": "msg",
      "direction": "out",
      "type": "queue",
      "queueName": "outqueue",
      "connection": "AzureWebJobsStorage"
    },
    {
      "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

Logging

Access to the Azure Functions runtime logger is available via a root logging handler in your function app. This logger is tied to Application Insights and allows you to flag warnings and errors encountered during the function execution.

The following example logs an info message when the function is invoked via an HTTP trigger.

import logging


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

Additional logging methods are available that let you write to the console at different trace levels:

Method Description
logging.critical(message) Writes a message with level CRITICAL on the root logger.
logging.error(message) Writes a message with level ERROR on the root logger.
logging.warning(message) Writes a message with level WARNING on the root logger.
logging.info(message) Writes a message with level INFO on the root logger.
logging.debug(message) Writes a message with level DEBUG on the root logger.

Async

We recommend that you write your Azure Function as an asynchronous coroutine using the async def statement.

# Will be run with asyncio directly


async def main():
    await some_nonblocking_socket_io_op()

If the main() function is synchronous (no async qualifier) we automatically run the function in an asyncio thread-pool.

# Would be run in an asyncio thread-pool


def main():
    some_blocking_socket_io()

Context

To get the invocation context of a function during execution, include the context argument in its signature.

For example:

import azure.functions


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

The Context class has the following methods:

function_directory
The directory in which the function is running.

function_name
Name of the function.

invocation_id
ID of the current function invocation.

Global variables

It is not guaranteed that the state of your app will be preserved for future executions. However, the Azure Functions runtime often reuses the same process for multiple executions of the same app. In order to cache the results of an expensive computation, declare it as a global variable.

CACHED_DATA = None


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

    # ... use CACHED_DATA in code

Python version and package management

Currently, Azure Functions only supports Python 3.6.x (official CPython distribution).

When developing locally using the Azure Functions Core Tools or Visual Studio Code, add the names and versions of the required packages to the requirements.txt file and install them using pip.

For example, the following requirements file and pip command can be used to install the requests package from PyPI.

requests==2.19.1
pip install -r requirements.txt

Publishing to Azure

When you're ready to publish, make sure that all your dependencies are listed in the requirements.txt file, which is located at the root of your project directory. If you're using a package that requires a compiler and does not support the installation of manylinux-compatible wheels from PyPI, publishing to Azure will fail with the following error:

There was an error restoring dependencies.ERROR: cannot install <package name - version> dependency: binary dependencies without wheels are not supported.  
The terminal process terminated with exit code: 1

To automatically build and configure the required binaries, install Docker on your local machine and run the following command to publish using the Azure Functions Core Tools (func). Remember to replace <app name> with the name of your function app in Azure.

func azure functionapp publish <app name> --build-native-deps

Underneath the covers, Core Tools will use docker to run the mcr.microsoft.com/azure-functions/python image as a container on your local machine. Using this environment, it'll then build and install the required modules from source distribution, before packaging them up for final deployment to Azure.

To build your dependencies and publish using a continuous delivery (CD) system, use Azure DevOps Pipelines.

Unit Testing

Functions written in Python can be tested like other Python code using standard testing frameworks. For most bindings, it's possible to create a mock input object by creating an instance of an appropriate class from the azure.functions package. Since the azure.functions package is not immediately available, be sure to install it via your requirements.txt file as described in Python version and package management section above.

For example, following is a mock test of an HTTP triggered function:

{
  "scriptFile": "httpfunc.py",
  "entryPoint": "my_function",
  "bindings": [
    {
      "authLevel": "function",
      "type": "httpTrigger",
      "direction": "in",
      "name": "req",
      "methods": [
        "get",
        "post"
      ]
    },
    {
      "type": "http",
      "direction": "out",
      "name": "$return"
    }
  ]
}
# myapp/httpfunc.py
import azure.functions as func
import logging

def my_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}")
    else:
        return func.HttpResponse(
             "Please pass a name on the query string or in the request body",
             status_code=400
        )
# myapp/test_httpfunc.py
import unittest

import azure.functions as func
from httpfunc import my_function


class TestFunction(unittest.TestCase):
    def test_my_function(self):
        # Construct a mock HTTP request.
        req = func.HttpRequest(
            method='GET',
            body=None,
            url='/api/HttpTrigger',
            params={'name': 'Test'})

        # Call the function.
        resp = my_function(req)

        # Check the output.
        self.assertEqual(
            resp.get_body(),
            b'Hello Test',
        )

Here is another example, with a queue triggered function:

# myapp/__init__.py
import azure.functions as func


def my_function(msg: func.QueueMessage) -> str:
    return f'msg body: {msg.get_body().decode()}'
# myapp/test_func.py
import unittest

import azure.functions as func
from . import my_function


class TestFunction(unittest.TestCase):
    def test_my_function(self):
        # Construct a mock Queue message.
        req = func.QueueMessage(
            body=b'test')

        # Call the function.
        resp = my_function(req)

        # Check the output.
        self.assertEqual(
            resp,
            'msg body: test',
        )

Known issues and FAQ

All known issues and feature requests are tracked using GitHub issues list. If you run into a problem and can't find the issue in GitHub, open a new issue and include a detailed description of the problem.

Next steps

For more information, see the following resources: