diff --git a/azure_functions_worker/functions.py b/azure_functions_worker/functions.py index 085fb5de6..04f7652c0 100644 --- a/azure_functions_worker/functions.py +++ b/azure_functions_worker/functions.py @@ -5,8 +5,6 @@ import pathlib import typing -from azure.functions import DataType, Function - from . import bindings as bindings_utils from . import protos from ._thirdparty import typing_inspect @@ -216,8 +214,7 @@ def validate_function_params(params: dict, bound_params: dict, param_bind_type, param_py_type) if not checks_out: - if binding.data_type is not DataType( - protos.BindingInfo.undefined): + if binding.data_type is not protos.BindingInfo.undefined: raise FunctionLoadError( func_name, f'{param.name!r} binding type "{binding.type}" ' @@ -357,7 +354,7 @@ def add_function(self, function_id: str, output_types, return_type) def add_indexed_function(self, function_id: str, - function: Function): + function): func = function.get_user_function() func_name = function.get_function_name() return_binding_name: typing.Optional[str] = None diff --git a/azure_functions_worker/loader.py b/azure_functions_worker/loader.py index e5120a2b9..36787e651 100644 --- a/azure_functions_worker/loader.py +++ b/azure_functions_worker/loader.py @@ -10,9 +10,7 @@ import sys import uuid from os import PathLike, fspath -from typing import List, Optional, Dict - -from azure.functions import Function, FunctionApp +from typing import Optional, Dict from . import protos, functions from .constants import MODULE_NOT_FOUND_TS_URL, SCRIPT_FILE_NAME, \ @@ -47,7 +45,7 @@ def uninstall() -> None: pass -def build_binding_protos(indexed_function: List[Function]) -> Dict: +def build_binding_protos(indexed_function) -> Dict: binding_protos = {} for binding in indexed_function.get_bindings(): binding_protos[binding.name] = protos.BindingInfo( @@ -59,7 +57,7 @@ def build_binding_protos(indexed_function: List[Function]) -> Dict: def process_indexed_function(functions_registry: functions.Registry, - indexed_functions: List[Function]): + indexed_functions): fx_metadata_results = [] for indexed_function in indexed_functions: function_id = str(uuid.uuid4()) @@ -141,10 +139,11 @@ def load_function(name: str, directory: str, script_file: str, expt_type=ImportError, message=f'Troubleshooting Guide: {MODULE_NOT_FOUND_TS_URL}' ) -def index_function_app(function_path: str) -> List[Function]: +def index_function_app(function_path: str): module_name = pathlib.Path(function_path).stem imported_module = importlib.import_module(module_name) + from azure.functions import FunctionApp app: Optional[FunctionApp] = None for i in imported_module.__dir__(): if isinstance(getattr(imported_module, i, None), FunctionApp): diff --git a/tests/unittests/broken_functions/invalid_datatype/function.json b/tests/unittests/broken_functions/invalid_datatype/function.json new file mode 100644 index 000000000..247beea27 --- /dev/null +++ b/tests/unittests/broken_functions/invalid_datatype/function.json @@ -0,0 +1,11 @@ +{ + "scriptFile": "main.py", + "bindings": [ + { + "type": "httpTrigger", + "direction": "in", + "dataType" : "string", + "name": "req" + } + ] +} diff --git a/tests/unittests/broken_functions/invalid_datatype/main.py b/tests/unittests/broken_functions/invalid_datatype/main.py new file mode 100644 index 000000000..0fbe6b520 --- /dev/null +++ b/tests/unittests/broken_functions/invalid_datatype/main.py @@ -0,0 +1,7 @@ +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. +import azure.functions as azf + + +def main(req: azf.HttpResponse): + return 'This function should fail!!' diff --git a/tests/unittests/test_broken_functions.py b/tests/unittests/test_broken_functions.py index 0be21c84d..bf92fb615 100644 --- a/tests/unittests/test_broken_functions.py +++ b/tests/unittests/test_broken_functions.py @@ -201,10 +201,9 @@ async def test_load_broken__invalid_http_trigger_anno(self): self.assertEqual( r.response.result.exception.message, - 'FunctionLoadError: cannot load the invalid_http_trigger_anno ' - 'function: \'req\' binding type "httpTrigger" and dataType "0"' - ' in function.json do not match the corresponding function' - ' parameter\'s Python type annotation "int"') + 'FunctionLoadError: cannot load the invalid_http_trigger_anno' + ' function: type of req binding in function.json "httpTrigger" ' + 'does not match its Python annotation "int"') async def test_load_broken__invalid_out_anno(self): async with testutils.start_mockhost( @@ -218,9 +217,8 @@ async def test_load_broken__invalid_out_anno(self): self.assertEqual( r.response.result.exception.message, 'FunctionLoadError: cannot load the invalid_out_anno function: ' - '\'ret\' binding type "http" and dataType "0" in function.json' - ' do not match the corresponding function parameter\'s Python' - ' type annotation "HttpRequest"') + r'type of ret binding in function.json "http" ' + r'does not match its Python annotation "HttpRequest"') async def test_load_broken__invalid_in_anno(self): async with testutils.start_mockhost( @@ -233,10 +231,25 @@ async def test_load_broken__invalid_in_anno(self): self.assertEqual( r.response.result.exception.message, - 'FunctionLoadError: cannot load the invalid_in_anno function:' - ' \'req\' binding type "httpTrigger" and dataType "0" in ' - 'function.json do not match the corresponding function ' - 'parameter\'s Python type annotation "HttpResponse"') + 'FunctionLoadError: cannot load the invalid_in_anno function: ' + r'type of req binding in function.json "httpTrigger" ' + r'does not match its Python annotation "HttpResponse"') + + async def test_load_broken__invalid_datatype(self): + async with testutils.start_mockhost( + script_root=self.broken_funcs_dir) as host: + func_id, r = await host.load_function('invalid_datatype') + + self.assertEqual(r.response.function_id, func_id) + self.assertEqual(r.response.result.status, + protos.StatusResult.Failure) + + self.assertRegex( + r.response.result.exception.message, + r'.*cannot load the invalid_datatype function: ' + r'.*binding type "httpTrigger" and dataType "1" in ' + r'function.json do not match the corresponding function ' + r'parameter.* Python type annotation "HttpResponse"') async def test_load_broken__invalid_in_anno_non_type(self): async with testutils.start_mockhost( diff --git a/tests/unittests/test_utilities.py b/tests/unittests/test_utilities.py index 91db929b9..7112a27c5 100644 --- a/tests/unittests/test_utilities.py +++ b/tests/unittests/test_utilities.py @@ -2,13 +2,12 @@ # Licensed under the MIT License. import os import sys +import typing import unittest from unittest.mock import patch -import typing from azure_functions_worker.utils import common, wrappers - TEST_APP_SETTING_NAME = "TEST_APP_SETTING_NAME" TEST_FEATURE_FLAG = "APP_SETTING_FEATURE_FLAG" FEATURE_DEFAULT = 42