diff --git a/azure_functions_worker/bindings/shared_memory_data_transfer/file_accessor_factory.py b/azure_functions_worker/bindings/shared_memory_data_transfer/file_accessor_factory.py index eb97f0f54..25c24916c 100644 --- a/azure_functions_worker/bindings/shared_memory_data_transfer/file_accessor_factory.py +++ b/azure_functions_worker/bindings/shared_memory_data_transfer/file_accessor_factory.py @@ -5,7 +5,7 @@ import sys from ...constants import FUNCTIONS_WORKER_SHARED_MEMORY_DATA_TRANSFER_ENABLED -from ...utils.common import is_envvar_true +from ...utils.config_manager import config_manager from .file_accessor import DummyFileAccessor from .file_accessor_unix import FileAccessorUnix from .file_accessor_windows import FileAccessorWindows @@ -18,7 +18,7 @@ class FileAccessorFactory: """ @staticmethod def create_file_accessor(): - if sys.platform == "darwin" and not is_envvar_true( + if sys.platform == "darwin" and not config_manager.is_envvar_true( FUNCTIONS_WORKER_SHARED_MEMORY_DATA_TRANSFER_ENABLED): return DummyFileAccessor() elif os.name == 'nt': diff --git a/azure_functions_worker/bindings/shared_memory_data_transfer/file_accessor_unix.py b/azure_functions_worker/bindings/shared_memory_data_transfer/file_accessor_unix.py index ae4f6206c..95d4919d4 100644 --- a/azure_functions_worker/bindings/shared_memory_data_transfer/file_accessor_unix.py +++ b/azure_functions_worker/bindings/shared_memory_data_transfer/file_accessor_unix.py @@ -9,7 +9,7 @@ from azure_functions_worker import constants from ...logging import logger -from ...utils.common import get_app_setting +from ...utils.config_manager import config_manager from .file_accessor import FileAccessor from .shared_memory_constants import SharedMemoryConstants as consts from .shared_memory_exception import SharedMemoryException @@ -95,7 +95,7 @@ def _get_allowed_mem_map_dirs(self) -> List[str]: Otherwise, the default value will be used. """ setting = constants.UNIX_SHARED_MEMORY_DIRECTORIES - allowed_mem_map_dirs_str = get_app_setting(setting) + allowed_mem_map_dirs_str = config_manager.get_app_setting(setting) if allowed_mem_map_dirs_str is None: allowed_mem_map_dirs = consts.UNIX_TEMP_DIRS logger.info( diff --git a/azure_functions_worker/bindings/shared_memory_data_transfer/shared_memory_manager.py b/azure_functions_worker/bindings/shared_memory_data_transfer/shared_memory_manager.py index ec1a1a7cb..25cee6218 100644 --- a/azure_functions_worker/bindings/shared_memory_data_transfer/shared_memory_manager.py +++ b/azure_functions_worker/bindings/shared_memory_data_transfer/shared_memory_manager.py @@ -6,7 +6,7 @@ from ...constants import FUNCTIONS_WORKER_SHARED_MEMORY_DATA_TRANSFER_ENABLED from ...logging import logger -from ...utils.common import is_envvar_true +from ...utils.config_manager import config_manager from ..datumdef import Datum from .file_accessor_factory import FileAccessorFactory from .shared_memory_constants import SharedMemoryConstants as consts @@ -55,7 +55,7 @@ def is_enabled(self) -> bool: Whether supported types should be transferred between functions host and the worker using shared memory. """ - return is_envvar_true( + return config_manager.is_envvar_true( FUNCTIONS_WORKER_SHARED_MEMORY_DATA_TRANSFER_ENABLED) def is_supported(self, datum: Datum) -> bool: diff --git a/azure_functions_worker/dispatcher.py b/azure_functions_worker/dispatcher.py index 897a3499a..07989a706 100644 --- a/azure_functions_worker/dispatcher.py +++ b/azure_functions_worker/dispatcher.py @@ -31,7 +31,6 @@ PYTHON_ENABLE_DEBUG_LOGGING, PYTHON_ENABLE_INIT_INDEXING, PYTHON_ENABLE_OPENTELEMETRY, - PYTHON_ENABLE_OPENTELEMETRY_DEFAULT, PYTHON_LANGUAGE_RUNTIME, PYTHON_ROLLBACK_CWD_PATH, PYTHON_SCRIPT_FILE_NAME, @@ -60,7 +59,8 @@ logger, ) from .utils.app_setting_manager import get_python_appsetting_state -from .utils.common import get_app_setting, is_envvar_true, validate_script_file_name +from .utils.common import validate_script_file_name +from .utils.config_manager import config_manager from .utils.dependency import DependencyManager from .utils.tracing import marshall_exception_trace from .utils.wrappers import disable_feature_by @@ -113,6 +113,7 @@ def __init__(self, loop: BaseEventLoop, host: str, port: int, # For 3.[6|7|8] The default value is 1. # For 3.9, we don't set this value by default but we honor incoming # the app setting. + config_manager.read_environment_variables() self._sync_call_tp: concurrent.futures.Executor = ( self._create_sync_call_tp(self._get_sync_tp_max_workers()) ) @@ -188,7 +189,7 @@ async def dispatch_forever(self): # sourcery skip: swap-if-expression logging_handler = AsyncLoggingHandler() root_logger = logging.getLogger() - log_level = logging.INFO if not is_envvar_true( + log_level = logging.INFO if not config_manager.is_envvar_true( PYTHON_ENABLE_DEBUG_LOGGING) else logging.DEBUG root_logger.setLevel(log_level) @@ -314,10 +315,10 @@ def initialize_azure_monitor(self): # Connection string can be explicitly specified in Appsetting # If not set, defaults to env var # APPLICATIONINSIGHTS_CONNECTION_STRING - connection_string=get_app_setting( + connection_string=config_manager.get_app_setting( setting=APPLICATIONINSIGHTS_CONNECTION_STRING ), - logger_name=get_app_setting( + logger_name=config_manager.get_app_setting( setting=PYTHON_AZURE_MONITOR_LOGGER_NAME, default_value=PYTHON_AZURE_MONITOR_LOGGER_NAME_DEFAULT ), @@ -354,20 +355,24 @@ def update_opentelemetry_status(self): ) async def _handle__worker_init_request(self, request): - logger.info('Received WorkerInitRequest, ' - 'python version %s, ' - 'worker version %s, ' - 'request ID %s. ' - 'App Settings state: %s. ' - 'To enable debug level logging, please refer to ' - 'https://aka.ms/python-enable-debug-logging', - sys.version, - VERSION, - self.request_id, - get_python_appsetting_state() - ) - worker_init_request = request.worker_init_request + config_manager.set_config( + os.path.join(worker_init_request.function_app_directory, 'az-config.json') + ) + logger.info( + 'Received WorkerInitRequest, ' + 'python version %s, ' + 'worker version %s, ' + 'request ID %s. ' + 'App Settings state: %s. ' + 'To enable debug level logging, please refer to ' + 'https://aka.ms/python-enable-debug-logging', + sys.version, + VERSION, + self.request_id, + get_python_appsetting_state(), + ) + host_capabilities = worker_init_request.capabilities if constants.FUNCTION_DATA_CACHE in host_capabilities: val = host_capabilities[constants.FUNCTION_DATA_CACHE] @@ -381,8 +386,7 @@ async def _handle__worker_init_request(self, request): constants.RPC_HTTP_TRIGGER_METADATA_REMOVED: _TRUE, constants.SHARED_MEMORY_DATA_TRANSFER: _TRUE, } - if get_app_setting(setting=PYTHON_ENABLE_OPENTELEMETRY, - default_value=PYTHON_ENABLE_OPENTELEMETRY_DEFAULT): + if config_manager.is_envvar_true(PYTHON_ENABLE_OPENTELEMETRY): self.initialize_azure_monitor() if self._azure_monitor_available: @@ -398,7 +402,7 @@ async def _handle__worker_init_request(self, request): # dictionary which will be later used in the invocation request bindings.load_binding_registry() - if is_envvar_true(PYTHON_ENABLE_INIT_INDEXING): + if config_manager.is_envvar_true(PYTHON_ENABLE_INIT_INDEXING): try: self.load_function_metadata( worker_init_request.function_app_directory, @@ -436,7 +440,7 @@ def load_function_metadata(self, function_app_directory, caller_info): directory and save the results in function_metadata_result or function_metadata_exception in case of an exception. """ - script_file_name = get_app_setting( + script_file_name = config_manager.get_app_setting( setting=PYTHON_SCRIPT_FILE_NAME, default_value=f'{PYTHON_SCRIPT_FILE_NAME_DEFAULT}') @@ -459,7 +463,7 @@ async def _handle__functions_metadata_request(self, request): metadata_request = request.functions_metadata_request function_app_directory = metadata_request.function_app_directory - script_file_name = get_app_setting( + script_file_name = config_manager.get_app_setting( setting=PYTHON_SCRIPT_FILE_NAME, default_value=f'{PYTHON_SCRIPT_FILE_NAME_DEFAULT}') function_path = os.path.join(function_app_directory, @@ -468,9 +472,11 @@ async def _handle__functions_metadata_request(self, request): logger.info( 'Received WorkerMetadataRequest, request ID %s, ' 'function_path: %s', - self.request_id, function_path) + self.request_id, + function_path, + ) - if not is_envvar_true(PYTHON_ENABLE_INIT_INDEXING): + if not config_manager.is_envvar_true(PYTHON_ENABLE_INIT_INDEXING): try: self.load_function_metadata( function_app_directory, @@ -757,9 +763,15 @@ async def _handle__function_environment_reload_request(self, request): # Reload environment variables os.environ.clear() + config_manager.clear_config() env_vars = func_env_reload_request.environment_variables for var in env_vars: os.environ[var] = env_vars[var] + config_manager.set_config( + os.path.join( + func_env_reload_request.function_app_directory, 'az-config.json' + ) + ) # Apply PYTHON_THREADPOOL_THREAD_COUNT self._stop_sync_call_tp() @@ -767,7 +779,7 @@ async def _handle__function_environment_reload_request(self, request): self._create_sync_call_tp(self._get_sync_tp_max_workers()) ) - if is_envvar_true(PYTHON_ENABLE_DEBUG_LOGGING): + if config_manager.is_envvar_true(PYTHON_ENABLE_DEBUG_LOGGING): root_logger = logging.getLogger() root_logger.setLevel(logging.DEBUG) @@ -779,16 +791,14 @@ async def _handle__function_environment_reload_request(self, request): bindings.load_binding_registry() capabilities = {} - if get_app_setting( - setting=PYTHON_ENABLE_OPENTELEMETRY, - default_value=PYTHON_ENABLE_OPENTELEMETRY_DEFAULT): + if config_manager.is_envvar_true(PYTHON_ENABLE_OPENTELEMETRY): self.initialize_azure_monitor() if self._azure_monitor_available: capabilities[constants.WORKER_OPEN_TELEMETRY_ENABLED] = ( _TRUE) - if is_envvar_true(PYTHON_ENABLE_INIT_INDEXING): + if config_manager.is_envvar_true(PYTHON_ENABLE_INIT_INDEXING): try: self.load_function_metadata( directory, @@ -969,9 +979,10 @@ def tp_max_workers_validator(value: str) -> bool: default_value = None if sys.version_info.minor >= 9 \ else f'{PYTHON_THREADPOOL_THREAD_COUNT_DEFAULT}' - max_workers = get_app_setting(setting=PYTHON_THREADPOOL_THREAD_COUNT, - default_value=default_value, - validator=tp_max_workers_validator) + max_workers = config_manager.get_app_setting( + setting=PYTHON_THREADPOOL_THREAD_COUNT, + default_value=default_value, + validator=tp_max_workers_validator) if sys.version_info.minor <= 7: max_workers = min(int(max_workers), diff --git a/azure_functions_worker/http_v2.py b/azure_functions_worker/http_v2.py index 4eeeea9d9..dddfd4e5c 100644 --- a/azure_functions_worker/http_v2.py +++ b/azure_functions_worker/http_v2.py @@ -14,7 +14,7 @@ X_MS_INVOCATION_ID, ) from azure_functions_worker.logging import logger -from azure_functions_worker.utils.common import is_envvar_false +from azure_functions_worker.utils.config_manager import config_manager # Http V2 Exceptions @@ -280,7 +280,7 @@ def ext_base(cls): @classmethod def _check_http_v2_enabled(cls): if sys.version_info.minor < BASE_EXT_SUPPORTED_PY_MINOR_VERSION or \ - is_envvar_false(PYTHON_ENABLE_INIT_INDEXING): + config_manager.is_envvar_false(PYTHON_ENABLE_INIT_INDEXING): return False import azurefunctions.extensions.base as ext_base diff --git a/azure_functions_worker/loader.py b/azure_functions_worker/loader.py index ce96c1406..4d013a3bf 100644 --- a/azure_functions_worker/loader.py +++ b/azure_functions_worker/loader.py @@ -26,7 +26,7 @@ RETRY_POLICY, ) from .logging import logger -from .utils.common import get_app_setting +from .utils.config_manager import config_manager from .utils.wrappers import attach_message_to_exception _AZURE_NAMESPACE = '__app__' @@ -255,7 +255,7 @@ def index_function_app(function_path: str): f"level function app instances are defined.") if not app: - script_file_name = get_app_setting( + script_file_name = config_manager.get_app_setting( setting=PYTHON_SCRIPT_FILE_NAME, default_value=f'{PYTHON_SCRIPT_FILE_NAME_DEFAULT}') raise ValueError("Could not find top level function app instances in " diff --git a/azure_functions_worker/utils/app_setting_manager.py b/azure_functions_worker/utils/app_setting_manager.py index 3d8ccbb45..c148357d9 100644 --- a/azure_functions_worker/utils/app_setting_manager.py +++ b/azure_functions_worker/utils/app_setting_manager.py @@ -1,8 +1,8 @@ # Copyright (c) Microsoft Corporation. All rights reserved. # Licensed under the MIT License. -import os import sys +from .config_manager import config_manager from ..constants import ( FUNCTIONS_WORKER_SHARED_MEMORY_DATA_TRANSFER_ENABLED, PYTHON_ENABLE_DEBUG_LOGGING, @@ -19,7 +19,7 @@ def get_python_appsetting_state(): - current_vars = os.environ.copy() + current_vars = config_manager.get_config() python_specific_settings = \ [PYTHON_ROLLBACK_CWD_PATH, PYTHON_THREADPOOL_THREAD_COUNT, diff --git a/azure_functions_worker/utils/common.py b/azure_functions_worker/utils/common.py index 963cd3c1c..e0410b62b 100644 --- a/azure_functions_worker/utils/common.py +++ b/azure_functions_worker/utils/common.py @@ -1,11 +1,10 @@ # Copyright (c) Microsoft Corporation. All rights reserved. # Licensed under the MIT License. import importlib -import os import re import sys from types import ModuleType -from typing import Callable, Optional +from azure_functions_worker.utils.config_manager import config_manager from azure_functions_worker.constants import ( CUSTOMER_PACKAGES_PATH, @@ -13,82 +12,11 @@ ) -def is_true_like(setting: str) -> bool: - if setting is None: - return False - - return setting.lower().strip() in {'1', 'true', 't', 'yes', 'y'} - - -def is_false_like(setting: str) -> bool: - if setting is None: - return False - - return setting.lower().strip() in {'0', 'false', 'f', 'no', 'n'} - - -def is_envvar_true(env_key: str) -> bool: - if os.getenv(env_key) is None: - return False - - return is_true_like(os.environ[env_key]) - - -def is_envvar_false(env_key: str) -> bool: - if os.getenv(env_key) is None: - return False - - return is_false_like(os.environ[env_key]) - - def is_python_version(version: str) -> bool: current_version = f'{sys.version_info.major}.{sys.version_info.minor}' return current_version == version -def get_app_setting( - setting: str, - default_value: Optional[str] = None, - validator: Optional[Callable[[str], bool]] = None -) -> Optional[str]: - """Returns the application setting from environment variable. - - Parameters - ---------- - setting: str - The name of the application setting (e.g. FUNCTIONS_RUNTIME_VERSION) - - default_value: Optional[str] - The expected return value when the application setting is not found, - or the app setting does not pass the validator. - - validator: Optional[Callable[[str], bool]] - A function accepts the app setting value and should return True when - the app setting value is acceptable. - - Returns - ------- - Optional[str] - A string value that is set in the application setting - """ - app_setting_value = os.getenv(setting) - - # If an app setting is not configured, we return the default value - if app_setting_value is None: - return default_value - - # If there's no validator, we should return the app setting value directly - if validator is None: - return app_setting_value - - # If the app setting is set with a validator, - # On True, should return the app setting value - # On False, should return the default value - if validator(app_setting_value): - return app_setting_value - return default_value - - def get_sdk_version(module: ModuleType) -> str: """Check the version of azure.functions sdk. @@ -117,7 +45,7 @@ def get_sdk_from_sys_path() -> ModuleType: The azure.functions that is loaded from the first sys.path entry """ - if is_envvar_true(PYTHON_EXTENSIONS_RELOAD_FUNCTIONS): + if config_manager.is_envvar_true(PYTHON_EXTENSIONS_RELOAD_FUNCTIONS): backup_azure_functions = None backup_azure = None diff --git a/azure_functions_worker/utils/config_manager.py b/azure_functions_worker/utils/config_manager.py new file mode 100644 index 000000000..3588f4de6 --- /dev/null +++ b/azure_functions_worker/utils/config_manager.py @@ -0,0 +1,130 @@ +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. +import os +import json +from typing import Optional, Callable, Dict + + +class ConfigManager(object): + """ + // TODO: docs here + """ + def __init__(self): + """ + // TODO: docs here + """ + self.config_data: Optional[Dict[str, str]] = None + + def read_environment_variables(self): + if self.config_data is None: + self.config_data = {} + env_copy = os.environ + for k, v in env_copy.items(): + self.config_data.update({k.upper(): v}) + + def read_config(self, function_path: str): + if self.config_data is None: + self.config_data = {} + try: + with open(function_path, "r") as stream: + # loads the entire json file + full_config_data = json.load(stream) + # gets the python section of the json file + self.config_data = full_config_data.get("PYTHON") + except FileNotFoundError: + pass + + def set_config(self, function_path: str): + self.read_config(function_path) + self.read_environment_variables() + + def is_true_like(self, setting: str) -> bool: + if setting is None: + return False + + return setting.lower().strip() in {"1", "true", "t", "yes", "y"} + + def is_false_like(self, setting: str) -> bool: + if setting is None: + return False + + return setting.lower().strip() in {"0", "false", "f", "no", "n"} + + def is_envvar_true(self, key: str) -> bool: + key_upper = key.upper() + if self.config_exists() and not self.config_data.get(key_upper): + return False + return self.is_true_like(self.config_data.get(key_upper)) + + def is_envvar_false(self, key: str) -> bool: + key_upper = key.upper() + if self.config_exists() and not self.config_data.get(key_upper): + return False + return self.is_false_like(self.config_data.get(key_upper)) + + def get_app_setting( + self, + setting: str, + default_value: Optional[str] = None, + validator: Optional[Callable[[str], bool]] = None, + ) -> Optional[str]: + """Returns the application setting from environment variable. + + Parameters + ---------- + setting: str + The name of the application setting (e.g. FUNCTIONS_RUNTIME_VERSION) + + default_value: Optional[str] + The expected return value when the application setting is not found, + or the app setting does not pass the validator. + + validator: Optional[Callable[[str], bool]] + A function accepts the app setting value and should return True when + the app setting value is acceptable. + + Returns + ------- + Optional[str] + A string value that is set in the application setting + """ + setting_upper = setting.upper() + if self.config_exists() and self.config_data.get(setting_upper) is not None: + # Setting exists, check with validator + app_setting_value = self.config_data.get(setting_upper) + + # If there's no validator, return the app setting value directly + if validator is None: + return app_setting_value + + # If the app setting is set with a validator, + # On True, should return the app setting value + # On False, should return the default value + if validator(app_setting_value): + return app_setting_value + + # Setting is not configured or validator is false + # Return default value + return default_value + + def set_env_var(self, setting: str, value: str): + self.config_data[setting] = value + + def del_env_var(self, setting: str): + self.config_data.pop(setting, None) + + def clear_config(self): + if self.config_data is not None: + self.config_data.clear() + self.config_data = None + + def config_exists(self) -> bool: + if self.config_data is None: + self.set_config("") + return self.config_data is not None + + def get_config(self) -> dict: + return self.config_data + + +config_manager = ConfigManager() diff --git a/azure_functions_worker/utils/dependency.py b/azure_functions_worker/utils/dependency.py index 76d4259be..5e36e858a 100644 --- a/azure_functions_worker/utils/dependency.py +++ b/azure_functions_worker/utils/dependency.py @@ -8,8 +8,7 @@ from types import ModuleType from typing import List, Optional -from azure_functions_worker.utils.common import is_envvar_true, is_true_like - +from azure_functions_worker.utils.config_manager import config_manager from ..constants import ( AZURE_WEBJOBS_SCRIPT_ROOT, CONTAINER_NAME, @@ -73,7 +72,7 @@ def initialize(cls): @classmethod def is_in_linux_consumption(cls): - return CONTAINER_NAME in os.environ + return config_manager.get_app_setting(CONTAINER_NAME) is not None @classmethod def should_load_cx_dependencies(cls): @@ -86,7 +85,7 @@ def should_load_cx_dependencies(cls): (OOM, timeouts etc) and env reload request is not called. """ return not (DependencyManager.is_in_linux_consumption() - and is_envvar_true("WEBSITE_PLACEHOLDER_MODE")) + and config_manager.is_envvar_true("WEBSITE_PLACEHOLDER_MODE")) @classmethod @enable_feature_by( @@ -145,7 +144,8 @@ def prioritize_customer_dependencies(cls, cx_working_dir=None): if not working_directory: working_directory = cls.cx_working_dir if not working_directory: - working_directory = os.getenv(AZURE_WEBJOBS_SCRIPT_ROOT, '') + working_directory = config_manager.get_app_setting( + AZURE_WEBJOBS_SCRIPT_ROOT, '') # Try to get the latest customer's dependency path cx_deps_path: str = cls._get_cx_deps_path() @@ -158,7 +158,7 @@ def prioritize_customer_dependencies(cls, cx_working_dir=None): 'working_directory: %s, Linux Consumption: %s, Placeholder: %s', cls.worker_deps_path, cx_deps_path, working_directory, DependencyManager.is_in_linux_consumption(), - is_envvar_true("WEBSITE_PLACEHOLDER_MODE")) + config_manager.is_envvar_true("WEBSITE_PLACEHOLDER_MODE")) cls._remove_from_sys_path(cls.worker_deps_path) cls._add_to_sys_path(cls.cx_deps_path, True) @@ -193,7 +193,7 @@ def reload_customer_libraries(cls, cx_working_dir: str): cx_working_dir: str The path which contains customer's project file (e.g. wwwroot). """ - use_new_env = os.getenv(PYTHON_ISOLATE_WORKER_DEPENDENCIES) + use_new_env = config_manager.get_app_setting(PYTHON_ISOLATE_WORKER_DEPENDENCIES) if use_new_env is None: use_new = ( PYTHON_ISOLATE_WORKER_DEPENDENCIES_DEFAULT_310 if @@ -201,7 +201,7 @@ def reload_customer_libraries(cls, cx_working_dir: str): PYTHON_ISOLATE_WORKER_DEPENDENCIES_DEFAULT ) else: - use_new = is_true_like(use_new_env) + use_new = config_manager.is_true_like(use_new_env) if use_new: cls.prioritize_customer_dependencies(cx_working_dir) @@ -314,7 +314,8 @@ def _get_cx_deps_path() -> str: Linux Dedicated/Premium: path to customer's site pacakges Linux Consumption: empty string """ - prefix: Optional[str] = os.getenv(AZURE_WEBJOBS_SCRIPT_ROOT) + prefix: Optional[str] = config_manager.get_app_setting( + AZURE_WEBJOBS_SCRIPT_ROOT) cx_paths: List[str] = [ p for p in sys.path if prefix and p.startswith(prefix) and ('site-packages' in p) @@ -333,7 +334,7 @@ def _get_cx_working_dir() -> str: Linux Dedicated/Premium: AzureWebJobsScriptRoot env variable Linux Consumption: empty string """ - return os.getenv(AZURE_WEBJOBS_SCRIPT_ROOT, '') + return config_manager.get_app_setting(AZURE_WEBJOBS_SCRIPT_ROOT, '') @staticmethod def _get_worker_deps_path() -> str: diff --git a/azure_functions_worker/utils/wrappers.py b/azure_functions_worker/utils/wrappers.py index 29f379da3..979bb7c29 100644 --- a/azure_functions_worker/utils/wrappers.py +++ b/azure_functions_worker/utils/wrappers.py @@ -4,7 +4,7 @@ from typing import Any, Callable from ..logging import error_logger, logger -from .common import is_envvar_false, is_envvar_true +from .config_manager import config_manager from .tracing import extend_exception_message @@ -13,9 +13,9 @@ def enable_feature_by(flag: str, flag_default: bool = False) -> Callable: def decorate(func): def call(*args, **kwargs): - if is_envvar_true(flag): + if config_manager.is_envvar_true(flag): return func(*args, **kwargs) - if flag_default and not is_envvar_false(flag): + if flag_default and not config_manager.is_envvar_false(flag): return func(*args, **kwargs) return default return call @@ -27,9 +27,9 @@ def disable_feature_by(flag: str, flag_default: bool = False) -> Callable: def decorate(func): def call(*args, **kwargs): - if is_envvar_true(flag): + if config_manager.is_envvar_true(flag): return default - if flag_default and not is_envvar_false(flag): + if flag_default and not config_manager.is_envvar_false(flag): return default return func(*args, **kwargs) return call diff --git a/tests/endtoend/test_dependency_isolation_functions.py b/tests/endtoend/test_dependency_isolation_functions.py index ec1cc251f..d94852a6d 100644 --- a/tests/endtoend/test_dependency_isolation_functions.py +++ b/tests/endtoend/test_dependency_isolation_functions.py @@ -14,13 +14,13 @@ PYAZURE_INTEGRATION_TEST, ) -from azure_functions_worker.utils.common import is_envvar_true +from azure_functions_worker.utils.config_manager import config_manager REQUEST_TIMEOUT_SEC = 5 -@skipIf(is_envvar_true(DEDICATED_DOCKER_TEST) - or is_envvar_true(CONSUMPTION_DOCKER_TEST), +@skipIf(config_manager.is_envvar_true(DEDICATED_DOCKER_TEST) + or config_manager.is_envvar_true(CONSUMPTION_DOCKER_TEST), 'Docker tests do not work with dependency isolation ') class TestGRPCandProtobufDependencyIsolationOnDedicated( testutils.WebHostTestCase): @@ -95,7 +95,7 @@ def test_working_directory_resolution(self): os.path.join(dir, 'dependency_isolation_functions').lower() ) - @skipIf(is_envvar_true(PYAZURE_INTEGRATION_TEST), + @skipIf(config_manager.is_envvar_true(PYAZURE_INTEGRATION_TEST), 'Integration test expects dependencies derived from core ' 'tools folder') def test_paths_resolution(self): @@ -121,7 +121,7 @@ def test_paths_resolution(self): ).lower() ) - @skipIf(is_envvar_true('USETESTPYTHONSDK'), + @skipIf(config_manager.is_envvar_true('USETESTPYTHONSDK'), 'Running tests using an editable azure-functions package.') def test_loading_libraries_from_customers_package(self): """Since the Python now loaded the customer's dependencies, the diff --git a/tests/endtoend/test_durable_functions.py b/tests/endtoend/test_durable_functions.py index 8cab19b6f..d792b04cb 100644 --- a/tests/endtoend/test_durable_functions.py +++ b/tests/endtoend/test_durable_functions.py @@ -9,11 +9,11 @@ from tests.utils import testutils from tests.utils.constants import CONSUMPTION_DOCKER_TEST, DEDICATED_DOCKER_TEST -from azure_functions_worker.utils.common import is_envvar_true +from azure_functions_worker.utils.config_manager import config_manager -@skipIf(is_envvar_true(DEDICATED_DOCKER_TEST) - or is_envvar_true(CONSUMPTION_DOCKER_TEST), +@skipIf(config_manager.is_envvar_true(DEDICATED_DOCKER_TEST) + or config_manager.is_envvar_true(CONSUMPTION_DOCKER_TEST), "Docker tests cannot retrieve port needed for a webhook") class TestDurableFunctions(testutils.WebHostTestCase): diff --git a/tests/endtoend/test_warmup_functions.py b/tests/endtoend/test_warmup_functions.py index b33eee26f..b1cbb2238 100644 --- a/tests/endtoend/test_warmup_functions.py +++ b/tests/endtoend/test_warmup_functions.py @@ -7,11 +7,11 @@ from tests.utils import testutils from tests.utils.constants import CONSUMPTION_DOCKER_TEST, DEDICATED_DOCKER_TEST -from azure_functions_worker.utils.common import is_envvar_true +from azure_functions_worker.utils.config_manager import config_manager -@skipIf(is_envvar_true(DEDICATED_DOCKER_TEST) - or is_envvar_true(CONSUMPTION_DOCKER_TEST), +@skipIf(config_manager.is_envvar_true(DEDICATED_DOCKER_TEST) + or config_manager.is_envvar_true(CONSUMPTION_DOCKER_TEST), "Docker tests cannot call admin functions") class TestWarmupFunctions(testutils.WebHostTestCase): """Test the Warmup Trigger in the local webhost. @@ -36,8 +36,8 @@ def check_log_warmup(self, host_out: typing.List[str]): self.assertEqual(host_out.count("Function App instance is warm"), 1) -@skipIf(is_envvar_true(DEDICATED_DOCKER_TEST) - or is_envvar_true(CONSUMPTION_DOCKER_TEST), +@skipIf(config_manager.is_envvar_true(DEDICATED_DOCKER_TEST) + or config_manager.is_envvar_true(CONSUMPTION_DOCKER_TEST), "Docker tests cannot call admin functions") class TestWarmupFunctionsStein(TestWarmupFunctions): diff --git a/tests/unittests/test_app_setting_manager.py b/tests/unittests/test_app_setting_manager.py index d203704f9..e4428d35a 100644 --- a/tests/unittests/test_app_setting_manager.py +++ b/tests/unittests/test_app_setting_manager.py @@ -12,6 +12,7 @@ PYTHON_THREADPOOL_THREAD_COUNT, ) from azure_functions_worker.utils.app_setting_manager import get_python_appsetting_state +from azure_functions_worker.utils.config_manager import config_manager SysVersionInfo = col.namedtuple("VersionInfo", ["major", "minor", "micro", "releaselevel", "serial"]) @@ -71,6 +72,7 @@ def setUpClass(cls): cls._patch_environ = patch.dict('os.environ', os_environ) cls._patch_environ.start() super().setUpClass() + config_manager.read_environment_variables() @classmethod def tearDownClass(cls): diff --git a/tests/unittests/test_dispatcher.py b/tests/unittests/test_dispatcher.py index ebaac2ced..f1b1e8722 100644 --- a/tests/unittests/test_dispatcher.py +++ b/tests/unittests/test_dispatcher.py @@ -25,6 +25,7 @@ REQUIRES_ROUTE_PARAMETERS ) from azure_functions_worker.dispatcher import Dispatcher, ContextEnabledTask +from azure_functions_worker.utils.config_manager import config_manager from azure_functions_worker.version import VERSION SysVersionInfo = col.namedtuple("VersionInfo", ["major", "minor", "micro", @@ -66,6 +67,7 @@ def tearDown(self): os.environ.clear() os.environ.update(self._pre_env) self.mock_version_info.stop() + config_manager.clear_config() async def test_dispatcher_initialize_worker(self): """Test if the dispatcher can be initialized worker successfully @@ -635,6 +637,7 @@ class TestDispatcherStein(testutils.AsyncTestCase): def setUp(self): self._ctrl = testutils.start_mockhost( script_root=DISPATCHER_STEIN_FUNCTIONS_DIR) + config_manager.clear_config() async def test_dispatcher_functions_metadata_request(self): """Test if the functions metadata response will be sent correctly @@ -701,6 +704,7 @@ def setUp(self): 'azure_functions_worker.dispatcher.sys.version_info', SysVersionInfo(3, 9, 0, 'final', 0)) self.mock_version_info.start() + config_manager.clear_config() def tearDown(self): os.environ.clear() diff --git a/tests/unittests/test_enable_debug_logging_functions.py b/tests/unittests/test_enable_debug_logging_functions.py index c39e7b60e..112c905a2 100644 --- a/tests/unittests/test_enable_debug_logging_functions.py +++ b/tests/unittests/test_enable_debug_logging_functions.py @@ -28,7 +28,7 @@ class TestDebugLoggingEnabledFunctions(testutils.WebHostTestCase): """ @classmethod def setUpClass(cls): - os.environ["PYTHON_ENABLE_DEBUG_LOGGING"] = "1" + os.environ[PYTHON_ENABLE_DEBUG_LOGGING] = "1" super().setUpClass() @classmethod @@ -62,7 +62,7 @@ class TestDebugLoggingDisabledFunctions(testutils.WebHostTestCase): """ @classmethod def setUpClass(cls): - os.environ["PYTHON_ENABLE_DEBUG_LOGGING"] = "0" + os.environ[PYTHON_ENABLE_DEBUG_LOGGING] = "0" super().setUpClass() @classmethod @@ -102,7 +102,7 @@ def setUpClass(cls): with open(host_json, 'w+') as f: f.write(HOST_JSON_TEMPLATE_WITH_LOGLEVEL_INFO) - os.environ["PYTHON_ENABLE_DEBUG_LOGGING"] = "1" + os.environ[PYTHON_ENABLE_DEBUG_LOGGING] = "1" super().setUpClass() @classmethod diff --git a/tests/unittests/test_extension.py b/tests/unittests/test_extension.py index 62569fefd..8bf016557 100644 --- a/tests/unittests/test_extension.py +++ b/tests/unittests/test_extension.py @@ -24,6 +24,7 @@ ExtensionManager, ) from azure_functions_worker.utils.common import get_sdk_from_sys_path +from azure_functions_worker.utils.config_manager import config_manager class MockContext: @@ -75,6 +76,7 @@ def setUp(self): # Set feature flag to on os.environ[PYTHON_ENABLE_WORKER_EXTENSIONS] = 'true' + config_manager.clear_config() def tearDown(self) -> None: os.environ.pop(PYTHON_ENABLE_WORKER_EXTENSIONS) diff --git a/tests/unittests/test_opentelemetry.py b/tests/unittests/test_opentelemetry.py index b26334bdf..c528b1777 100644 --- a/tests/unittests/test_opentelemetry.py +++ b/tests/unittests/test_opentelemetry.py @@ -7,6 +7,7 @@ from tests.utils import testutils from azure_functions_worker import protos +from azure_functions_worker.utils.config_manager import config_manager class TestOpenTelemetry(unittest.TestCase): @@ -15,6 +16,7 @@ def setUp(self): self.loop = asyncio.new_event_loop() asyncio.set_event_loop(self.loop) self.dispatcher = testutils.create_dummy_dispatcher() + config_manager.clear_config() def tearDown(self): self.loop.close() diff --git a/tests/unittests/test_script_file_name.py b/tests/unittests/test_script_file_name.py index 24327249a..0437cf429 100644 --- a/tests/unittests/test_script_file_name.py +++ b/tests/unittests/test_script_file_name.py @@ -29,13 +29,13 @@ class TestDefaultScriptFileName(testutils.WebHostTestCase): @classmethod def setUpClass(cls): - os.environ["PYTHON_SCRIPT_FILE_NAME"] = "function_app.py" + os.environ[PYTHON_SCRIPT_FILE_NAME] = "function_app.py" super().setUpClass() @classmethod def tearDownClass(cls): # Remove the PYTHON_SCRIPT_FILE_NAME environment variable - os.environ.pop('PYTHON_SCRIPT_FILE_NAME') + os.environ.pop(PYTHON_SCRIPT_FILE_NAME) super().tearDownClass() @classmethod @@ -58,13 +58,13 @@ class TestNewScriptFileName(testutils.WebHostTestCase): @classmethod def setUpClass(cls): - os.environ["PYTHON_SCRIPT_FILE_NAME"] = "test.py" + os.environ[PYTHON_SCRIPT_FILE_NAME] = "test.py" super().setUpClass() @classmethod def tearDownClass(cls): # Remove the PYTHON_SCRIPT_FILE_NAME environment variable - os.environ.pop('PYTHON_SCRIPT_FILE_NAME') + os.environ.pop(PYTHON_SCRIPT_FILE_NAME) super().tearDownClass() @classmethod @@ -87,13 +87,13 @@ class TestInvalidScriptFileName(testutils.WebHostTestCase): @classmethod def setUpClass(cls): - os.environ["PYTHON_SCRIPT_FILE_NAME"] = "main" + os.environ[PYTHON_SCRIPT_FILE_NAME] = "main" super().setUpClass() @classmethod def tearDownClass(cls): # Remove the PYTHON_SCRIPT_FILE_NAME environment variable - os.environ.pop('PYTHON_SCRIPT_FILE_NAME') + os.environ.pop(PYTHON_SCRIPT_FILE_NAME) super().tearDownClass() @classmethod diff --git a/tests/unittests/test_shared_memory_manager.py b/tests/unittests/test_shared_memory_manager.py index ca3cb6088..aaa366feb 100644 --- a/tests/unittests/test_shared_memory_manager.py +++ b/tests/unittests/test_shared_memory_manager.py @@ -20,7 +20,7 @@ from azure_functions_worker.constants import ( FUNCTIONS_WORKER_SHARED_MEMORY_DATA_TRANSFER_ENABLED, ) -from azure_functions_worker.utils.common import is_envvar_true +from azure_functions_worker.utils.config_manager import config_manager @skipIf(sys.platform == 'darwin', 'MacOS M1 machines do not correctly test the' @@ -39,6 +39,7 @@ def setUp(self): self.mock_environ.start() self.mock_sys_module.start() self.mock_sys_path.start() + config_manager.clear_config() def tearDown(self): self.mock_sys_path.stop() @@ -52,7 +53,7 @@ def test_is_enabled(self): """ # Make sure shared memory data transfer is enabled - was_shmem_env_true = is_envvar_true( + was_shmem_env_true = config_manager.is_envvar_true( FUNCTIONS_WORKER_SHARED_MEMORY_DATA_TRANSFER_ENABLED) os.environ.update( {FUNCTIONS_WORKER_SHARED_MEMORY_DATA_TRANSFER_ENABLED: '1'}) @@ -69,8 +70,9 @@ def test_is_disabled(self): disabled. """ # Make sure shared memory data transfer is disabled - was_shmem_env_true = is_envvar_true( + was_shmem_env_true = config_manager.is_envvar_true( FUNCTIONS_WORKER_SHARED_MEMORY_DATA_TRANSFER_ENABLED) + config_manager.clear_config() os.environ.update( {FUNCTIONS_WORKER_SHARED_MEMORY_DATA_TRANSFER_ENABLED: '0'}) manager = SharedMemoryManager() diff --git a/tests/unittests/test_utilities.py b/tests/unittests/test_utilities.py index 99b014e09..d2f5c3634 100644 --- a/tests/unittests/test_utilities.py +++ b/tests/unittests/test_utilities.py @@ -9,6 +9,7 @@ from azure_functions_worker.constants import PYTHON_EXTENSIONS_RELOAD_FUNCTIONS from azure_functions_worker.utils import common, wrappers +from azure_functions_worker.utils.config_manager import config_manager TEST_APP_SETTING_NAME = "TEST_APP_SETTING_NAME" TEST_FEATURE_FLAG = "APP_SETTING_FEATURE_FLAG" @@ -87,46 +88,47 @@ def tearDown(self): self.mock_sys_path.stop() self.mock_sys_module.stop() self.mock_environ.stop() + config_manager.clear_config() def test_is_true_like_accepted(self): - self.assertTrue(common.is_true_like('1')) - self.assertTrue(common.is_true_like('true')) - self.assertTrue(common.is_true_like('T')) - self.assertTrue(common.is_true_like('YES')) - self.assertTrue(common.is_true_like('y')) + self.assertTrue(config_manager.is_true_like('1')) + self.assertTrue(config_manager.is_true_like('true')) + self.assertTrue(config_manager.is_true_like('T')) + self.assertTrue(config_manager.is_true_like('YES')) + self.assertTrue(config_manager.is_true_like('y')) def test_is_true_like_rejected(self): - self.assertFalse(common.is_true_like(None)) - self.assertFalse(common.is_true_like('')) - self.assertFalse(common.is_true_like('secret')) + self.assertFalse(config_manager.is_true_like(None)) + self.assertFalse(config_manager.is_true_like('')) + self.assertFalse(config_manager.is_true_like('secret')) def test_is_false_like_accepted(self): - self.assertTrue(common.is_false_like('0')) - self.assertTrue(common.is_false_like('false')) - self.assertTrue(common.is_false_like('F')) - self.assertTrue(common.is_false_like('NO')) - self.assertTrue(common.is_false_like('n')) + self.assertTrue(config_manager.is_false_like('0')) + self.assertTrue(config_manager.is_false_like('false')) + self.assertTrue(config_manager.is_false_like('F')) + self.assertTrue(config_manager.is_false_like('NO')) + self.assertTrue(config_manager.is_false_like('n')) def test_is_false_like_rejected(self): - self.assertFalse(common.is_false_like(None)) - self.assertFalse(common.is_false_like('')) - self.assertFalse(common.is_false_like('secret')) + self.assertFalse(config_manager.is_false_like(None)) + self.assertFalse(config_manager.is_false_like('')) + self.assertFalse(config_manager.is_false_like('secret')) def test_is_envvar_true(self): os.environ[TEST_FEATURE_FLAG] = 'true' - self.assertTrue(common.is_envvar_true(TEST_FEATURE_FLAG)) + self.assertTrue(config_manager.is_envvar_true(TEST_FEATURE_FLAG)) def test_is_envvar_not_true_on_unset(self): self._unset_feature_flag() - self.assertFalse(common.is_envvar_true(TEST_FEATURE_FLAG)) + self.assertFalse(config_manager.is_envvar_true(TEST_FEATURE_FLAG)) def test_is_envvar_false(self): os.environ[TEST_FEATURE_FLAG] = 'false' - self.assertTrue(common.is_envvar_false(TEST_FEATURE_FLAG)) + self.assertTrue(config_manager.is_envvar_false(TEST_FEATURE_FLAG)) def test_is_envvar_not_false_on_unset(self): self._unset_feature_flag() - self.assertFalse(common.is_envvar_true(TEST_FEATURE_FLAG)) + self.assertFalse(config_manager.is_envvar_true(TEST_FEATURE_FLAG)) def test_disable_feature_with_no_feature_flag(self): mock_feature = MockFeature() @@ -244,7 +246,7 @@ def test_exception_message_should_not_be_extended_on_other_exception(self): self.assertEqual(type(e), ValueError) def test_app_settings_not_set_should_return_none(self): - app_setting = common.get_app_setting(TEST_APP_SETTING_NAME) + app_setting = config_manager.get_app_setting(TEST_APP_SETTING_NAME) self.assertIsNone(app_setting) def test_app_settings_should_return_value(self): @@ -252,11 +254,12 @@ def test_app_settings_should_return_value(self): os.environ.update({TEST_APP_SETTING_NAME: '42'}) # Try using utility to acquire application setting - app_setting = common.get_app_setting(TEST_APP_SETTING_NAME) + app_setting = config_manager.get_app_setting(TEST_APP_SETTING_NAME) self.assertEqual(app_setting, '42') def test_app_settings_not_set_should_return_default_value(self): - app_setting = common.get_app_setting(TEST_APP_SETTING_NAME, 'default') + app_setting = config_manager.get_app_setting(TEST_APP_SETTING_NAME, + 'default') self.assertEqual(app_setting, 'default') def test_app_settings_should_ignore_default_value(self): @@ -264,14 +267,16 @@ def test_app_settings_should_ignore_default_value(self): os.environ.update({TEST_APP_SETTING_NAME: '42'}) # Try using utility to acquire application setting - app_setting = common.get_app_setting(TEST_APP_SETTING_NAME, 'default') + app_setting = config_manager.get_app_setting(TEST_APP_SETTING_NAME, + 'default') self.assertEqual(app_setting, '42') def test_app_settings_should_not_trigger_validator_when_not_set(self): def raise_excpt(value: str): raise Exception('Should not raise on app setting not found') - common.get_app_setting(TEST_APP_SETTING_NAME, validator=raise_excpt) + config_manager.get_app_setting(TEST_APP_SETTING_NAME, + validator=raise_excpt) def test_app_settings_return_default_value_when_validation_fail(self): def parse_int_no_raise(value: str): @@ -284,7 +289,7 @@ def parse_int_no_raise(value: str): # Set application setting to an invalid value os.environ.update({TEST_APP_SETTING_NAME: 'invalid'}) - app_setting = common.get_app_setting( + app_setting = config_manager.get_app_setting( TEST_APP_SETTING_NAME, default_value='1', validator=parse_int_no_raise @@ -304,7 +309,7 @@ def parse_int_no_raise(value: str): # Set application setting to an invalid value os.environ.update({TEST_APP_SETTING_NAME: '42'}) - app_setting = common.get_app_setting( + app_setting = config_manager.get_app_setting( TEST_APP_SETTING_NAME, default_value='1', validator=parse_int_no_raise diff --git a/tests/unittests/test_utilities_dependency.py b/tests/unittests/test_utilities_dependency.py index 432aee750..8e9f3f576 100644 --- a/tests/unittests/test_utilities_dependency.py +++ b/tests/unittests/test_utilities_dependency.py @@ -9,6 +9,7 @@ from tests.utils import testutils from azure_functions_worker.utils.dependency import DependencyManager +from azure_functions_worker.utils.config_manager import config_manager class TestDependencyManager(unittest.TestCase): @@ -38,6 +39,7 @@ def setUp(self): self._patch_sys_path.start() self._patch_importer_cache.start() self._patch_modules.start() + config_manager.clear_config() def tearDown(self): self._patch_environ.stop() diff --git a/tests/utils/testutils.py b/tests/utils/testutils.py index aaeb40dec..d7773ca2d 100644 --- a/tests/utils/testutils.py +++ b/tests/utils/testutils.py @@ -61,7 +61,7 @@ FUNCTIONS_WORKER_SHARED_MEMORY_DATA_TRANSFER_ENABLED, UNIX_SHARED_MEMORY_DIRECTORIES, ) -from azure_functions_worker.utils.common import get_app_setting, is_envvar_true +from azure_functions_worker.utils.config_manager import config_manager TESTS_ROOT = PROJECT_ROOT / 'tests' E2E_TESTS_FOLDER = pathlib.Path('endtoend') @@ -141,8 +141,9 @@ class AsyncTestCase(unittest.TestCase, metaclass=AsyncTestCaseMeta): class WebHostTestCaseMeta(type(unittest.TestCase)): def __new__(mcls, name, bases, dct): - if is_envvar_true(DEDICATED_DOCKER_TEST) \ - or is_envvar_true(CONSUMPTION_DOCKER_TEST): + config_manager.read_environment_variables() + if config_manager.is_envvar_true(DEDICATED_DOCKER_TEST) \ + or config_manager.is_envvar_true(CONSUMPTION_DOCKER_TEST): return super().__new__(mcls, name, bases, dct) for attrname, attr in dct.items(): @@ -156,7 +157,8 @@ def wrapper(self, *args, __meth__=test_case, __check_log__=check_log_case, **kwargs): if (__check_log__ is not None and callable(__check_log__) - and not is_envvar_true(PYAZURE_WEBHOST_DEBUG)): + and not config_manager.is_envvar_true( + PYAZURE_WEBHOST_DEBUG)): # Check logging output for unit test scenarios result = self._run_test(__meth__, *args, **kwargs) @@ -216,9 +218,9 @@ def docker_tests_enabled(self) -> (bool, str): CONSUMPTION_DOCKER_TEST or DEDICATED_DOCKER_TEST is enabled else returns False """ - if is_envvar_true(CONSUMPTION_DOCKER_TEST): + if config_manager.is_envvar_true(CONSUMPTION_DOCKER_TEST): return True, CONSUMPTION_DOCKER_TEST - elif is_envvar_true(DEDICATED_DOCKER_TEST): + elif config_manager.is_envvar_true(DEDICATED_DOCKER_TEST): return True, DEDICATED_DOCKER_TEST else: return False, None @@ -230,7 +232,7 @@ def setUpClass(cls): docker_tests_enabled, sku = cls.docker_tests_enabled() - cls.host_stdout = None if is_envvar_true(PYAZURE_WEBHOST_DEBUG) \ + cls.host_stdout = None if config_manager.is_envvar_true(PYAZURE_WEBHOST_DEBUG) \ else tempfile.NamedTemporaryFile('w+t') try: @@ -271,7 +273,7 @@ def tearDownClass(cls): cls.webhost = None if cls.host_stdout is not None: - if is_envvar_true(ARCHIVE_WEBHOST_LOGS): + if config_manager.is_envvar_true(ARCHIVE_WEBHOST_LOGS): cls.host_stdout.seek(0) content = cls.host_stdout.read() if content is not None and len(content) > 0: @@ -326,7 +328,7 @@ class SharedMemoryTestCase(unittest.TestCase): """ def setUp(self): - self.was_shmem_env_true = is_envvar_true( + self.was_shmem_env_true = config_manager.is_envvar_true( FUNCTIONS_WORKER_SHARED_MEMORY_DATA_TRANSFER_ENABLED) os.environ.update( {FUNCTIONS_WORKER_SHARED_MEMORY_DATA_TRANSFER_ENABLED: '1'}) @@ -335,7 +337,7 @@ def setUp(self): if os_name == 'Darwin': # If an existing AppSetting is specified, save it so it can be # restored later - self.was_shmem_dirs = get_app_setting( + self.was_shmem_dirs = config_manager.get_app_setting( UNIX_SHARED_MEMORY_DIRECTORIES ) self._setUpDarwin() @@ -916,7 +918,7 @@ def popen_webhost(*, stdout, stderr, script_root=FUNCS_PATH, port=None): # In E2E Integration mode, we should use the core tools worker # from the latest artifact instead of the azure_functions_worker module - if is_envvar_true(PYAZURE_INTEGRATION_TEST): + if config_manager.is_envvar_true(PYAZURE_INTEGRATION_TEST): extra_env.pop('languageWorkers:python:workerDirectory') if testconfig and 'azure' in testconfig: @@ -966,7 +968,7 @@ def popen_webhost(*, stdout, stderr, script_root=FUNCS_PATH, port=None): def start_webhost(*, script_dir=None, stdout=None): script_root = TESTS_ROOT / script_dir if script_dir else FUNCS_PATH if stdout is None: - if is_envvar_true(PYAZURE_WEBHOST_DEBUG): + if config_manager.is_envvar_true(PYAZURE_WEBHOST_DEBUG): stdout = sys.stdout else: stdout = subprocess.DEVNULL