|
|
|
@ -1,5 +1,3 @@ |
|
|
|
from typing import Union |
|
|
|
|
|
|
|
from fastapi import FastAPI, UploadFile |
|
|
|
from fastapi._compat import ( |
|
|
|
Undefined, |
|
|
|
@ -10,8 +8,6 @@ from fastapi.testclient import TestClient |
|
|
|
from pydantic import BaseModel, ConfigDict |
|
|
|
from pydantic.fields import FieldInfo |
|
|
|
|
|
|
|
from .utils import needs_py310 |
|
|
|
|
|
|
|
|
|
|
|
def test_model_field_default_required(): |
|
|
|
from fastapi._compat import v2 |
|
|
|
@ -26,7 +22,7 @@ def test_complex(): |
|
|
|
app = FastAPI() |
|
|
|
|
|
|
|
@app.post("/") |
|
|
|
def foo(foo: Union[str, list[int]]): |
|
|
|
def foo(foo: str | list[int]): |
|
|
|
return foo |
|
|
|
|
|
|
|
client = TestClient(app) |
|
|
|
@ -49,17 +45,17 @@ def test_propagates_pydantic2_model_config(): |
|
|
|
|
|
|
|
class EmbeddedModel(BaseModel): |
|
|
|
model_config = ConfigDict(arbitrary_types_allowed=True) |
|
|
|
value: Union[str, Missing] = Missing() |
|
|
|
value: str | Missing = Missing() |
|
|
|
|
|
|
|
class Model(BaseModel): |
|
|
|
model_config = ConfigDict( |
|
|
|
arbitrary_types_allowed=True, |
|
|
|
) |
|
|
|
value: Union[str, Missing] = Missing() |
|
|
|
value: str | Missing = Missing() |
|
|
|
embedded_model: EmbeddedModel = EmbeddedModel() |
|
|
|
|
|
|
|
@app.post("/") |
|
|
|
def foo(req: Model) -> dict[str, Union[str, None]]: |
|
|
|
def foo(req: Model) -> dict[str, str | None]: |
|
|
|
return { |
|
|
|
"value": req.value or None, |
|
|
|
"embedded_value": req.embedded_model.value or None, |
|
|
|
@ -89,7 +85,7 @@ def test_is_bytes_sequence_annotation_union(): |
|
|
|
# TODO: in theory this would allow declaring types that could be lists of bytes |
|
|
|
# to be read from files and other types, but I'm not even sure it's a good idea |
|
|
|
# to support it as a first class "feature" |
|
|
|
assert is_bytes_sequence_annotation(Union[list[str], list[bytes]]) |
|
|
|
assert is_bytes_sequence_annotation(list[str] | list[bytes]) |
|
|
|
|
|
|
|
|
|
|
|
def test_is_uploadfile_sequence_annotation(): |
|
|
|
@ -97,21 +93,20 @@ def test_is_uploadfile_sequence_annotation(): |
|
|
|
# TODO: in theory this would allow declaring types that could be lists of UploadFile |
|
|
|
# and other types, but I'm not even sure it's a good idea to support it as a first |
|
|
|
# class "feature" |
|
|
|
assert is_uploadfile_sequence_annotation(Union[list[str], list[UploadFile]]) |
|
|
|
assert is_uploadfile_sequence_annotation(list[str] | list[UploadFile]) |
|
|
|
|
|
|
|
|
|
|
|
def test_serialize_sequence_value_with_optional_list(): |
|
|
|
"""Test that serialize_sequence_value handles optional lists correctly.""" |
|
|
|
from fastapi._compat import v2 |
|
|
|
|
|
|
|
field_info = FieldInfo(annotation=Union[list[str], None]) |
|
|
|
field_info = FieldInfo(annotation=list[str] | None) |
|
|
|
field = v2.ModelField(name="items", field_info=field_info) |
|
|
|
result = v2.serialize_sequence_value(field=field, value=["a", "b", "c"]) |
|
|
|
assert result == ["a", "b", "c"] |
|
|
|
assert isinstance(result, list) |
|
|
|
|
|
|
|
|
|
|
|
@needs_py310 |
|
|
|
def test_serialize_sequence_value_with_optional_list_pipe_union(): |
|
|
|
"""Test that serialize_sequence_value handles optional lists correctly (with new syntax).""" |
|
|
|
from fastapi._compat import v2 |
|
|
|
@ -125,9 +120,12 @@ def test_serialize_sequence_value_with_optional_list_pipe_union(): |
|
|
|
|
|
|
|
def test_serialize_sequence_value_with_none_first_in_union(): |
|
|
|
"""Test that serialize_sequence_value handles Union[None, List[...]] correctly.""" |
|
|
|
from typing import Union |
|
|
|
|
|
|
|
from fastapi._compat import v2 |
|
|
|
|
|
|
|
field_info = FieldInfo(annotation=Union[None, list[str]]) |
|
|
|
# Use Union[None, list[str]] to ensure None comes first in the union args |
|
|
|
field_info = FieldInfo(annotation=Union[None, list[str]]) # noqa: UP007 |
|
|
|
field = v2.ModelField(name="items", field_info=field_info) |
|
|
|
result = v2.serialize_sequence_value(field=field, value=["x", "y"]) |
|
|
|
assert result == ["x", "y"] |
|
|
|
|