diff --git a/src/azure_functions_validation/decorator.py b/src/azure_functions_validation/decorator.py index 5a387fc..afcf9df 100644 --- a/src/azure_functions_validation/decorator.py +++ b/src/azure_functions_validation/decorator.py @@ -4,12 +4,12 @@ from collections.abc import Callable, Mapping import inspect +import sys import types from typing import Any, get_type_hints import warnings from pydantic import TypeAdapter -from pydantic.v1 import BaseModel as BaseModelV1 from ._endpoint import build_endpoint_metadata, set_endpoint_metadata from ._metadata import METADATA_ATTR, ValidationMetadata, set_validation_metadata @@ -146,7 +146,13 @@ def decorator(func: Callable[..., Any]) -> Callable[..., Any]: ("request_model", request_model), ("response_model", response_model), ): - if isinstance(model, type(BaseModelV1)) and issubclass(model, BaseModelV1): + legacy_module = sys.modules.get("pydantic.v1") + legacy_base = getattr(legacy_module, "BaseModel", None) + if ( + legacy_base is not None + and isinstance(model, type) + and issubclass(model, legacy_base) + ): raise TypeError( f"{option_name} uses a Pydantic v1 model; Pydantic v2 models are required" ) diff --git a/tests/test_lazy_legacy_import.py b/tests/test_lazy_legacy_import.py new file mode 100644 index 0000000..bdc9c0c --- /dev/null +++ b/tests/test_lazy_legacy_import.py @@ -0,0 +1,20 @@ +import subprocess +import sys + + +def test_v2_decoration_does_not_import_legacy_pydantic() -> None: + result = subprocess.run( + [ + sys.executable, + "-c", + "import sys; from pydantic import BaseModel; " + "from azure_functions_validation import validate_http; " + "Model = type('Model', (BaseModel,), {'__annotations__': {'name': str}}); " + "validate_http(body=Model)(lambda req, body: body); " + "assert 'pydantic.v1' not in sys.modules", + ], + capture_output=True, + text=True, + timeout=30, + ) + assert result.returncode == 0, result.stderr