🎨 Upgrade typing syntax for Python 3.10 (#14932)

Co-authored-by: Copilot <198982749+Copilot@users.noreply.github.com>
Co-authored-by: tiangolo <1326112+tiangolo@users.noreply.github.com>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
This commit is contained in:
Sebastián Ramírez 2026-02-17 01:59:14 -08:00 committed by GitHub
parent 6e5680c7ea
commit faee822574
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
77 changed files with 269 additions and 375 deletions

View File

@ -1,5 +1,3 @@
from typing import Union
from fastapi import FastAPI
from pydantic import BaseModel
@ -30,6 +28,6 @@ items = {
}
@app.get("/items/{item_id}", response_model=Union[PlaneItem, CarItem])
@app.get("/items/{item_id}", response_model=PlaneItem | CarItem)
async def read_item(item_id: str):
return items[item_id]

View File

@ -1,5 +1,5 @@
import re
from typing import TypedDict, Union
from typing import TypedDict
CODE_INCLUDE_RE = re.compile(r"^\{\*\s*(\S+)\s*(.*)\*\}$")
CODE_INCLUDE_PLACEHOLDER = "<CODE_INCLUDE>"
@ -50,8 +50,8 @@ class MarkdownLinkInfo(TypedDict):
line_no: int
url: str
text: str
title: Union[str, None]
attributes: Union[str, None]
title: str | None
attributes: str | None
full_match: str
@ -287,8 +287,8 @@ def _add_lang_code_to_url(url: str, lang_code: str) -> str:
def _construct_markdown_link(
url: str,
text: str,
title: Union[str, None],
attributes: Union[str, None],
title: str | None,
attributes: str | None,
lang_code: str,
) -> str:
"""
@ -549,7 +549,7 @@ def extract_multiline_code_blocks(text: list[str]) -> list[MultilineCodeBlockInf
return blocks
def _split_hash_comment(line: str) -> tuple[str, Union[str, None]]:
def _split_hash_comment(line: str) -> tuple[str, str | None]:
match = HASH_COMMENT_RE.match(line)
if match:
code = match.group("code").rstrip()
@ -558,7 +558,7 @@ def _split_hash_comment(line: str) -> tuple[str, Union[str, None]]:
return line.rstrip(), None
def _split_slashes_comment(line: str) -> tuple[str, Union[str, None]]:
def _split_slashes_comment(line: str) -> tuple[str, str | None]:
match = SLASHES_COMMENT_RE.match(line)
if match:
code = match.group("code").rstrip()
@ -603,9 +603,9 @@ def replace_multiline_code_block(
return block_a["content"].copy() # We don't handle mermaid code blocks for now
code_block: list[str] = []
for line_a, line_b in zip(block_a["content"], block_b["content"]):
line_a_comment: Union[str, None] = None
line_b_comment: Union[str, None] = None
for line_a, line_b in zip(block_a["content"], block_b["content"], strict=False):
line_a_comment: str | None = None
line_b_comment: str | None = None
# Handle comments based on language
if block_language in {
@ -659,7 +659,7 @@ def replace_multiline_code_blocks_in_text(
)
modified_text = text.copy()
for block, original_block in zip(code_blocks, original_code_blocks):
for block, original_block in zip(code_blocks, original_code_blocks, strict=True):
updated_content = replace_multiline_code_block(block, original_block)
start_line_index = block["start_line_no"] - 1

View File

@ -1,6 +1,6 @@
from functools import lru_cache
from pathlib import Path
from typing import Any, Union
from typing import Any
import material
from mkdocs.config.defaults import MkDocsConfig
@ -105,9 +105,9 @@ def on_files(files: Files, *, config: MkDocsConfig) -> Files:
def generate_renamed_section_items(
items: list[Union[Page, Section, Link]], *, config: MkDocsConfig
) -> list[Union[Page, Section, Link]]:
new_items: list[Union[Page, Section, Link]] = []
items: list[Page | Section | Link], *, config: MkDocsConfig
) -> list[Page | Section | Link]:
new_items: list[Page | Section | Link] = []
for item in items:
if isinstance(item, Section):
new_title = item.title

View File

@ -3,7 +3,7 @@ import random
import sys
import time
from pathlib import Path
from typing import Any, Union, cast
from typing import Any, cast
import httpx
from github import Github
@ -181,9 +181,9 @@ class Settings(BaseSettings):
github_repository: str
github_token: SecretStr
github_event_path: Path
github_event_name: Union[str, None] = None
github_event_name: str | None = None
httpx_timeout: int = 30
debug: Union[bool, None] = False
debug: bool | None = False
number: int | None = None
@ -199,12 +199,12 @@ def get_graphql_response(
*,
settings: Settings,
query: str,
after: Union[str, None] = None,
category_id: Union[str, None] = None,
discussion_number: Union[int, None] = None,
discussion_id: Union[str, None] = None,
comment_id: Union[str, None] = None,
body: Union[str, None] = None,
after: str | None = None,
category_id: str | None = None,
discussion_number: int | None = None,
discussion_id: str | None = None,
comment_id: str | None = None,
body: str | None = None,
) -> dict[str, Any]:
headers = {"Authorization": f"token {settings.github_token.get_secret_value()}"}
variables = {
@ -249,7 +249,7 @@ def get_graphql_translation_discussions(
def get_graphql_translation_discussion_comments_edges(
*, settings: Settings, discussion_number: int, after: Union[str, None] = None
*, settings: Settings, discussion_number: int, after: str | None = None
) -> list[CommentsEdge]:
data = get_graphql_response(
settings=settings,
@ -372,8 +372,8 @@ def main() -> None:
f"Found a translation discussion for language: {lang} in discussion: #{discussion.number}"
)
already_notified_comment: Union[Comment, None] = None
already_done_comment: Union[Comment, None] = None
already_notified_comment: Comment | None = None
already_done_comment: Comment | None = None
logging.info(
f"Checking current comments in discussion: #{discussion.number} to see if already notified about this PR: #{pr.number}"

View File

@ -6,7 +6,7 @@ from collections import Counter
from collections.abc import Container
from datetime import datetime, timedelta, timezone
from pathlib import Path
from typing import Any, Union
from typing import Any
import httpx
import yaml
@ -70,7 +70,7 @@ class Author(BaseModel):
class CommentsNode(BaseModel):
createdAt: datetime
author: Union[Author, None] = None
author: Author | None = None
class Replies(BaseModel):
@ -89,7 +89,7 @@ class DiscussionsComments(BaseModel):
class DiscussionsNode(BaseModel):
number: int
author: Union[Author, None] = None
author: Author | None = None
title: str | None = None
createdAt: datetime
comments: DiscussionsComments
@ -127,8 +127,8 @@ def get_graphql_response(
*,
settings: Settings,
query: str,
after: Union[str, None] = None,
category_id: Union[str, None] = None,
after: str | None = None,
category_id: str | None = None,
) -> dict[str, Any]:
headers = {"Authorization": f"token {settings.github_token.get_secret_value()}"}
variables = {"after": after, "category_id": category_id}
@ -156,7 +156,7 @@ def get_graphql_response(
def get_graphql_question_discussion_edges(
*,
settings: Settings,
after: Union[str, None] = None,
after: str | None = None,
) -> list[DiscussionsEdge]:
data = get_graphql_response(
settings=settings,

View File

@ -1,5 +1,4 @@
import http
from typing import Optional
from fastapi import FastAPI, Path, Query
@ -54,7 +53,7 @@ def get_bool_id(item_id: bool):
@app.get("/path/param/{item_id}")
def get_path_param_id(item_id: Optional[str] = Path()):
def get_path_param_id(item_id: str | None = Path()):
return item_id
@ -161,7 +160,7 @@ def get_query_type(query: int):
@app.get("/query/int/optional")
def get_query_type_optional(query: Optional[int] = None):
def get_query_type_optional(query: int | None = None):
if query is None:
return "foo bar"
return f"foo bar {query}"

View File

@ -1,5 +1,3 @@
from typing import Union
from fastapi import FastAPI
from fastapi.testclient import TestClient
from inline_snapshot import snapshot
@ -19,7 +17,7 @@ app = FastAPI()
@app.post("/")
async def post(
foo: Union[Foo, None] = None,
foo: Foo | None = None,
):
return foo

View File

@ -4,8 +4,6 @@ don't accumulate duplicate $ref entries in anyOf arrays.
See https://github.com/fastapi/fastapi/pull/14463
"""
from typing import Union
from fastapi import FastAPI
from fastapi.testclient import TestClient
from inline_snapshot import snapshot
@ -23,7 +21,7 @@ class ModelB(BaseModel):
app = FastAPI(
responses={
500: {
"model": Union[ModelA, ModelB],
"model": ModelA | ModelB,
"content": {"application/json": {"examples": {"Case A": {"value": "a"}}}},
}
}

View File

@ -1,11 +1,10 @@
from functools import partial
from typing import Optional
from fastapi import FastAPI
from fastapi.testclient import TestClient
def main(some_arg, q: Optional[str] = None):
def main(some_arg, q: str | None = None):
return {"some_arg": some_arg, "q": q}

View File

@ -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"]

View File

@ -1,5 +1,4 @@
from pathlib import Path
from typing import Optional
from fastapi import APIRouter, FastAPI, File, UploadFile
from fastapi.exceptions import HTTPException
@ -17,7 +16,7 @@ class ContentSizeLimitMiddleware:
max_content_size (optional): the maximum content size allowed in bytes, None for no limit
"""
def __init__(self, app: APIRouter, max_content_size: Optional[int] = None):
def __init__(self, app: APIRouter, max_content_size: int | None = None):
self.app = app
self.max_content_size = max_content_size

View File

@ -1,4 +1,4 @@
from typing import Annotated, Optional
from typing import Annotated
from fastapi import FastAPI
from fastapi.testclient import TestClient
@ -10,9 +10,9 @@ app = FastAPI()
class Item(BaseModel):
name: str
description: Annotated[
Optional[str], WithJsonSchema({"type": ["string", "null"]})
] = None
description: Annotated[str | None, WithJsonSchema({"type": ["string", "null"]})] = (
None
)
model_config = {
"json_schema_extra": {

View File

@ -1,11 +1,11 @@
from collections.abc import Awaitable
from collections.abc import Awaitable, Callable
from contextvars import ContextVar
from typing import Any, Callable, Optional
from typing import Any
from fastapi import Depends, FastAPI, Request, Response
from fastapi.testclient import TestClient
legacy_request_state_context_var: ContextVar[Optional[dict[str, Any]]] = ContextVar(
legacy_request_state_context_var: ContextVar[dict[str, Any] | None] = ContextVar(
"legacy_request_state_context_var", default=None
)

View File

@ -1,5 +1,3 @@
from typing import Optional
import pytest
from fastapi import APIRouter, Depends, FastAPI
from fastapi.testclient import TestClient
@ -38,7 +36,7 @@ app.include_router(router)
client = TestClient(app)
async def overrider_dependency_simple(q: Optional[str] = None):
async def overrider_dependency_simple(q: str | None = None):
return {"q": q, "skip": 5, "limit": 10}

View File

@ -1,4 +1,4 @@
from typing import Annotated, Union
from typing import Annotated
from fastapi import FastAPI, HTTPException, Security
from fastapi.security import (
@ -13,7 +13,7 @@ oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")
def process_auth(
credentials: Annotated[Union[str, None], Security(oauth2_scheme)],
credentials: Annotated[str | None, Security(oauth2_scheme)],
security_scopes: SecurityScopes,
):
# This is an incorrect way of using it, this is not checking if the scopes are

View File

@ -1,5 +1,3 @@
from typing import Optional
from fastapi import Depends, FastAPI, Query
from fastapi.testclient import TestClient
from inline_snapshot import snapshot
@ -11,7 +9,7 @@ def _get_client_key(client_id: str = Query(...)) -> str:
return f"{client_id}_key"
def _get_client_tag(client_id: Optional[str] = Query(None)) -> Optional[str]:
def _get_client_tag(client_id: str | None = Query(None)) -> str | None:
if client_id is None:
return None
return f"{client_id}_tag"
@ -20,7 +18,7 @@ def _get_client_tag(client_id: Optional[str] = Query(None)) -> Optional[str]:
@app.get("/foo")
def foo_handler(
client_key: str = Depends(_get_client_key),
client_tag: Optional[str] = Depends(_get_client_tag),
client_tag: str | None = Depends(_get_client_tag),
):
return {"client_id": client_key, "client_tag": client_tag}

View File

@ -1,5 +1,3 @@
from typing import Optional
from fastapi import FastAPI
from fastapi.responses import JSONResponse
from fastapi.testclient import TestClient
@ -11,7 +9,7 @@ app = FastAPI()
class Item(BaseModel):
name: str
price: Optional[float] = None
price: float | None = None
@app.api_route("/items/{item_id}", methods=["GET"])

View File

@ -1,5 +1,3 @@
from typing import Optional
import pytest
from dirty_equals import HasRepr
from fastapi import Depends, FastAPI
@ -22,7 +20,7 @@ def get_client():
class ModelA(BaseModel):
name: str
description: Optional[str] = None
description: str | None = None
foo: ModelB
tags: dict[str, str] = {}

View File

@ -1,4 +1,4 @@
from typing import Annotated, Optional
from typing import Annotated
from fastapi import FastAPI, File, Form
from starlette.testclient import TestClient
@ -7,14 +7,14 @@ app = FastAPI()
@app.post("/urlencoded")
async def post_url_encoded(age: Annotated[Optional[int], Form()] = None):
async def post_url_encoded(age: Annotated[int | None, Form()] = None):
return age
@app.post("/multipart")
async def post_multi_part(
age: Annotated[Optional[int], Form()] = None,
file: Annotated[Optional[bytes], File()] = None,
age: Annotated[int | None, Form()] = None,
file: Annotated[bytes | None, File()] = None,
):
return {"file": file, "age": age}

View File

@ -1,4 +1,4 @@
from typing import Annotated, Optional
from typing import Annotated
from fastapi import FastAPI, Form
from fastapi.testclient import TestClient
@ -10,7 +10,7 @@ app = FastAPI()
class FormModel(BaseModel):
username: str
lastname: str
age: Optional[int] = None
age: int | None = None
tags: list[str] = ["foo", "bar"]
alias_with: str = Field(alias="with", default="nothing")

View File

@ -1,5 +1,3 @@
from typing import Optional
from fastapi import APIRouter, FastAPI
from fastapi.testclient import TestClient
from inline_snapshot import snapshot
@ -22,7 +20,7 @@ def get_user(user_id: str):
@item_router.get("/")
def get_items(user_id: Optional[str] = None):
def get_items(user_id: str | None = None):
if user_id is None:
return [{"item_id": "i1", "user_id": "u1"}, {"item_id": "i2", "user_id": "u2"}]
else:
@ -30,7 +28,7 @@ def get_items(user_id: Optional[str] = None):
@item_router.get("/{item_id}")
def get_item(item_id: str, user_id: Optional[str] = None):
def get_item(item_id: str, user_id: str | None = None):
if user_id is None:
return {"item_id": item_id}
else:

View File

@ -1,5 +1,3 @@
from typing import Optional
import pytest
from fastapi import FastAPI, Query
from pydantic import BaseModel
@ -61,5 +59,5 @@ def test_invalid_simple_dict():
title: str
@app.get("/items/")
def read_items(q: Optional[dict] = Query(default=None)):
def read_items(q: dict | None = Query(default=None)):
pass # pragma: no cover

View File

@ -6,7 +6,7 @@ from decimal import Decimal
from enum import Enum
from math import isinf, isnan
from pathlib import PurePath, PurePosixPath, PureWindowsPath
from typing import Optional, TypedDict
from typing import TypedDict
import pytest
from fastapi._compat import Undefined
@ -57,7 +57,7 @@ class RoleEnum(Enum):
class ModelWithConfig(BaseModel):
role: Optional[RoleEnum] = None
role: RoleEnum | None = None
model_config = {"use_enum_values": True}

View File

@ -1,5 +1,3 @@
from typing import Union
from fastapi import Body, Cookie, FastAPI, Header, Path, Query
from fastapi.testclient import TestClient
from inline_snapshot import snapshot
@ -57,7 +55,7 @@ def path_examples(
@app.get("/query_examples/")
def query_examples(
data: Union[str, None] = Query(
data: str | None = Query(
default=None,
examples=[
"json_schema_query1",
@ -80,7 +78,7 @@ def query_examples(
@app.get("/header_examples/")
def header_examples(
data: Union[str, None] = Header(
data: str | None = Header(
default=None,
examples=[
"json_schema_header1",
@ -103,7 +101,7 @@ def header_examples(
@app.get("/cookie_examples/")
def cookie_examples(
data: Union[str, None] = Cookie(
data: str | None = Cookie(
default=None,
examples=["json_schema_cookie1", "json_schema_cookie2"],
openapi_examples={

View File

@ -1,5 +1,3 @@
from typing import Optional
from fastapi import FastAPI
from fastapi.testclient import TestClient
from inline_snapshot import snapshot
@ -26,7 +24,7 @@ app = FastAPI()
]
},
)
def route_with_extra_query_parameters(standard_query_param: Optional[int] = 50):
def route_with_extra_query_parameters(standard_query_param: int | None = 50):
return {}

View File

@ -1,5 +1,3 @@
from typing import Optional, Union
import pytest
from fastapi.openapi.models import Schema, SchemaType
@ -13,7 +11,7 @@ from fastapi.openapi.models import Schema, SchemaType
],
)
def test_allowed_schema_type(
type_value: Optional[Union[SchemaType, list[SchemaType]]],
type_value: SchemaType | list[SchemaType] | None,
) -> None:
"""Test that Schema accepts SchemaType, List[SchemaType] and None for type field."""
schema = Schema(type=type_value)

View File

@ -1,5 +1,3 @@
from typing import Optional
from fastapi import FastAPI
from fastapi.testclient import TestClient
from inline_snapshot import snapshot
@ -8,15 +6,15 @@ from pydantic import BaseModel, computed_field
class SubItem(BaseModel):
subname: str
sub_description: Optional[str] = None
sub_description: str | None = None
tags: list[str] = []
model_config = {"json_schema_serialization_defaults_required": True}
class Item(BaseModel):
name: str
description: Optional[str] = None
sub: Optional[SubItem] = None
description: str | None = None
sub: SubItem | None = None
model_config = {"json_schema_serialization_defaults_required": True}

View File

@ -1,5 +1,3 @@
from typing import Optional
from fastapi import FastAPI, File
from fastapi.testclient import TestClient
@ -7,7 +5,7 @@ app = FastAPI()
@app.post("/files")
async def upload_files(files: Optional[list[bytes]] = File(None)):
async def upload_files(files: list[bytes] | None = File(None)):
if files is None:
return {"files_count": 0}
return {"files_count": len(files), "sizes": [len(f) for f in files]}

View File

@ -1,5 +1,3 @@
from typing import Optional
from fastapi import FastAPI
from fastapi.params import Param
from fastapi.testclient import TestClient
@ -8,7 +6,7 @@ app = FastAPI()
@app.get("/items/")
def read_items(q: Optional[str] = Param(default=None)): # type: ignore
def read_items(q: str | None = Param(default=None)): # type: ignore
return {"q": q}

View File

@ -1,5 +1,3 @@
from typing import Optional
import pytest
from fastapi import Cookie, FastAPI, Header, Path, Query
from fastapi.testclient import TestClient
@ -10,14 +8,14 @@ app = FastAPI()
@app.get("/hidden_cookie")
async def hidden_cookie(
hidden_cookie: Optional[str] = Cookie(default=None, include_in_schema=False),
hidden_cookie: str | None = Cookie(default=None, include_in_schema=False),
):
return {"hidden_cookie": hidden_cookie}
@app.get("/hidden_header")
async def hidden_header(
hidden_header: Optional[str] = Header(default=None, include_in_schema=False),
hidden_header: str | None = Header(default=None, include_in_schema=False),
):
return {"hidden_header": hidden_header}
@ -29,7 +27,7 @@ async def hidden_path(hidden_path: str = Path(include_in_schema=False)):
@app.get("/hidden_query")
async def hidden_query(
hidden_query: Optional[str] = Query(default=None, include_in_schema=False),
hidden_query: str | None = Query(default=None, include_in_schema=False),
):
return {"hidden_query": hidden_query}

View File

@ -1,6 +1,5 @@
import sys
import warnings
from typing import Union
import pytest
@ -80,7 +79,7 @@ def test_raises_pydantic_v1_model_in_union() -> None:
with pytest.raises(PydanticV1NotSupportedError):
@app.post("/union")
def endpoint(data: Union[dict, ModelV1A]): # pragma: no cover
def endpoint(data: dict | ModelV1A): # pragma: no cover
return data

View File

@ -2,7 +2,6 @@ from __future__ import annotations
import uuid
from dataclasses import dataclass, field
from typing import Union
from dirty_equals import IsUUID
from fastapi import FastAPI
@ -16,8 +15,8 @@ class Item:
name: str
price: float
tags: list[str] = field(default_factory=list)
description: Union[str, None] = None
tax: Union[float, None] = None
description: str | None = None
tax: float | None = None
app = FastAPI()

View File

@ -1,4 +1,4 @@
from typing import Annotated, Union
from typing import Annotated
import pytest
from dirty_equals import IsOneOf, IsPartialDict
@ -55,7 +55,7 @@ def test_required_list_str_schema(path: str):
"path",
["/required-list-str", "/model-required-list-str"],
)
def test_required_list_str_missing(path: str, json: Union[dict, None]):
def test_required_list_str_missing(path: str, json: dict | None):
client = TestClient(app)
response = client.post(path, json=json)
assert response.status_code == 422
@ -132,7 +132,7 @@ def test_required_list_str_alias_schema(path: str):
"path",
["/required-list-alias", "/model-required-list-alias"],
)
def test_required_list_alias_missing(path: str, json: Union[dict, None]):
def test_required_list_alias_missing(path: str, json: dict | None):
client = TestClient(app)
response = client.post(path, json=json)
assert response.status_code == 422
@ -236,7 +236,7 @@ def test_required_list_validation_alias_schema(path: str):
"/model-required-list-validation-alias",
],
)
def test_required_list_validation_alias_missing(path: str, json: Union[dict, None]):
def test_required_list_validation_alias_missing(path: str, json: dict | None):
client = TestClient(app)
response = client.post(path, json=json)
assert response.status_code == 422

View File

@ -1,4 +1,4 @@
from typing import Annotated, Optional
from typing import Annotated
import pytest
from fastapi import Body, FastAPI
@ -15,13 +15,13 @@ app = FastAPI()
@app.post("/optional-list-str", operation_id="optional_list_str")
async def read_optional_list_str(
p: Annotated[Optional[list[str]], Body(embed=True)] = None,
p: Annotated[list[str] | None, Body(embed=True)] = None,
):
return {"p": p}
class BodyModelOptionalListStr(BaseModel):
p: Optional[list[str]] = None
p: list[str] | None = None
@app.post("/model-optional-list-str", operation_id="model_optional_list_str")
@ -103,13 +103,13 @@ def test_optional_list_str(path: str):
@app.post("/optional-list-alias", operation_id="optional_list_alias")
async def read_optional_list_alias(
p: Annotated[Optional[list[str]], Body(embed=True, alias="p_alias")] = None,
p: Annotated[list[str] | None, Body(embed=True, alias="p_alias")] = None,
):
return {"p": p}
class BodyModelOptionalListAlias(BaseModel):
p: Optional[list[str]] = Field(None, alias="p_alias")
p: list[str] | None = Field(None, alias="p_alias")
@app.post("/model-optional-list-alias", operation_id="model_optional_list_alias")
@ -208,14 +208,14 @@ def test_optional_list_alias_by_alias(path: str):
)
def read_optional_list_validation_alias(
p: Annotated[
Optional[list[str]], Body(embed=True, validation_alias="p_val_alias")
list[str] | None, Body(embed=True, validation_alias="p_val_alias")
] = None,
):
return {"p": p}
class BodyModelOptionalListValidationAlias(BaseModel):
p: Optional[list[str]] = Field(None, validation_alias="p_val_alias")
p: list[str] | None = Field(None, validation_alias="p_val_alias")
@app.post(
@ -323,7 +323,7 @@ def test_optional_list_validation_alias_by_validation_alias(path: str):
)
def read_optional_list_alias_and_validation_alias(
p: Annotated[
Optional[list[str]],
list[str] | None,
Body(embed=True, alias="p_alias", validation_alias="p_val_alias"),
] = None,
):
@ -331,9 +331,7 @@ def read_optional_list_alias_and_validation_alias(
class BodyModelOptionalListAliasAndValidationAlias(BaseModel):
p: Optional[list[str]] = Field(
None, alias="p_alias", validation_alias="p_val_alias"
)
p: list[str] | None = Field(None, alias="p_alias", validation_alias="p_val_alias")
@app.post(

View File

@ -1,4 +1,4 @@
from typing import Annotated, Optional
from typing import Annotated
import pytest
from fastapi import Body, FastAPI
@ -14,12 +14,12 @@ app = FastAPI()
@app.post("/optional-str", operation_id="optional_str")
async def read_optional_str(p: Annotated[Optional[str], Body(embed=True)] = None):
async def read_optional_str(p: Annotated[str | None, Body(embed=True)] = None):
return {"p": p}
class BodyModelOptionalStr(BaseModel):
p: Optional[str] = None
p: str | None = None
@app.post("/model-optional-str", operation_id="model_optional_str")
@ -98,13 +98,13 @@ def test_optional_str(path: str):
@app.post("/optional-alias", operation_id="optional_alias")
async def read_optional_alias(
p: Annotated[Optional[str], Body(embed=True, alias="p_alias")] = None,
p: Annotated[str | None, Body(embed=True, alias="p_alias")] = None,
):
return {"p": p}
class BodyModelOptionalAlias(BaseModel):
p: Optional[str] = Field(None, alias="p_alias")
p: str | None = Field(None, alias="p_alias")
@app.post("/model-optional-alias", operation_id="model_optional_alias")
@ -197,15 +197,13 @@ def test_optional_alias_by_alias(path: str):
@app.post("/optional-validation-alias", operation_id="optional_validation_alias")
def read_optional_validation_alias(
p: Annotated[
Optional[str], Body(embed=True, validation_alias="p_val_alias")
] = None,
p: Annotated[str | None, Body(embed=True, validation_alias="p_val_alias")] = None,
):
return {"p": p}
class BodyModelOptionalValidationAlias(BaseModel):
p: Optional[str] = Field(None, validation_alias="p_val_alias")
p: str | None = Field(None, validation_alias="p_val_alias")
@app.post(
@ -309,14 +307,14 @@ def test_optional_validation_alias_by_validation_alias(path: str):
)
def read_optional_alias_and_validation_alias(
p: Annotated[
Optional[str], Body(embed=True, alias="p_alias", validation_alias="p_val_alias")
str | None, Body(embed=True, alias="p_alias", validation_alias="p_val_alias")
] = None,
):
return {"p": p}
class BodyModelOptionalAliasAndValidationAlias(BaseModel):
p: Optional[str] = Field(None, alias="p_alias", validation_alias="p_val_alias")
p: str | None = Field(None, alias="p_alias", validation_alias="p_val_alias")
@app.post(

View File

@ -1,4 +1,4 @@
from typing import Annotated, Any, Union
from typing import Annotated, Any
import pytest
from dirty_equals import IsOneOf
@ -51,7 +51,7 @@ def test_required_str_schema(path: str):
"path",
["/required-str", "/model-required-str"],
)
def test_required_str_missing(path: str, json: Union[dict[str, Any], None]):
def test_required_str_missing(path: str, json: dict[str, Any] | None):
client = TestClient(app)
response = client.post(path, json=json)
assert response.status_code == 422
@ -124,7 +124,7 @@ def test_required_str_alias_schema(path: str):
"path",
["/required-alias", "/model-required-alias"],
)
def test_required_alias_missing(path: str, json: Union[dict[str, Any], None]):
def test_required_alias_missing(path: str, json: dict[str, Any] | None):
client = TestClient(app)
response = client.post(path, json=json)
assert response.status_code == 422
@ -221,9 +221,7 @@ def test_required_validation_alias_schema(path: str):
"/model-required-validation-alias",
],
)
def test_required_validation_alias_missing(
path: str, json: Union[dict[str, Any], None]
):
def test_required_validation_alias_missing(path: str, json: dict[str, Any] | None):
client = TestClient(app)
response = client.post(path, json=json)
assert response.status_code == 422
@ -338,7 +336,7 @@ def test_required_alias_and_validation_alias_schema(path: str):
],
)
def test_required_alias_and_validation_alias_missing(
path: str, json: Union[dict[str, Any], None]
path: str, json: dict[str, Any] | None
):
client = TestClient(app)
response = client.post(path, json=json)

View File

@ -1,4 +1,4 @@
from typing import Annotated, Optional
from typing import Annotated
import pytest
from fastapi import Cookie, FastAPI
@ -13,12 +13,12 @@ app = FastAPI()
@app.get("/optional-str")
async def read_optional_str(p: Annotated[Optional[str], Cookie()] = None):
async def read_optional_str(p: Annotated[str | None, Cookie()] = None):
return {"p": p}
class CookieModelOptionalStr(BaseModel):
p: Optional[str] = None
p: str | None = None
@app.get("/model-optional-str")
@ -75,13 +75,13 @@ def test_optional_str(path: str):
@app.get("/optional-alias")
async def read_optional_alias(
p: Annotated[Optional[str], Cookie(alias="p_alias")] = None,
p: Annotated[str | None, Cookie(alias="p_alias")] = None,
):
return {"p": p}
class CookieModelOptionalAlias(BaseModel):
p: Optional[str] = Field(None, alias="p_alias")
p: str | None = Field(None, alias="p_alias")
@app.get("/model-optional-alias")
@ -153,13 +153,13 @@ def test_optional_alias_by_alias(path: str):
@app.get("/optional-validation-alias")
def read_optional_validation_alias(
p: Annotated[Optional[str], Cookie(validation_alias="p_val_alias")] = None,
p: Annotated[str | None, Cookie(validation_alias="p_val_alias")] = None,
):
return {"p": p}
class CookieModelOptionalValidationAlias(BaseModel):
p: Optional[str] = Field(None, validation_alias="p_val_alias")
p: str | None = Field(None, validation_alias="p_val_alias")
@app.get("/model-optional-validation-alias")
@ -237,14 +237,14 @@ def test_optional_validation_alias_by_validation_alias(path: str):
@app.get("/optional-alias-and-validation-alias")
def read_optional_alias_and_validation_alias(
p: Annotated[
Optional[str], Cookie(alias="p_alias", validation_alias="p_val_alias")
str | None, Cookie(alias="p_alias", validation_alias="p_val_alias")
] = None,
):
return {"p": p}
class CookieModelOptionalAliasAndValidationAlias(BaseModel):
p: Optional[str] = Field(None, alias="p_alias", validation_alias="p_val_alias")
p: str | None = Field(None, alias="p_alias", validation_alias="p_val_alias")
@app.get("/model-optional-alias-and-validation-alias")

View File

@ -1,4 +1,4 @@
from typing import Annotated, Optional
from typing import Annotated
import pytest
from fastapi import FastAPI, File, UploadFile
@ -13,12 +13,12 @@ app = FastAPI()
@app.post("/optional-bytes", operation_id="optional_bytes")
async def read_optional_bytes(p: Annotated[Optional[bytes], File()] = None):
async def read_optional_bytes(p: Annotated[bytes | None, File()] = None):
return {"file_size": len(p) if p else None}
@app.post("/optional-uploadfile", operation_id="optional_uploadfile")
async def read_optional_uploadfile(p: Annotated[Optional[UploadFile], File()] = None):
async def read_optional_uploadfile(p: Annotated[UploadFile | None, File()] = None):
return {"file_size": p.size if p else None}
@ -82,14 +82,14 @@ def test_optional(path: str):
@app.post("/optional-bytes-alias", operation_id="optional_bytes_alias")
async def read_optional_bytes_alias(
p: Annotated[Optional[bytes], File(alias="p_alias")] = None,
p: Annotated[bytes | None, File(alias="p_alias")] = None,
):
return {"file_size": len(p) if p else None}
@app.post("/optional-uploadfile-alias", operation_id="optional_uploadfile_alias")
async def read_optional_uploadfile_alias(
p: Annotated[Optional[UploadFile], File(alias="p_alias")] = None,
p: Annotated[UploadFile | None, File(alias="p_alias")] = None,
):
return {"file_size": p.size if p else None}
@ -170,7 +170,7 @@ def test_optional_alias_by_alias(path: str):
"/optional-bytes-validation-alias", operation_id="optional_bytes_validation_alias"
)
def read_optional_bytes_validation_alias(
p: Annotated[Optional[bytes], File(validation_alias="p_val_alias")] = None,
p: Annotated[bytes | None, File(validation_alias="p_val_alias")] = None,
):
return {"file_size": len(p) if p else None}
@ -180,7 +180,7 @@ def read_optional_bytes_validation_alias(
operation_id="optional_uploadfile_validation_alias",
)
def read_optional_uploadfile_validation_alias(
p: Annotated[Optional[UploadFile], File(validation_alias="p_val_alias")] = None,
p: Annotated[UploadFile | None, File(validation_alias="p_val_alias")] = None,
):
return {"file_size": p.size if p else None}
@ -263,7 +263,7 @@ def test_optional_validation_alias_by_validation_alias(path: str):
)
def read_optional_bytes_alias_and_validation_alias(
p: Annotated[
Optional[bytes], File(alias="p_alias", validation_alias="p_val_alias")
bytes | None, File(alias="p_alias", validation_alias="p_val_alias")
] = None,
):
return {"file_size": len(p) if p else None}
@ -275,7 +275,7 @@ def read_optional_bytes_alias_and_validation_alias(
)
def read_optional_uploadfile_alias_and_validation_alias(
p: Annotated[
Optional[UploadFile], File(alias="p_alias", validation_alias="p_val_alias")
UploadFile | None, File(alias="p_alias", validation_alias="p_val_alias")
] = None,
):
return {"file_size": p.size if p else None}

View File

@ -1,4 +1,4 @@
from typing import Annotated, Optional
from typing import Annotated
import pytest
from fastapi import FastAPI, File, UploadFile
@ -13,13 +13,13 @@ app = FastAPI()
@app.post("/optional-list-bytes")
async def read_optional_list_bytes(p: Annotated[Optional[list[bytes]], File()] = None):
async def read_optional_list_bytes(p: Annotated[list[bytes] | None, File()] = None):
return {"file_size": [len(file) for file in p] if p else None}
@app.post("/optional-list-uploadfile")
async def read_optional_list_uploadfile(
p: Annotated[Optional[list[UploadFile]], File()] = None,
p: Annotated[list[UploadFile] | None, File()] = None,
):
return {"file_size": [file.size for file in p] if p else None}
@ -87,14 +87,14 @@ def test_optional_list(path: str):
@app.post("/optional-list-bytes-alias")
async def read_optional_list_bytes_alias(
p: Annotated[Optional[list[bytes]], File(alias="p_alias")] = None,
p: Annotated[list[bytes] | None, File(alias="p_alias")] = None,
):
return {"file_size": [len(file) for file in p] if p else None}
@app.post("/optional-list-uploadfile-alias")
async def read_optional_list_uploadfile_alias(
p: Annotated[Optional[list[UploadFile]], File(alias="p_alias")] = None,
p: Annotated[list[UploadFile] | None, File(alias="p_alias")] = None,
):
return {"file_size": [file.size for file in p] if p else None}
@ -176,16 +176,14 @@ def test_optional_list_alias_by_alias(path: str):
@app.post("/optional-list-bytes-validation-alias")
def read_optional_list_bytes_validation_alias(
p: Annotated[Optional[list[bytes]], File(validation_alias="p_val_alias")] = None,
p: Annotated[list[bytes] | None, File(validation_alias="p_val_alias")] = None,
):
return {"file_size": [len(file) for file in p] if p else None}
@app.post("/optional-list-uploadfile-validation-alias")
def read_optional_list_uploadfile_validation_alias(
p: Annotated[
Optional[list[UploadFile]], File(validation_alias="p_val_alias")
] = None,
p: Annotated[list[UploadFile] | None, File(validation_alias="p_val_alias")] = None,
):
return {"file_size": [file.size for file in p] if p else None}
@ -270,7 +268,7 @@ def test_optional_validation_alias_by_validation_alias(path: str):
@app.post("/optional-list-bytes-alias-and-validation-alias")
def read_optional_list_bytes_alias_and_validation_alias(
p: Annotated[
Optional[list[bytes]], File(alias="p_alias", validation_alias="p_val_alias")
list[bytes] | None, File(alias="p_alias", validation_alias="p_val_alias")
] = None,
):
return {"file_size": [len(file) for file in p] if p else None}
@ -279,7 +277,7 @@ def read_optional_list_bytes_alias_and_validation_alias(
@app.post("/optional-list-uploadfile-alias-and-validation-alias")
def read_optional_list_uploadfile_alias_and_validation_alias(
p: Annotated[
Optional[list[UploadFile]],
list[UploadFile] | None,
File(alias="p_alias", validation_alias="p_val_alias"),
] = None,
):

View File

@ -1,4 +1,4 @@
from typing import Annotated, Optional
from typing import Annotated
import pytest
from fastapi import FastAPI, Form
@ -15,13 +15,13 @@ app = FastAPI()
@app.post("/optional-list-str", operation_id="optional_list_str")
async def read_optional_list_str(
p: Annotated[Optional[list[str]], Form()] = None,
p: Annotated[list[str] | None, Form()] = None,
):
return {"p": p}
class FormModelOptionalListStr(BaseModel):
p: Optional[list[str]] = None
p: list[str] | None = None
@app.post("/model-optional-list-str", operation_id="model_optional_list_str")
@ -80,13 +80,13 @@ def test_optional_list_str(path: str):
@app.post("/optional-list-alias", operation_id="optional_list_alias")
async def read_optional_list_alias(
p: Annotated[Optional[list[str]], Form(alias="p_alias")] = None,
p: Annotated[list[str] | None, Form(alias="p_alias")] = None,
):
return {"p": p}
class FormModelOptionalListAlias(BaseModel):
p: Optional[list[str]] = Field(None, alias="p_alias")
p: list[str] | None = Field(None, alias="p_alias")
@app.post("/model-optional-list-alias", operation_id="model_optional_list_alias")
@ -163,13 +163,13 @@ def test_optional_list_alias_by_alias(path: str):
"/optional-list-validation-alias", operation_id="optional_list_validation_alias"
)
def read_optional_list_validation_alias(
p: Annotated[Optional[list[str]], Form(validation_alias="p_val_alias")] = None,
p: Annotated[list[str] | None, Form(validation_alias="p_val_alias")] = None,
):
return {"p": p}
class FormModelOptionalListValidationAlias(BaseModel):
p: Optional[list[str]] = Field(None, validation_alias="p_val_alias")
p: list[str] | None = Field(None, validation_alias="p_val_alias")
@app.post(
@ -251,16 +251,14 @@ def test_optional_list_validation_alias_by_validation_alias(path: str):
)
def read_optional_list_alias_and_validation_alias(
p: Annotated[
Optional[list[str]], Form(alias="p_alias", validation_alias="p_val_alias")
list[str] | None, Form(alias="p_alias", validation_alias="p_val_alias")
] = None,
):
return {"p": p}
class FormModelOptionalListAliasAndValidationAlias(BaseModel):
p: Optional[list[str]] = Field(
None, alias="p_alias", validation_alias="p_val_alias"
)
p: list[str] | None = Field(None, alias="p_alias", validation_alias="p_val_alias")
@app.post(

View File

@ -1,4 +1,4 @@
from typing import Annotated, Optional
from typing import Annotated
import pytest
from fastapi import FastAPI, Form
@ -14,12 +14,12 @@ app = FastAPI()
@app.post("/optional-str", operation_id="optional_str")
async def read_optional_str(p: Annotated[Optional[str], Form()] = None):
async def read_optional_str(p: Annotated[str | None, Form()] = None):
return {"p": p}
class FormModelOptionalStr(BaseModel):
p: Optional[str] = None
p: str | None = None
@app.post("/model-optional-str", operation_id="model_optional_str")
@ -75,13 +75,13 @@ def test_optional_str(path: str):
@app.post("/optional-alias", operation_id="optional_alias")
async def read_optional_alias(
p: Annotated[Optional[str], Form(alias="p_alias")] = None,
p: Annotated[str | None, Form(alias="p_alias")] = None,
):
return {"p": p}
class FormModelOptionalAlias(BaseModel):
p: Optional[str] = Field(None, alias="p_alias")
p: str | None = Field(None, alias="p_alias")
@app.post("/model-optional-alias", operation_id="model_optional_alias")
@ -151,13 +151,13 @@ def test_optional_alias_by_alias(path: str):
@app.post("/optional-validation-alias", operation_id="optional_validation_alias")
def read_optional_validation_alias(
p: Annotated[Optional[str], Form(validation_alias="p_val_alias")] = None,
p: Annotated[str | None, Form(validation_alias="p_val_alias")] = None,
):
return {"p": p}
class FormModelOptionalValidationAlias(BaseModel):
p: Optional[str] = Field(None, validation_alias="p_val_alias")
p: str | None = Field(None, validation_alias="p_val_alias")
@app.post(
@ -238,14 +238,14 @@ def test_optional_validation_alias_by_validation_alias(path: str):
)
def read_optional_alias_and_validation_alias(
p: Annotated[
Optional[str], Form(alias="p_alias", validation_alias="p_val_alias")
str | None, Form(alias="p_alias", validation_alias="p_val_alias")
] = None,
):
return {"p": p}
class FormModelOptionalAliasAndValidationAlias(BaseModel):
p: Optional[str] = Field(None, alias="p_alias", validation_alias="p_val_alias")
p: str | None = Field(None, alias="p_alias", validation_alias="p_val_alias")
@app.post(

View File

@ -1,4 +1,4 @@
from typing import Annotated, Optional
from typing import Annotated
import pytest
from fastapi import FastAPI, Header
@ -14,13 +14,13 @@ app = FastAPI()
@app.get("/optional-list-str")
async def read_optional_list_str(
p: Annotated[Optional[list[str]], Header()] = None,
p: Annotated[list[str] | None, Header()] = None,
):
return {"p": p}
class HeaderModelOptionalListStr(BaseModel):
p: Optional[list[str]] = None
p: list[str] | None = None
@app.get("/model-optional-list-str")
@ -81,13 +81,13 @@ def test_optional_list_str(path: str):
@app.get("/optional-list-alias")
async def read_optional_list_alias(
p: Annotated[Optional[list[str]], Header(alias="p_alias")] = None,
p: Annotated[list[str] | None, Header(alias="p_alias")] = None,
):
return {"p": p}
class HeaderModelOptionalListAlias(BaseModel):
p: Optional[list[str]] = Field(None, alias="p_alias")
p: list[str] | None = Field(None, alias="p_alias")
@app.get("/model-optional-list-alias")
@ -162,13 +162,13 @@ def test_optional_list_alias_by_alias(path: str):
@app.get("/optional-list-validation-alias")
def read_optional_list_validation_alias(
p: Annotated[Optional[list[str]], Header(validation_alias="p_val_alias")] = None,
p: Annotated[list[str] | None, Header(validation_alias="p_val_alias")] = None,
):
return {"p": p}
class HeaderModelOptionalListValidationAlias(BaseModel):
p: Optional[list[str]] = Field(None, validation_alias="p_val_alias")
p: list[str] | None = Field(None, validation_alias="p_val_alias")
@app.get("/model-optional-list-validation-alias")
@ -246,16 +246,14 @@ def test_optional_list_validation_alias_by_validation_alias(path: str):
@app.get("/optional-list-alias-and-validation-alias")
def read_optional_list_alias_and_validation_alias(
p: Annotated[
Optional[list[str]], Header(alias="p_alias", validation_alias="p_val_alias")
list[str] | None, Header(alias="p_alias", validation_alias="p_val_alias")
] = None,
):
return {"p": p}
class HeaderModelOptionalListAliasAndValidationAlias(BaseModel):
p: Optional[list[str]] = Field(
None, alias="p_alias", validation_alias="p_val_alias"
)
p: list[str] | None = Field(None, alias="p_alias", validation_alias="p_val_alias")
@app.get("/model-optional-list-alias-and-validation-alias")

View File

@ -1,4 +1,4 @@
from typing import Annotated, Optional
from typing import Annotated
import pytest
from fastapi import FastAPI, Header
@ -13,12 +13,12 @@ app = FastAPI()
@app.get("/optional-str")
async def read_optional_str(p: Annotated[Optional[str], Header()] = None):
async def read_optional_str(p: Annotated[str | None, Header()] = None):
return {"p": p}
class HeaderModelOptionalStr(BaseModel):
p: Optional[str] = None
p: str | None = None
@app.get("/model-optional-str")
@ -74,13 +74,13 @@ def test_optional_str(path: str):
@app.get("/optional-alias")
async def read_optional_alias(
p: Annotated[Optional[str], Header(alias="p_alias")] = None,
p: Annotated[str | None, Header(alias="p_alias")] = None,
):
return {"p": p}
class HeaderModelOptionalAlias(BaseModel):
p: Optional[str] = Field(None, alias="p_alias")
p: str | None = Field(None, alias="p_alias")
@app.get("/model-optional-alias")
@ -150,13 +150,13 @@ def test_optional_alias_by_alias(path: str):
@app.get("/optional-validation-alias")
def read_optional_validation_alias(
p: Annotated[Optional[str], Header(validation_alias="p_val_alias")] = None,
p: Annotated[str | None, Header(validation_alias="p_val_alias")] = None,
):
return {"p": p}
class HeaderModelOptionalValidationAlias(BaseModel):
p: Optional[str] = Field(None, validation_alias="p_val_alias")
p: str | None = Field(None, validation_alias="p_val_alias")
@app.get("/model-optional-validation-alias")
@ -232,14 +232,14 @@ def test_optional_validation_alias_by_validation_alias(path: str):
@app.get("/optional-alias-and-validation-alias")
def read_optional_alias_and_validation_alias(
p: Annotated[
Optional[str], Header(alias="p_alias", validation_alias="p_val_alias")
str | None, Header(alias="p_alias", validation_alias="p_val_alias")
] = None,
):
return {"p": p}
class HeaderModelOptionalAliasAndValidationAlias(BaseModel):
p: Optional[str] = Field(None, alias="p_alias", validation_alias="p_val_alias")
p: str | None = Field(None, alias="p_alias", validation_alias="p_val_alias")
@app.get("/model-optional-alias-and-validation-alias")

View File

@ -1,4 +1,4 @@
from typing import Annotated, Optional
from typing import Annotated
import pytest
from fastapi import FastAPI, Query
@ -14,13 +14,13 @@ app = FastAPI()
@app.get("/optional-list-str")
async def read_optional_list_str(
p: Annotated[Optional[list[str]], Query()] = None,
p: Annotated[list[str] | None, Query()] = None,
):
return {"p": p}
class QueryModelOptionalListStr(BaseModel):
p: Optional[list[str]] = None
p: list[str] | None = None
@app.get("/model-optional-list-str")
@ -81,13 +81,13 @@ def test_optional_list_str(path: str):
@app.get("/optional-list-alias")
async def read_optional_list_alias(
p: Annotated[Optional[list[str]], Query(alias="p_alias")] = None,
p: Annotated[list[str] | None, Query(alias="p_alias")] = None,
):
return {"p": p}
class QueryModelOptionalListAlias(BaseModel):
p: Optional[list[str]] = Field(None, alias="p_alias")
p: list[str] | None = Field(None, alias="p_alias")
@app.get("/model-optional-list-alias")
@ -162,13 +162,13 @@ def test_optional_list_alias_by_alias(path: str):
@app.get("/optional-list-validation-alias")
def read_optional_list_validation_alias(
p: Annotated[Optional[list[str]], Query(validation_alias="p_val_alias")] = None,
p: Annotated[list[str] | None, Query(validation_alias="p_val_alias")] = None,
):
return {"p": p}
class QueryModelOptionalListValidationAlias(BaseModel):
p: Optional[list[str]] = Field(None, validation_alias="p_val_alias")
p: list[str] | None = Field(None, validation_alias="p_val_alias")
@app.get("/model-optional-list-validation-alias")
@ -244,16 +244,14 @@ def test_optional_list_validation_alias_by_validation_alias(path: str):
@app.get("/optional-list-alias-and-validation-alias")
def read_optional_list_alias_and_validation_alias(
p: Annotated[
Optional[list[str]], Query(alias="p_alias", validation_alias="p_val_alias")
list[str] | None, Query(alias="p_alias", validation_alias="p_val_alias")
] = None,
):
return {"p": p}
class QueryModelOptionalListAliasAndValidationAlias(BaseModel):
p: Optional[list[str]] = Field(
None, alias="p_alias", validation_alias="p_val_alias"
)
p: list[str] | None = Field(None, alias="p_alias", validation_alias="p_val_alias")
@app.get("/model-optional-list-alias-and-validation-alias")

View File

@ -1,4 +1,4 @@
from typing import Annotated, Optional
from typing import Annotated
import pytest
from fastapi import FastAPI, Query
@ -13,12 +13,12 @@ app = FastAPI()
@app.get("/optional-str")
async def read_optional_str(p: Optional[str] = None):
async def read_optional_str(p: str | None = None):
return {"p": p}
class QueryModelOptionalStr(BaseModel):
p: Optional[str] = None
p: str | None = None
@app.get("/model-optional-str")
@ -74,13 +74,13 @@ def test_optional_str(path: str):
@app.get("/optional-alias")
async def read_optional_alias(
p: Annotated[Optional[str], Query(alias="p_alias")] = None,
p: Annotated[str | None, Query(alias="p_alias")] = None,
):
return {"p": p}
class QueryModelOptionalAlias(BaseModel):
p: Optional[str] = Field(None, alias="p_alias")
p: str | None = Field(None, alias="p_alias")
@app.get("/model-optional-alias")
@ -150,13 +150,13 @@ def test_optional_alias_by_alias(path: str):
@app.get("/optional-validation-alias")
def read_optional_validation_alias(
p: Annotated[Optional[str], Query(validation_alias="p_val_alias")] = None,
p: Annotated[str | None, Query(validation_alias="p_val_alias")] = None,
):
return {"p": p}
class QueryModelOptionalValidationAlias(BaseModel):
p: Optional[str] = Field(None, validation_alias="p_val_alias")
p: str | None = Field(None, validation_alias="p_val_alias")
@app.get("/model-optional-validation-alias")
@ -232,14 +232,14 @@ def test_optional_validation_alias_by_validation_alias(path: str):
@app.get("/optional-alias-and-validation-alias")
def read_optional_alias_and_validation_alias(
p: Annotated[
Optional[str], Query(alias="p_alias", validation_alias="p_val_alias")
str | None, Query(alias="p_alias", validation_alias="p_val_alias")
] = None,
):
return {"p": p}
class QueryModelOptionalAliasAndValidationAlias(BaseModel):
p: Optional[str] = Field(None, alias="p_alias", validation_alias="p_val_alias")
p: str | None = Field(None, alias="p_alias", validation_alias="p_val_alias")
@app.get("/model-optional-alias-and-validation-alias")

View File

@ -1,5 +1,3 @@
from typing import Union
from fastapi import Body, FastAPI, Query
from fastapi.testclient import TestClient
@ -7,17 +5,17 @@ app = FastAPI()
@app.get("/query")
def read_query(q: Union[str, None]):
def read_query(q: str | None):
return q
@app.get("/explicit-query")
def read_explicit_query(q: Union[str, None] = Query()):
def read_explicit_query(q: str | None = Query()):
return q
@app.post("/body-embed")
def send_body_embed(b: Union[str, None] = Body(embed=True)):
def send_body_embed(b: str | None = Body(embed=True)):
return b

View File

@ -1,5 +1,3 @@
from typing import Union
import pytest
from fastapi import FastAPI
from fastapi.exceptions import FastAPIError, ResponseValidationError
@ -216,7 +214,7 @@ def no_response_model_annotation_forward_ref_list_of_model() -> "list[User]":
@app.get(
"/response_model_union-no_annotation-return_model1",
response_model=Union[User, Item],
response_model=User | Item,
)
def response_model_union_no_annotation_return_model1():
return DBUser(name="John", surname="Doe", password_hash="secret")
@ -224,19 +222,19 @@ def response_model_union_no_annotation_return_model1():
@app.get(
"/response_model_union-no_annotation-return_model2",
response_model=Union[User, Item],
response_model=User | Item,
)
def response_model_union_no_annotation_return_model2():
return Item(name="Foo", price=42.0)
@app.get("/no_response_model-annotation_union-return_model1")
def no_response_model_annotation_union_return_model1() -> Union[User, Item]:
def no_response_model_annotation_union_return_model1() -> User | Item:
return DBUser(name="John", surname="Doe", password_hash="secret")
@app.get("/no_response_model-annotation_union-return_model2")
def no_response_model_annotation_union_return_model2() -> Union[User, Item]:
def no_response_model_annotation_union_return_model2() -> User | Item:
return Item(name="Foo", price=42.0)
@ -503,7 +501,7 @@ def test_invalid_response_model_field():
with pytest.raises(FastAPIError) as e:
@app.get("/")
def read_root() -> Union[Response, None]:
def read_root() -> Response | None:
return Response(content="Foo") # pragma: no cover
assert "valid Pydantic field type" in e.value.args[0]

View File

@ -1,6 +1,5 @@
from collections.abc import AsyncGenerator
from contextlib import asynccontextmanager
from typing import Union
import pytest
from fastapi import APIRouter, FastAPI, Request
@ -176,7 +175,7 @@ def test_router_nested_lifespan_state_overriding_by_parent() -> None:
@asynccontextmanager
async def lifespan(
app: FastAPI,
) -> AsyncGenerator[dict[str, Union[str, bool]], None]:
) -> AsyncGenerator[dict[str, str | bool], None]:
yield {
"app_specific": True,
"overridden": "app",
@ -185,7 +184,7 @@ def test_router_nested_lifespan_state_overriding_by_parent() -> None:
@asynccontextmanager
async def router_lifespan(
app: FastAPI,
) -> AsyncGenerator[dict[str, Union[str, bool]], None]:
) -> AsyncGenerator[dict[str, str | bool], None]:
yield {
"router_specific": True,
"overridden": "router", # should override parent

View File

@ -1,5 +1,3 @@
from typing import Union
import pytest
from fastapi import Body, Cookie, FastAPI, Header, Path, Query
from fastapi.exceptions import FastAPIDeprecationWarning
@ -117,7 +115,7 @@ def create_app():
@app.get("/query_example/")
def query_example(
data: Union[str, None] = Query(
data: str | None = Query(
default=None,
example="query1",
),
@ -126,7 +124,7 @@ def create_app():
@app.get("/query_examples/")
def query_examples(
data: Union[str, None] = Query(
data: str | None = Query(
default=None,
examples=["query1", "query2"],
),
@ -137,7 +135,7 @@ def create_app():
@app.get("/query_example_examples/")
def query_example_examples(
data: Union[str, None] = Query(
data: str | None = Query(
default=None,
example="query_overridden",
examples=["query1", "query2"],
@ -149,7 +147,7 @@ def create_app():
@app.get("/header_example/")
def header_example(
data: Union[str, None] = Header(
data: str | None = Header(
default=None,
example="header1",
),
@ -158,7 +156,7 @@ def create_app():
@app.get("/header_examples/")
def header_examples(
data: Union[str, None] = Header(
data: str | None = Header(
default=None,
examples=[
"header1",
@ -172,7 +170,7 @@ def create_app():
@app.get("/header_example_examples/")
def header_example_examples(
data: Union[str, None] = Header(
data: str | None = Header(
default=None,
example="header_overridden",
examples=["header1", "header2"],
@ -184,7 +182,7 @@ def create_app():
@app.get("/cookie_example/")
def cookie_example(
data: Union[str, None] = Cookie(
data: str | None = Cookie(
default=None,
example="cookie1",
),
@ -193,7 +191,7 @@ def create_app():
@app.get("/cookie_examples/")
def cookie_examples(
data: Union[str, None] = Cookie(
data: str | None = Cookie(
default=None,
examples=["cookie1", "cookie2"],
),
@ -204,7 +202,7 @@ def create_app():
@app.get("/cookie_example_examples/")
def cookie_example_examples(
data: Union[str, None] = Cookie(
data: str | None = Cookie(
default=None,
example="cookie_overridden",
examples=["cookie1", "cookie2"],

View File

@ -1,5 +1,3 @@
from typing import Optional
from fastapi import Depends, FastAPI, Security
from fastapi.security import APIKeyCookie
from fastapi.testclient import TestClient
@ -15,7 +13,7 @@ class User(BaseModel):
username: str
def get_current_user(oauth_header: Optional[str] = Security(api_key)):
def get_current_user(oauth_header: str | None = Security(api_key)):
if oauth_header is None:
return None
user = User(username=oauth_header)

View File

@ -1,5 +1,3 @@
from typing import Optional
from fastapi import Depends, FastAPI, Security
from fastapi.security import APIKeyHeader
from fastapi.testclient import TestClient
@ -15,7 +13,7 @@ class User(BaseModel):
username: str
def get_current_user(oauth_header: Optional[str] = Security(api_key)):
def get_current_user(oauth_header: str | None = Security(api_key)):
if oauth_header is None:
return None
user = User(username=oauth_header)
@ -23,7 +21,7 @@ def get_current_user(oauth_header: Optional[str] = Security(api_key)):
@app.get("/users/me")
def read_current_user(current_user: Optional[User] = Depends(get_current_user)):
def read_current_user(current_user: User | None = Depends(get_current_user)):
if current_user is None:
return {"msg": "Create an account first"}
return current_user

View File

@ -1,5 +1,3 @@
from typing import Optional
from fastapi import Depends, FastAPI, Security
from fastapi.security import APIKeyQuery
from fastapi.testclient import TestClient
@ -15,7 +13,7 @@ class User(BaseModel):
username: str
def get_current_user(oauth_header: Optional[str] = Security(api_key)):
def get_current_user(oauth_header: str | None = Security(api_key)):
if oauth_header is None:
return None
user = User(username=oauth_header)
@ -23,7 +21,7 @@ def get_current_user(oauth_header: Optional[str] = Security(api_key)):
@app.get("/users/me")
def read_current_user(current_user: Optional[User] = Depends(get_current_user)):
def read_current_user(current_user: User | None = Depends(get_current_user)):
if current_user is None:
return {"msg": "Create an account first"}
return current_user

View File

@ -1,5 +1,3 @@
from typing import Optional
from fastapi import FastAPI, Security
from fastapi.security.http import HTTPAuthorizationCredentials, HTTPBase
from fastapi.testclient import TestClient
@ -12,7 +10,7 @@ security = HTTPBase(scheme="Other", auto_error=False)
@app.get("/users/me")
def read_current_user(
credentials: Optional[HTTPAuthorizationCredentials] = Security(security),
credentials: HTTPAuthorizationCredentials | None = Security(security),
):
if credentials is None:
return {"msg": "Create an account first"}

View File

@ -1,5 +1,4 @@
from base64 import b64encode
from typing import Optional
from fastapi import FastAPI, Security
from fastapi.security import HTTPBasic, HTTPBasicCredentials
@ -12,7 +11,7 @@ security = HTTPBasic(auto_error=False)
@app.get("/users/me")
def read_current_user(credentials: Optional[HTTPBasicCredentials] = Security(security)):
def read_current_user(credentials: HTTPBasicCredentials | None = Security(security)):
if credentials is None:
return {"msg": "Create an account first"}
return {"username": credentials.username, "password": credentials.password}

View File

@ -1,5 +1,3 @@
from typing import Optional
from fastapi import FastAPI, Security
from fastapi.security import HTTPAuthorizationCredentials, HTTPBearer
from fastapi.testclient import TestClient
@ -12,7 +10,7 @@ security = HTTPBearer(auto_error=False)
@app.get("/users/me")
def read_current_user(
credentials: Optional[HTTPAuthorizationCredentials] = Security(security),
credentials: HTTPAuthorizationCredentials | None = Security(security),
):
if credentials is None:
return {"msg": "Create an account first"}

View File

@ -1,5 +1,3 @@
from typing import Optional
from fastapi import FastAPI, Security
from fastapi.security import HTTPAuthorizationCredentials, HTTPDigest
from fastapi.testclient import TestClient
@ -12,7 +10,7 @@ security = HTTPDigest(auto_error=False)
@app.get("/users/me")
def read_current_user(
credentials: Optional[HTTPAuthorizationCredentials] = Security(security),
credentials: HTTPAuthorizationCredentials | None = Security(security),
):
if credentials is None:
return {"msg": "Create an account first"}

View File

@ -1,5 +1,3 @@
from typing import Optional
from fastapi import FastAPI, Security
from fastapi.security import OAuth2AuthorizationCodeBearer
from fastapi.testclient import TestClient
@ -13,7 +11,7 @@ oauth2_scheme = OAuth2AuthorizationCodeBearer(
@app.get("/items/")
async def read_items(token: Optional[str] = Security(oauth2_scheme)):
async def read_items(token: str | None = Security(oauth2_scheme)):
return {"token": token}

View File

@ -1,5 +1,3 @@
from typing import Optional
from fastapi import FastAPI, Security
from fastapi.security import OAuth2AuthorizationCodeBearer
from fastapi.testclient import TestClient
@ -16,7 +14,7 @@ oauth2_scheme = OAuth2AuthorizationCodeBearer(
@app.get("/items/")
async def read_items(token: Optional[str] = Security(oauth2_scheme)):
async def read_items(token: str | None = Security(oauth2_scheme)):
return {"token": token}

View File

@ -1,6 +1,6 @@
# Ref: https://github.com/fastapi/fastapi/issues/14454
from typing import Annotated, Optional
from typing import Annotated
from fastapi import APIRouter, Depends, FastAPI, Security
from fastapi.security import OAuth2AuthorizationCodeBearer
@ -46,13 +46,13 @@ router = APIRouter(dependencies=[Security(oauth2_scheme, scopes=["read"])])
@router.get("/items/")
async def read_items(token: Optional[str] = Depends(oauth2_scheme)):
async def read_items(token: str | None = Depends(oauth2_scheme)):
return {"token": token}
@router.post("/items/")
async def create_item(
token: Optional[str] = Security(oauth2_scheme, scopes=["read", "write"]),
token: str | None = Security(oauth2_scheme, scopes=["read", "write"]),
):
return {"token": token}

View File

@ -1,5 +1,3 @@
from typing import Optional
import pytest
from fastapi import Depends, FastAPI, Security
from fastapi.security import OAuth2, OAuth2PasswordRequestFormStrict
@ -24,7 +22,7 @@ class User(BaseModel):
username: str
def get_current_user(oauth_header: Optional[str] = Security(reusable_oauth2)):
def get_current_user(oauth_header: str | None = Security(reusable_oauth2)):
if oauth_header is None:
return None
user = User(username=oauth_header)
@ -37,7 +35,7 @@ def login(form_data: OAuth2PasswordRequestFormStrict = Depends()):
@app.get("/users/me")
def read_users_me(current_user: Optional[User] = Depends(get_current_user)):
def read_users_me(current_user: User | None = Depends(get_current_user)):
if current_user is None:
return {"msg": "Create an account first"}
return current_user

View File

@ -1,5 +1,3 @@
from typing import Optional
import pytest
from fastapi import Depends, FastAPI, Security
from fastapi.security import OAuth2, OAuth2PasswordRequestFormStrict
@ -25,7 +23,7 @@ class User(BaseModel):
username: str
def get_current_user(oauth_header: Optional[str] = Security(reusable_oauth2)):
def get_current_user(oauth_header: str | None = Security(reusable_oauth2)):
if oauth_header is None:
return None
user = User(username=oauth_header)
@ -38,7 +36,7 @@ def login(form_data: OAuth2PasswordRequestFormStrict = Depends()):
@app.get("/users/me")
def read_users_me(current_user: Optional[User] = Depends(get_current_user)):
def read_users_me(current_user: User | None = Depends(get_current_user)):
if current_user is None:
return {"msg": "Create an account first"}
return current_user

View File

@ -1,5 +1,3 @@
from typing import Optional
from fastapi import FastAPI, Security
from fastapi.security import OAuth2PasswordBearer
from fastapi.testclient import TestClient
@ -11,7 +9,7 @@ oauth2_scheme = OAuth2PasswordBearer(tokenUrl="/token", auto_error=False)
@app.get("/items/")
async def read_items(token: Optional[str] = Security(oauth2_scheme)):
async def read_items(token: str | None = Security(oauth2_scheme)):
if token is None:
return {"msg": "Create an account first"}
return {"token": token}

View File

@ -1,5 +1,3 @@
from typing import Optional
from fastapi import FastAPI, Security
from fastapi.security import OAuth2PasswordBearer
from fastapi.testclient import TestClient
@ -15,7 +13,7 @@ oauth2_scheme = OAuth2PasswordBearer(
@app.get("/items/")
async def read_items(token: Optional[str] = Security(oauth2_scheme)):
async def read_items(token: str | None = Security(oauth2_scheme)):
if token is None:
return {"msg": "Create an account first"}
return {"token": token}

View File

@ -1,5 +1,3 @@
from typing import Optional
from fastapi import Depends, FastAPI, Security
from fastapi.security.open_id_connect_url import OpenIdConnect
from fastapi.testclient import TestClient
@ -15,7 +13,7 @@ class User(BaseModel):
username: str
def get_current_user(oauth_header: Optional[str] = Security(oid)):
def get_current_user(oauth_header: str | None = Security(oid)):
if oauth_header is None:
return None
user = User(username=oauth_header)
@ -23,7 +21,7 @@ def get_current_user(oauth_header: Optional[str] = Security(oid)):
@app.get("/users/me")
def read_current_user(current_user: Optional[User] = Depends(get_current_user)):
def read_current_user(current_user: User | None = Depends(get_current_user)):
if current_user is None:
return {"msg": "Create an account first"}
return current_user

View File

@ -1,5 +1,3 @@
from typing import Optional
from fastapi import FastAPI
from fastapi.testclient import TestClient
from pydantic import BaseModel
@ -9,8 +7,8 @@ app = FastAPI()
class Item(BaseModel):
name: str
price: Optional[float] = None
owner_ids: Optional[list[int]] = None
price: float | None = None
owner_ids: list[int] | None = None
@app.get("/items/valid", response_model=Item)

View File

@ -1,6 +1,5 @@
from dataclasses import dataclass
from datetime import datetime
from typing import Optional
from fastapi import FastAPI
from fastapi.testclient import TestClient
@ -12,8 +11,8 @@ app = FastAPI()
class Item:
name: str
date: datetime
price: Optional[float] = None
owner_ids: Optional[list[int]] = None
price: float | None = None
owner_ids: list[int] | None = None
@app.get("/items/valid", response_model=Item)

View File

@ -1,5 +1,3 @@
from typing import Optional
from fastapi import FastAPI
from pydantic import BaseModel, Field
from starlette.testclient import TestClient
@ -9,8 +7,8 @@ app = FastAPI()
class Item(BaseModel):
name: str = Field(alias="aliased_name")
price: Optional[float] = None
owner_ids: Optional[list[int]] = None
price: float | None = None
owner_ids: list[int] | None = None
@app.get("/items/valid", response_model=Item)

View File

@ -1,5 +1,3 @@
from typing import Optional
from fastapi import FastAPI
from fastapi.testclient import TestClient
from pydantic import BaseModel
@ -8,23 +6,23 @@ app = FastAPI()
class SubModel(BaseModel):
a: Optional[str] = "foo"
a: str | None = "foo"
class Model(BaseModel):
x: Optional[int] = None
x: int | None = None
sub: SubModel
class ModelSubclass(Model):
y: int
z: int = 0
w: Optional[int] = None
w: int | None = None
class ModelDefaults(BaseModel):
w: Optional[str] = None
x: Optional[str] = None
w: str | None = None
x: str | None = None
y: str = "y"
z: str = "z"

View File

@ -1,5 +1,3 @@
from typing import Optional
from fastapi import APIRouter, FastAPI
from fastapi.testclient import TestClient
from inline_snapshot import snapshot
@ -10,7 +8,7 @@ app = FastAPI()
class Invoice(BaseModel):
id: str
title: Optional[str] = None
title: str | None = None
customer: str
total: float
@ -51,7 +49,7 @@ subrouter = APIRouter()
@subrouter.post("/invoices/", callbacks=invoices_callback_router.routes)
def create_invoice(invoice: Invoice, callback_url: Optional[HttpUrl] = None):
def create_invoice(invoice: Invoice, callback_url: HttpUrl | None = None):
"""
Create an invoice.

View File

@ -1,5 +1,3 @@
from typing import Optional, Union
from fastapi import FastAPI
from fastapi.testclient import TestClient
from inline_snapshot import snapshot
@ -9,7 +7,7 @@ app = FastAPI()
class Item(BaseModel):
name: Optional[str] = None
name: str | None = None
class OtherItem(BaseModel):
@ -17,7 +15,7 @@ class OtherItem(BaseModel):
@app.post("/items/")
def save_union_body(item: Union[OtherItem, Item]):
def save_union_body(item: OtherItem | Item):
return {"item": item}

View File

@ -1,10 +1,9 @@
from typing import Annotated, Any, Union
from typing import Annotated, Any, Literal
from fastapi import FastAPI
from fastapi.testclient import TestClient
from inline_snapshot import snapshot
from pydantic import BaseModel, Field
from typing_extensions import Literal
def test_discriminator_pydantic_v2() -> None:
@ -21,7 +20,7 @@ def test_discriminator_pydantic_v2() -> None:
price: float
Item = Annotated[
Union[Annotated[FirstItem, Tag("first")], Annotated[OtherItem, Tag("other")]],
Annotated[FirstItem, Tag("first")] | Annotated[OtherItem, Tag("other")],
Field(discriminator="value"),
]

View File

@ -1,6 +1,6 @@
# Ref: https://github.com/fastapi/fastapi/discussions/14495
from typing import Annotated, Union
from typing import Annotated
import pytest
from fastapi import FastAPI
@ -27,7 +27,7 @@ def client_fixture() -> TestClient:
return v.get("pet_type", "")
Pet = Annotated[
Union[Annotated[Cat, Tag("cat")], Annotated[Dog, Tag("dog")]],
Annotated[Cat, Tag("cat")] | Annotated[Dog, Tag("dog")],
Discriminator(get_pet_type),
]

View File

@ -1,4 +1,4 @@
from typing import Annotated, Union
from typing import Annotated
from fastapi import FastAPI, Form
from fastapi.testclient import TestClient
@ -19,7 +19,7 @@ class CompanyForm(BaseModel):
@app.post("/form-union/")
def post_union_form(data: Annotated[Union[UserForm, CompanyForm], Form()]):
def post_union_form(data: Annotated[UserForm | CompanyForm, Form()]):
return {"received": data}

View File

@ -1,5 +1,3 @@
from typing import Optional, Union
from fastapi import FastAPI
from fastapi.testclient import TestClient
from inline_snapshot import snapshot
@ -9,7 +7,7 @@ app = FastAPI()
class Item(BaseModel):
name: Optional[str] = None
name: str | None = None
class ExtendedItem(Item):
@ -17,7 +15,7 @@ class ExtendedItem(Item):
@app.post("/items/")
def save_union_different_body(item: Union[ExtendedItem, Item]):
def save_union_different_body(item: ExtendedItem | Item):
return {"item": item}

View File

@ -1,5 +1,3 @@
from typing import Optional, Union
import pytest
from fastapi import FastAPI
from fastapi.exceptions import ResponseValidationError
@ -11,8 +9,8 @@ app = FastAPI()
class Item(BaseModel):
name: str
price: Optional[float] = None
owner_ids: Optional[list[int]] = None
price: float | None = None
owner_ids: list[int] | None = None
@app.get("/items/invalid", response_model=Item)
@ -25,7 +23,7 @@ def get_invalid_none():
return None
@app.get("/items/validnone", response_model=Union[Item, None])
@app.get("/items/validnone", response_model=Item | None)
def get_valid_none(send_none: bool = False):
if send_none:
return None

View File

@ -1,5 +1,3 @@
from typing import Optional
import pytest
from fastapi import FastAPI
from fastapi.exceptions import ResponseValidationError
@ -12,8 +10,8 @@ app = FastAPI()
@dataclass
class Item:
name: str
price: Optional[float] = None
owner_ids: Optional[list[int]] = None
price: float | None = None
owner_ids: list[int] | None = None
@app.get("/items/invalid", response_model=Item)

View File

@ -2,7 +2,6 @@ import sys
import pytest
needs_py39 = pytest.mark.skipif(sys.version_info < (3, 9), reason="requires python3.9+")
needs_py310 = pytest.mark.skipif(
sys.version_info < (3, 10), reason="requires python3.10+"
)