From 68db03aa3d1b42866cf583fa1e90bf6aa79d55d4 Mon Sep 17 00:00:00 2001 From: Yeongseon Choe Date: Tue, 27 Oct 2026 00:03:29 +0900 Subject: [PATCH 1/2] fix: diagnose mismatched model injection names during decoration --- src/azure_functions_validation/decorator.py | 20 +++++++++++++++++ tests/test_injected_parameter_diagnostic.py | 25 +++++++++++++++++++++ 2 files changed, 45 insertions(+) create mode 100644 tests/test_injected_parameter_diagnostic.py diff --git a/src/azure_functions_validation/decorator.py b/src/azure_functions_validation/decorator.py index afcf9df..5de4211 100644 --- a/src/azure_functions_validation/decorator.py +++ b/src/azure_functions_validation/decorator.py @@ -307,6 +307,26 @@ def _validate_no_conflicts( f"conflicts with a @validate_http injected parameter of the same name. " f"Rename it (e.g. to 'req' or 'http_request')." ) + parameters = inspect.signature(func).parameters + try: + annotations = get_type_hints(func) + except (NameError, TypeError): + annotations = getattr(func, "__annotations__", {}) + for name, parameter in parameters.items(): + if name == request_param_name or name in _injected: + continue + if parameter.default is not inspect.Parameter.empty: + continue + annotation = annotations.get(name) + expected = [ + key for key, model in _injected.items() if model is not None and annotation is model + ] + if expected: + raise ValueError( + f"Function {func.__name__}: parameter '{name}' matches a configured model; " + f"expected injected parameter name(s): {', '.join(expected)}. " + "Validation injects by name, not by type." + ) _MISSING: Any = object() # sentinel for absent req argument diff --git a/tests/test_injected_parameter_diagnostic.py b/tests/test_injected_parameter_diagnostic.py new file mode 100644 index 0000000..11bb107 --- /dev/null +++ b/tests/test_injected_parameter_diagnostic.py @@ -0,0 +1,25 @@ +from azure.functions import HttpRequest +from pydantic import BaseModel +import pytest + +from azure_functions_validation import validate_http + + +class Input(BaseModel): + name: str + + +def test_mismatched_body_model_parameter_fails_during_decoration() -> None: + with pytest.raises(ValueError, match="payload.*expected.*body"): + + @validate_http(body=Input) + def handler(req: HttpRequest, payload: Input) -> Input: + return payload + + +def test_correctly_named_model_and_independent_bindings_remain_supported() -> None: + @validate_http(body=Input) + def handler(req: HttpRequest, body: Input, context: str) -> Input: + return body + + assert callable(handler) From 9b2c2e91c6fd09f9207faac1820e39b0189ba9fe Mon Sep 17 00:00:00 2001 From: Yeongseon Choe Date: Tue, 27 Oct 2026 00:03:29 +0900 Subject: [PATCH 2/2] docs: explain model injection parameter names --- docs/configuration.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/docs/configuration.md b/docs/configuration.md index e12c9be..6b72173 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -1,5 +1,13 @@ # Configuration +Validation injects configured models by parameter name: `body`, `query`, `path` +and `headers` (`req_model` for the deprecated shorthand). A required handler +parameter annotated with a configured model but named differently, such as +`payload: Input` for `body=Input`, raises a decoration-time configuration error. +Rename it to `body`; there is no implicit injection by type. Independent Azure +binding parameters and handlers that validate without consuming a model remain +supported. + This page documents every parameter of `validate_http` and how to combine them in production handlers.