Python Azure Function Debugging in Conda virtual environment

Creating virtual environment

$ mkdir python_demo
$ cd python_demo

don't forget to update name of environment name, and i added pip, ptvsd packages to debug azure function.

$ cat > environment.yml <<EOL
name: python_demo-3.9
channels:
  - defaults
dependencies:
  - python=3.9
  - pip
  - pip:
    - ptvsd
EOL
$ conda env create -f environment.yml

Collecting package metadata (repodata.json): done
Solving environment: done

Downloading and Extracting Packages

Preparing transaction: done
Verifying transaction: done
Executing transaction: done
Installing pip dependencies: - Ran pip subprocess with arguments:
['/opt/homebrew/Caskroom/miniconda/base/envs/python_azure_function_conda_virtual_env-3.9/bin/python', '-m', 'pip', 'install', '-U', '-r', '/Users/kenanhancer/Documents/projects/python-projects-kenanhancer/python_azure_function_conda_virtual_env/condaenv.5uk0y5ie.requirements.txt', '--exists-action=b']
Pip subprocess output:
Collecting ptvsd (from -r /Users/kenanhancer/Documents/projects/python-projects-kenanhancer/python_azure_function_conda_virtual_env/condaenv.5uk0y5ie.requirements.txt (line 1))
  Using cached ptvsd-4.3.2-py2.py3-none-any.whl (4.9 MB)
Installing collected packages: ptvsd
Successfully installed ptvsd-4.3.2

done
#
# To activate this environment, use
#
#     $ conda activate python_azure_function_conda_virtual_env-3.9
#
# To deactivate an active environment, use
#
#     $ conda deactivate

Activating virtual environment

You need to use your virtual environment name instead of python_demo-3.9

$ conda activate python_demo-3.9

Creating Azure Function Project

$ func init --python -m V2

Creating Azure Function Triggers

$ func new --name HttpExample --template "HTTP trigger" --authlevel "anonymous"
$ func new --name TimerTrigger --template "Timer trigger"
$ func new --name QueueTrigger --template "Queue trigger" --authlevel "anonymous"
$ func new --name BlobTrigger --template "Blob trigger"
$ func new --name CosmosDBTrigger --template "Cosmos DB trigger"
$ func new --name ServiceBusQueueTrigger --template "Service Bus Queue trigger"
$ func new --name ServiceBusTopicTrigger --template "Service Bus Topic trigger"
$ func new --name EventHubTrigger --template "Event Hub trigger"
$ func new --name EventGridTrigger --template "Event Grid trigger"
$ func new --name IoTHubTrigger --template "IoT Hub (Event Hub) trigger"
$ func new --name SignalRTrigger --template "SignalR Service trigger"
$ func new --name DurableOrchestratorTrigger --template "Durable Functions orchestrator trigger"
$ func new --name DurableActivityTrigger --template "Durable Functions activity trigger"
$ func new --name DurableHTTPStarter --template "Durable Functions HTTP starter"
$ func new --name RabbitMQTrigger --template "RabbitMQ trigger"
$ func new --name KafkaTrigger --template "Kafka trigger"

Opening VSCode

$ conda activate python_demo-3.9
$ code .

After opening VSCode, terminal will activate virtual environment as below;

If it doesn't activate it, close vscode and reopen it.

Don't forget to activate virtual environment in terminal before opening vscode, otherwise vscode debugger try to use different python runtime, in this use case, even i create virtual environment for python 3.9 with conda, vscode debugger try to use python 3.10 but Azure Function can run with python 3.9.

Vscode config files

We need to update two vscode config files to debug tasks.json, settings.json files.

{
    "version": "0.2.0",
    "configurations": [
        {
            "name": "Attach to Python Functions",
            "type": "python",
            "request": "attach",
            "port": 9091,
            "preLaunchTask": "func: host start"
        }
    ]
}
{
  "version": "2.0.0",
  "tasks": [
    {
      "label": "func: host start",
      "type": "shell",
      "command": "func host start --language-worker -- '-m ptvsd --host 127.0.0.1 --port 9091'",
      "problemMatcher": "$func-python-watch",
      "isBackground": true,
      "dependsOn": "pip install (functions)"
    },
    {
      "label": "pip install (functions)",
      "type": "shell",
      "osx": {
        "command": "${config:azureFunctions.pythonVenv}/bin/python -m pip install -r requirements.txt"
      },
      "windows": {
        "command": "${config:azureFunctions.pythonVenv}\\Scripts\\python -m pip install -r requirements.txt"
      },
      "linux": {
        "command": "${config:azureFunctions.pythonVenv}/bin/python -m pip install -r requirements.txt"
      },
      "problemMatcher": []
    },
    {
      "label": "Run Azurite",
      "type": "shell",
      "command": "azurite",
      "isBackground": true,
      "problemMatcher": "$tsc"
    }
  ]
}

update value of azureFunctions.pythonVenv to your environment name. Just update python_demo-3.9 to your virtual environment name.

{
    "azureFunctions.deploySubpath": ".",
    "azureFunctions.scmDoBuildDuringDeployment": true,
    "azureFunctions.pythonVenv": "/opt/homebrew/Caskroom/miniconda/base/envs/python_demo-3.9",
    "azureFunctions.projectLanguage": "Python",
    "azureFunctions.projectRuntime": "~4",
    "debug.internalConsoleOptions": "neverOpen",
    "azureFunctions.projectLanguageModel": 2
}
{
  "recommendations": [
    "ms-azuretools.vscode-azurefunctions",
    "ms-python.python"
  ]
}

Vscode start azurite to start Azure Function

Azurite is local emulator of Azure. Azure function needs azurite so that when we run func host start , azure function app will connect to Azurite storage acount, so click CMD+SHIFT+P in VSCode and type azurite: start and enter.

Debugging Azure Function locally

Click CMD+SHIFT+P and type Debug: Start Debugging as shown in below screenshot or just click F5 shortcut.

VSCode will ask for storage account, so select Use Local Emulator for local development.

This will update line 6 in local.settings.json

{
  "IsEncrypted": false,
  "Values": {
    "FUNCTIONS_WORKER_RUNTIME": "python",
    "AzureWebJobsFeatureFlags": "EnableWorkerIndexing",
    "AzureWebJobsStorage": "UseDevelopmentStorage=true"
  }
}

Testing Azure HTTP and Webhook Triggered Functions Locally

$ curl http://localhost:7071/api/HttpExample\?name=Azure
$ curl -X POST -H "Content-Type: application/json" -d '{ "name": "Azure" }' http://localhost:7071/api/HttpExample

Testing Azure Non-HTTP Triggered Functions Locally

$ curl --request POST -H "Content-Type: application/json" --data '{"input":"sample queue data"}' http://localhost:7071/admin/functions/QueueFunc

Deactivating virtual environment

$ conda deactivate