Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
68 changes: 68 additions & 0 deletions workers/proxy_worker/dispatcher.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
# Licensed under the MIT License.

import asyncio
import importlib
import logging
import os
import queue
Expand All @@ -11,6 +12,7 @@
import typing
from asyncio import AbstractEventLoop
from dataclasses import dataclass
from importlib.metadata import entry_points
from typing import Any, Optional

import grpc
Expand All @@ -30,6 +32,7 @@
check_python_eol
)
from proxy_worker.utils.constants import (
PYTHON_ENABLE_AGENT_RUNTIME,
PYTHON_ENABLE_DEBUG_LOGGING,
)
from proxy_worker.version import VERSION
Expand Down Expand Up @@ -417,7 +420,72 @@ def stop(self) -> None:

@staticmethod
def reload_library_worker(directory: str):
"""
Load the appropriate runtime using the base package pattern.

This uses the runtime base package to automatically discover which
runtime is loaded.

If no runtime is registered via the base package, it falls back to
the traditional detection method.
"""
global _library_worker, _library_worker_has_cv

if is_envvar_true(PYTHON_ENABLE_AGENT_RUNTIME):
# Import base package
import azurefunctions.extensions.base as runtime_base

# Discover all installed runtime packages via entry points
available_runtimes = list(entry_points(group='azurefunctions.runtimes'))

# Only one runtime should be defined
if len(available_runtimes) > 1:
runtime_names = [ep.name for ep in available_runtimes]
raise RuntimeError(
f"Multiple runtimes detected: {runtime_names}. "
f"Only one runtime should be defined."
)

# Load the single runtime entry point if available
if available_runtimes:
ep = available_runtimes[0]
try:
# Load the entry point (triggers import and
# metaclass registration)
ep.load()
logger.debug(f"Loaded runtime entry point: {ep.name}")
except Exception as e:
logger.debug(f"Could not load runtime {ep.name}: {e}")

# Check if a runtime was registered
# Check if the runtime base package has the RuntimeFeatureChecker
# Check if the runtime is loaded
if hasattr(runtime_base, 'RuntimeFeatureChecker') \
and runtime_base.RuntimeFeatureChecker.runtime_loaded():
# Get the registered runtime module
# (e.g., "azure_functions_fastapi.runtime")
runtime_module_name = (
runtime_base.RuntimeTrackerMeta.get_module())
runtime_name = (
runtime_base.RuntimeTrackerMeta.get_runtime_name())
package_name = runtime_base.RuntimeTrackerMeta.get_package_name()

logger.debug("Runtime registered: %s (module: %s). "
"Importing runtime package: %s",
runtime_name, runtime_module_name, package_name)

# Import the top-level runtime package (which exports
# the public API)
runtime_module = importlib.import_module(package_name)
_library_worker = runtime_module
_library_worker_has_cv = _library_worker.invocation_id_cv
# Module has been imported, end check
return

# No runtime registered via base package
# Use traditional detection
logger.debug(
"No runtime registered via base package, using fallback")
v2_scriptfile = os.path.join(directory, get_script_file_name())
if os.path.exists(v2_scriptfile):
try:
Expand Down
2 changes: 2 additions & 0 deletions workers/proxy_worker/utils/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@
PYTHON_SCRIPT_FILE_NAME = "PYTHON_SCRIPT_FILE_NAME"
PYTHON_SCRIPT_FILE_NAME_DEFAULT = "function_app.py"

PYTHON_ENABLE_AGENT_RUNTIME = "PYTHON_ENABLE_AGENT_RUNTIME"

# EOL Dates
PYTHON_EOL_DATES = {
'3.13': '2029-10',
Expand Down
Loading
Loading