|
|
|
@ -12,7 +12,6 @@ This test suite validates that: |
|
|
|
|
|
|
|
import sys |
|
|
|
import warnings |
|
|
|
from typing import Any |
|
|
|
|
|
|
|
import pytest |
|
|
|
from fastapi import FastAPI |
|
|
|
@ -27,60 +26,69 @@ class TestV2FirstCompatibility: |
|
|
|
"""Test that v2-only usage doesn't trigger warnings on Python 3.14.""" |
|
|
|
if sys.version_info < (3, 14): |
|
|
|
pytest.skip("Python 3.14+ specific test") |
|
|
|
|
|
|
|
|
|
|
|
# Capture warnings |
|
|
|
with warnings.catch_warnings(record=True) as w: |
|
|
|
warnings.simplefilter("error", DeprecationWarning) |
|
|
|
|
|
|
|
|
|
|
|
# These should not trigger any warnings |
|
|
|
import fastapi |
|
|
|
import fastapi.encoders |
|
|
|
from fastapi import FastAPI |
|
|
|
|
|
|
|
|
|
|
|
_ = FastAPI() # Create app but don't use it |
|
|
|
|
|
|
|
# Test jsonable_encoder with v2 model |
|
|
|
class TestModel(BaseModel): |
|
|
|
name: str |
|
|
|
value: int |
|
|
|
|
|
|
|
|
|
|
|
model = TestModel(name="test", value=42) |
|
|
|
result = jsonable_encoder(model) |
|
|
|
|
|
|
|
|
|
|
|
assert result == {"name": "test", "value": 42} |
|
|
|
assert len(w) == 0, f"Unexpected warnings: {[str(warning.message) for warning in w]}" |
|
|
|
assert len(w) == 0, ( |
|
|
|
f"Unexpected warnings: {[str(warning.message) for warning in w]}" |
|
|
|
) |
|
|
|
|
|
|
|
def test_proxy_classes_isinstance(self): |
|
|
|
"""Test that proxy classes work correctly with isinstance().""" |
|
|
|
import fastapi.temp_pydantic_v1_params as T |
|
|
|
from fastapi import params |
|
|
|
|
|
|
|
|
|
|
|
# Test that proxy classes are available |
|
|
|
assert hasattr(T, 'Param') |
|
|
|
assert hasattr(T, 'Body') |
|
|
|
assert hasattr(T, 'Form') |
|
|
|
assert hasattr(T, 'File') |
|
|
|
assert hasattr(T, 'Query') |
|
|
|
assert hasattr(T, 'Header') |
|
|
|
assert hasattr(T, 'Cookie') |
|
|
|
assert hasattr(T, 'Path') |
|
|
|
|
|
|
|
assert hasattr(T, "Param") |
|
|
|
assert hasattr(T, "Body") |
|
|
|
assert hasattr(T, "Form") |
|
|
|
assert hasattr(T, "File") |
|
|
|
assert hasattr(T, "Query") |
|
|
|
assert hasattr(T, "Header") |
|
|
|
assert hasattr(T, "Cookie") |
|
|
|
assert hasattr(T, "Path") |
|
|
|
|
|
|
|
# Test isinstance() with proxy classes (expect DeprecationWarning) |
|
|
|
with warnings.catch_warnings(): |
|
|
|
warnings.simplefilter("ignore", (DeprecationWarning, UserWarning)) |
|
|
|
param_instance = T.Param() |
|
|
|
assert isinstance(param_instance, T.Param) |
|
|
|
|
|
|
|
|
|
|
|
# Test that proxy classes are different from v2 params |
|
|
|
from fastapi import params as v2_params |
|
|
|
|
|
|
|
assert T.Param is not v2_params.Param |
|
|
|
assert T.Body is not v2_params.Body |
|
|
|
|
|
|
|
def test_backward_compatibility_imports(self): |
|
|
|
"""Test that existing imports continue to work.""" |
|
|
|
# Test direct import from temp_pydantic_v1_params |
|
|
|
from fastapi.temp_pydantic_v1_params import Body, Query, Form, File, Param, Header, Cookie, Path |
|
|
|
|
|
|
|
from fastapi.temp_pydantic_v1_params import ( |
|
|
|
Body, |
|
|
|
Cookie, |
|
|
|
File, |
|
|
|
Form, |
|
|
|
Header, |
|
|
|
Param, |
|
|
|
Path, |
|
|
|
Query, |
|
|
|
) |
|
|
|
|
|
|
|
# Test that classes are available and callable (expect DeprecationWarning) |
|
|
|
with warnings.catch_warnings(): |
|
|
|
warnings.simplefilter("ignore", (DeprecationWarning, UserWarning)) |
|
|
|
@ -92,50 +100,47 @@ class TestV2FirstCompatibility: |
|
|
|
header = Header() |
|
|
|
cookie = Cookie() |
|
|
|
path = Path() |
|
|
|
|
|
|
|
|
|
|
|
# Test that they have expected attributes |
|
|
|
assert hasattr(body, 'default') |
|
|
|
assert hasattr(query, 'default') |
|
|
|
assert hasattr(form, 'default') |
|
|
|
assert hasattr(file_param, 'default') |
|
|
|
assert hasattr(param, 'default') |
|
|
|
assert hasattr(header, 'default') |
|
|
|
assert hasattr(cookie, 'default') |
|
|
|
assert hasattr(path, 'default') |
|
|
|
assert hasattr(body, "default") |
|
|
|
assert hasattr(query, "default") |
|
|
|
assert hasattr(form, "default") |
|
|
|
assert hasattr(file_param, "default") |
|
|
|
assert hasattr(param, "default") |
|
|
|
assert hasattr(header, "default") |
|
|
|
assert hasattr(cookie, "default") |
|
|
|
assert hasattr(path, "default") |
|
|
|
|
|
|
|
def test_lazy_loading_behavior(self): |
|
|
|
"""Test that v1 is only loaded when actually used.""" |
|
|
|
import sys |
|
|
|
|
|
|
|
|
|
|
|
# Clear any existing pydantic.v1 from sys.modules |
|
|
|
_ = "pydantic.v1" in sys.modules # Check but don't use |
|
|
|
|
|
|
|
|
|
|
|
# Import FastAPI components |
|
|
|
import fastapi |
|
|
|
import fastapi.encoders |
|
|
|
from fastapi import FastAPI |
|
|
|
|
|
|
|
|
|
|
|
_ = FastAPI() # Create app but don't use it |
|
|
|
|
|
|
|
|
|
|
|
# At this point, pydantic.v1 should not be loaded unless it was already loaded |
|
|
|
# (we can't test the exact state because it might have been loaded by other tests) |
|
|
|
|
|
|
|
|
|
|
|
# Test that we can still access v1 proxy |
|
|
|
from fastapi._compat import v1 |
|
|
|
|
|
|
|
assert v1 is not None |
|
|
|
|
|
|
|
def test_encoders_lazy_registration(self): |
|
|
|
"""Test that v1 encoders are registered lazily.""" |
|
|
|
import sys |
|
|
|
|
|
|
|
|
|
|
|
# Test with a v2 model (should not trigger v1 encoder registration) |
|
|
|
class V2Model(BaseModel): |
|
|
|
name: str |
|
|
|
|
|
|
|
|
|
|
|
model = V2Model(name="test") |
|
|
|
result = jsonable_encoder(model) |
|
|
|
assert result == {"name": "test"} |
|
|
|
|
|
|
|
|
|
|
|
# The encoders should work without importing pydantic.v1 |
|
|
|
# (unless it was already imported by other tests) |
|
|
|
|
|
|
|
@ -145,36 +150,36 @@ class TestV2FirstCompatibility: |
|
|
|
with warnings.catch_warnings(): |
|
|
|
warnings.simplefilter("ignore", (DeprecationWarning, UserWarning)) |
|
|
|
from fastapi._compat import v1 |
|
|
|
|
|
|
|
|
|
|
|
# Test that v1 proxy has expected attributes |
|
|
|
assert hasattr(v1, 'BaseModel') |
|
|
|
assert hasattr(v1, 'FieldInfo') |
|
|
|
assert hasattr(v1, 'ValidationError') |
|
|
|
|
|
|
|
assert hasattr(v1, "BaseModel") |
|
|
|
assert hasattr(v1, "FieldInfo") |
|
|
|
assert hasattr(v1, "ValidationError") |
|
|
|
|
|
|
|
# Test that wrapper functions are available |
|
|
|
assert hasattr(v1, '_normalize_errors') |
|
|
|
assert hasattr(v1, '_model_dump') |
|
|
|
assert hasattr(v1, '_model_rebuild') |
|
|
|
assert hasattr(v1, "_normalize_errors") |
|
|
|
assert hasattr(v1, "_model_dump") |
|
|
|
assert hasattr(v1, "_model_rebuild") |
|
|
|
|
|
|
|
def test_strict_mode_environment_variable(self): |
|
|
|
"""Test FASTAPI_PYDANTIC_V1_STRICT environment variable behavior.""" |
|
|
|
import os |
|
|
|
|
|
|
|
|
|
|
|
# Save original value |
|
|
|
original_strict = os.environ.get("FASTAPI_PYDANTIC_V1_STRICT") |
|
|
|
|
|
|
|
|
|
|
|
try: |
|
|
|
# Test with strict mode enabled |
|
|
|
os.environ["FASTAPI_PYDANTIC_V1_STRICT"] = "1" |
|
|
|
|
|
|
|
|
|
|
|
# This should not raise an error unless we actually try to use v1 |
|
|
|
import fastapi |
|
|
|
from fastapi import FastAPI |
|
|
|
|
|
|
|
_ = FastAPI() # Create app but don't use it |
|
|
|
|
|
|
|
|
|
|
|
# The strict mode only affects actual v1 usage, not imports |
|
|
|
assert True |
|
|
|
|
|
|
|
|
|
|
|
finally: |
|
|
|
# Restore original value |
|
|
|
if original_strict is not None: |
|
|
|
@ -184,24 +189,24 @@ class TestV2FirstCompatibility: |
|
|
|
|
|
|
|
def test_v1_params_composition(self): |
|
|
|
"""Test that v1 params use composition instead of inheritance.""" |
|
|
|
from fastapi._compat._v1_params import Param, Body, Form |
|
|
|
|
|
|
|
from fastapi._compat._v1_params import Body, Form, Param |
|
|
|
|
|
|
|
# Test that they can be instantiated (expect DeprecationWarning) |
|
|
|
with warnings.catch_warnings(): |
|
|
|
warnings.simplefilter("ignore", (DeprecationWarning, UserWarning)) |
|
|
|
param = Param() |
|
|
|
body = Body() |
|
|
|
form = Form() |
|
|
|
|
|
|
|
|
|
|
|
# Test that they delegate to internal v1.FieldInfo |
|
|
|
assert hasattr(param, '_fi') |
|
|
|
assert hasattr(body, '_fi') |
|
|
|
assert hasattr(form, '_fi') |
|
|
|
|
|
|
|
assert hasattr(param, "_fi") |
|
|
|
assert hasattr(body, "_fi") |
|
|
|
assert hasattr(form, "_fi") |
|
|
|
|
|
|
|
# Test that they have expected interface |
|
|
|
assert hasattr(param, 'default') |
|
|
|
assert hasattr(body, 'default') |
|
|
|
assert hasattr(form, 'default') |
|
|
|
assert hasattr(param, "default") |
|
|
|
assert hasattr(body, "default") |
|
|
|
assert hasattr(form, "default") |
|
|
|
|
|
|
|
|
|
|
|
if __name__ == "__main__": |
|
|
|
|