Browse Source

🎨 Upgrade typing syntax for Python 3.10 (#14932)

Co-authored-by: Copilot <[email protected]>
Co-authored-by: tiangolo <[email protected]>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
pull/14863/head
Sebastián Ramírez 4 months ago
committed by GitHub
parent
commit
faee822574
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 4
      docs_src/extra_models/tutorial003_py310.py
  2. 22
      scripts/doc_parsing_utils.py
  3. 8
      scripts/mkdocs_hooks.py
  4. 24
      scripts/notify_translations.py
  5. 12
      scripts/people.py
  6. 5
      tests/main.py
  7. 4
      tests/test_additional_properties_bool.py
  8. 4
      tests/test_additional_responses_union_duplicate_anyof.py
  9. 3
      tests/test_callable_endpoint.py
  10. 24
      tests/test_compat.py
  11. 3
      tests/test_custom_middleware_exception.py
  12. 8
      tests/test_custom_schema_fields.py
  13. 6
      tests/test_dependency_contextvars.py
  14. 4
      tests/test_dependency_overrides.py
  15. 4
      tests/test_dependency_paramless.py
  16. 6
      tests/test_enforce_once_required_parameter.py
  17. 4
      tests/test_extra_routes.py
  18. 4
      tests/test_filter_pydantic_sub_model_pv2.py
  19. 8
      tests/test_form_default.py
  20. 4
      tests/test_forms_single_model.py
  21. 6
      tests/test_infer_param_optionality.py
  22. 4
      tests/test_invalid_sequence_param.py
  23. 4
      tests/test_jsonable_encoder.py
  24. 8
      tests/test_openapi_examples.py
  25. 4
      tests/test_openapi_query_parameter_extension.py
  26. 4
      tests/test_openapi_schema_type.py
  27. 8
      tests/test_openapi_separate_input_output_schemas.py
  28. 4
      tests/test_optional_file_list.py
  29. 4
      tests/test_param_class.py
  30. 8
      tests/test_param_include_in_schema.py
  31. 3
      tests/test_pydantic_v1_error.py
  32. 5
      tests/test_pydanticv2_dataclasses_uuid_stringified_annotations.py
  33. 8
      tests/test_request_params/test_body/test_list.py
  34. 20
      tests/test_request_params/test_body/test_optional_list.py
  35. 20
      tests/test_request_params/test_body/test_optional_str.py
  36. 12
      tests/test_request_params/test_body/test_required_str.py
  37. 18
      tests/test_request_params/test_cookie/test_optional_str.py
  38. 18
      tests/test_request_params/test_file/test_optional.py
  39. 20
      tests/test_request_params/test_file/test_optional_list.py
  40. 20
      tests/test_request_params/test_form/test_optional_list.py
  41. 18
      tests/test_request_params/test_form/test_optional_str.py
  42. 20
      tests/test_request_params/test_header/test_optional_list.py
  43. 18
      tests/test_request_params/test_header/test_optional_str.py
  44. 20
      tests/test_request_params/test_query/test_optional_list.py
  45. 18
      tests/test_request_params/test_query/test_optional_str.py
  46. 8
      tests/test_required_noneable.py
  47. 12
      tests/test_response_model_as_return_annotation.py
  48. 5
      tests/test_router_events.py
  49. 20
      tests/test_schema_extra_examples.py
  50. 4
      tests/test_security_api_key_cookie_optional.py
  51. 6
      tests/test_security_api_key_header_optional.py
  52. 6
      tests/test_security_api_key_query_optional.py
  53. 4
      tests/test_security_http_base_optional.py
  54. 3
      tests/test_security_http_basic_optional.py
  55. 4
      tests/test_security_http_bearer_optional.py
  56. 4
      tests/test_security_http_digest_optional.py
  57. 4
      tests/test_security_oauth2_authorization_code_bearer.py
  58. 4
      tests/test_security_oauth2_authorization_code_bearer_description.py
  59. 6
      tests/test_security_oauth2_authorization_code_bearer_scopes_openapi.py
  60. 6
      tests/test_security_oauth2_optional.py
  61. 6
      tests/test_security_oauth2_optional_description.py
  62. 4
      tests/test_security_oauth2_password_bearer_optional.py
  63. 4
      tests/test_security_oauth2_password_bearer_optional_description.py
  64. 6
      tests/test_security_openid_connect_optional.py
  65. 6
      tests/test_serialize_response.py
  66. 5
      tests/test_serialize_response_dataclass.py
  67. 6
      tests/test_serialize_response_model.py
  68. 12
      tests/test_skip_defaults.py
  69. 6
      tests/test_sub_callbacks.py
  70. 6
      tests/test_union_body.py
  71. 5
      tests/test_union_body_discriminator.py
  72. 4
      tests/test_union_body_discriminator_annotated.py
  73. 4
      tests/test_union_forms.py
  74. 6
      tests/test_union_inherited_body.py
  75. 8
      tests/test_validate_response.py
  76. 6
      tests/test_validate_response_dataclass.py
  77. 1
      tests/utils.py

4
docs_src/extra_models/tutorial003_py310.py

@ -1,5 +1,3 @@
from typing import Union
from fastapi import FastAPI from fastapi import FastAPI
from pydantic import BaseModel 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): async def read_item(item_id: str):
return items[item_id] return items[item_id]

22
scripts/doc_parsing_utils.py

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

8
scripts/mkdocs_hooks.py

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

24
scripts/notify_translations.py

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

12
scripts/people.py

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

5
tests/main.py

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

4
tests/test_additional_properties_bool.py

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

4
tests/test_additional_responses_union_duplicate_anyof.py

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

3
tests/test_callable_endpoint.py

@ -1,11 +1,10 @@
from functools import partial from functools import partial
from typing import Optional
from fastapi import FastAPI from fastapi import FastAPI
from fastapi.testclient import TestClient 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} return {"some_arg": some_arg, "q": q}

24
tests/test_compat.py

@ -1,5 +1,3 @@
from typing import Union
from fastapi import FastAPI, UploadFile from fastapi import FastAPI, UploadFile
from fastapi._compat import ( from fastapi._compat import (
Undefined, Undefined,
@ -10,8 +8,6 @@ from fastapi.testclient import TestClient
from pydantic import BaseModel, ConfigDict from pydantic import BaseModel, ConfigDict
from pydantic.fields import FieldInfo from pydantic.fields import FieldInfo
from .utils import needs_py310
def test_model_field_default_required(): def test_model_field_default_required():
from fastapi._compat import v2 from fastapi._compat import v2
@ -26,7 +22,7 @@ def test_complex():
app = FastAPI() app = FastAPI()
@app.post("/") @app.post("/")
def foo(foo: Union[str, list[int]]): def foo(foo: str | list[int]):
return foo return foo
client = TestClient(app) client = TestClient(app)
@ -49,17 +45,17 @@ def test_propagates_pydantic2_model_config():
class EmbeddedModel(BaseModel): class EmbeddedModel(BaseModel):
model_config = ConfigDict(arbitrary_types_allowed=True) model_config = ConfigDict(arbitrary_types_allowed=True)
value: Union[str, Missing] = Missing() value: str | Missing = Missing()
class Model(BaseModel): class Model(BaseModel):
model_config = ConfigDict( model_config = ConfigDict(
arbitrary_types_allowed=True, arbitrary_types_allowed=True,
) )
value: Union[str, Missing] = Missing() value: str | Missing = Missing()
embedded_model: EmbeddedModel = EmbeddedModel() embedded_model: EmbeddedModel = EmbeddedModel()
@app.post("/") @app.post("/")
def foo(req: Model) -> dict[str, Union[str, None]]: def foo(req: Model) -> dict[str, str | None]:
return { return {
"value": req.value or None, "value": req.value or None,
"embedded_value": req.embedded_model.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 # 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 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" # 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(): 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 # 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 # and other types, but I'm not even sure it's a good idea to support it as a first
# class "feature" # 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(): def test_serialize_sequence_value_with_optional_list():
"""Test that serialize_sequence_value handles optional lists correctly.""" """Test that serialize_sequence_value handles optional lists correctly."""
from fastapi._compat import v2 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) field = v2.ModelField(name="items", field_info=field_info)
result = v2.serialize_sequence_value(field=field, value=["a", "b", "c"]) result = v2.serialize_sequence_value(field=field, value=["a", "b", "c"])
assert result == ["a", "b", "c"] assert result == ["a", "b", "c"]
assert isinstance(result, list) assert isinstance(result, list)
@needs_py310
def test_serialize_sequence_value_with_optional_list_pipe_union(): def test_serialize_sequence_value_with_optional_list_pipe_union():
"""Test that serialize_sequence_value handles optional lists correctly (with new syntax).""" """Test that serialize_sequence_value handles optional lists correctly (with new syntax)."""
from fastapi._compat import v2 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(): def test_serialize_sequence_value_with_none_first_in_union():
"""Test that serialize_sequence_value handles Union[None, List[...]] correctly.""" """Test that serialize_sequence_value handles Union[None, List[...]] correctly."""
from typing import Union
from fastapi._compat import v2 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) field = v2.ModelField(name="items", field_info=field_info)
result = v2.serialize_sequence_value(field=field, value=["x", "y"]) result = v2.serialize_sequence_value(field=field, value=["x", "y"])
assert result == ["x", "y"] assert result == ["x", "y"]

3
tests/test_custom_middleware_exception.py

@ -1,5 +1,4 @@
from pathlib import Path from pathlib import Path
from typing import Optional
from fastapi import APIRouter, FastAPI, File, UploadFile from fastapi import APIRouter, FastAPI, File, UploadFile
from fastapi.exceptions import HTTPException 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 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.app = app
self.max_content_size = max_content_size self.max_content_size = max_content_size

8
tests/test_custom_schema_fields.py

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

6
tests/test_dependency_contextvars.py

@ -1,11 +1,11 @@
from collections.abc import Awaitable from collections.abc import Awaitable, Callable
from contextvars import ContextVar from contextvars import ContextVar
from typing import Any, Callable, Optional from typing import Any
from fastapi import Depends, FastAPI, Request, Response from fastapi import Depends, FastAPI, Request, Response
from fastapi.testclient import TestClient 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 "legacy_request_state_context_var", default=None
) )

4
tests/test_dependency_overrides.py

@ -1,5 +1,3 @@
from typing import Optional
import pytest import pytest
from fastapi import APIRouter, Depends, FastAPI from fastapi import APIRouter, Depends, FastAPI
from fastapi.testclient import TestClient from fastapi.testclient import TestClient
@ -38,7 +36,7 @@ app.include_router(router)
client = TestClient(app) 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} return {"q": q, "skip": 5, "limit": 10}

4
tests/test_dependency_paramless.py

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

6
tests/test_enforce_once_required_parameter.py

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

4
tests/test_extra_routes.py

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

4
tests/test_filter_pydantic_sub_model_pv2.py

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

8
tests/test_form_default.py

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

4
tests/test_forms_single_model.py

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

6
tests/test_infer_param_optionality.py

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

4
tests/test_invalid_sequence_param.py

@ -1,5 +1,3 @@
from typing import Optional
import pytest import pytest
from fastapi import FastAPI, Query from fastapi import FastAPI, Query
from pydantic import BaseModel from pydantic import BaseModel
@ -61,5 +59,5 @@ def test_invalid_simple_dict():
title: str title: str
@app.get("/items/") @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 pass # pragma: no cover

4
tests/test_jsonable_encoder.py

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

8
tests/test_openapi_examples.py

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

4
tests/test_openapi_query_parameter_extension.py

@ -1,5 +1,3 @@
from typing import Optional
from fastapi import FastAPI from fastapi import FastAPI
from fastapi.testclient import TestClient from fastapi.testclient import TestClient
from inline_snapshot import snapshot 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 {} return {}

4
tests/test_openapi_schema_type.py

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

8
tests/test_openapi_separate_input_output_schemas.py

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

4
tests/test_optional_file_list.py

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

4
tests/test_param_class.py

@ -1,5 +1,3 @@
from typing import Optional
from fastapi import FastAPI from fastapi import FastAPI
from fastapi.params import Param from fastapi.params import Param
from fastapi.testclient import TestClient from fastapi.testclient import TestClient
@ -8,7 +6,7 @@ app = FastAPI()
@app.get("/items/") @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} return {"q": q}

8
tests/test_param_include_in_schema.py

@ -1,5 +1,3 @@
from typing import Optional
import pytest import pytest
from fastapi import Cookie, FastAPI, Header, Path, Query from fastapi import Cookie, FastAPI, Header, Path, Query
from fastapi.testclient import TestClient from fastapi.testclient import TestClient
@ -10,14 +8,14 @@ app = FastAPI()
@app.get("/hidden_cookie") @app.get("/hidden_cookie")
async def 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} return {"hidden_cookie": hidden_cookie}
@app.get("/hidden_header") @app.get("/hidden_header")
async def 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} 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") @app.get("/hidden_query")
async def 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} return {"hidden_query": hidden_query}

3
tests/test_pydantic_v1_error.py

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

5
tests/test_pydanticv2_dataclasses_uuid_stringified_annotations.py

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

8
tests/test_request_params/test_body/test_list.py

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

20
tests/test_request_params/test_body/test_optional_list.py

@ -1,4 +1,4 @@
from typing import Annotated, Optional from typing import Annotated
import pytest import pytest
from fastapi import Body, FastAPI from fastapi import Body, FastAPI
@ -15,13 +15,13 @@ app = FastAPI()
@app.post("/optional-list-str", operation_id="optional_list_str") @app.post("/optional-list-str", operation_id="optional_list_str")
async def read_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} return {"p": p}
class BodyModelOptionalListStr(BaseModel): 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") @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") @app.post("/optional-list-alias", operation_id="optional_list_alias")
async def read_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} return {"p": p}
class BodyModelOptionalListAlias(BaseModel): 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") @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( def read_optional_list_validation_alias(
p: Annotated[ 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, ] = None,
): ):
return {"p": p} return {"p": p}
class BodyModelOptionalListValidationAlias(BaseModel): 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( @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( def read_optional_list_alias_and_validation_alias(
p: Annotated[ p: Annotated[
Optional[list[str]], list[str] | None,
Body(embed=True, alias="p_alias", validation_alias="p_val_alias"), Body(embed=True, alias="p_alias", validation_alias="p_val_alias"),
] = None, ] = None,
): ):
@ -331,9 +331,7 @@ def read_optional_list_alias_and_validation_alias(
class BodyModelOptionalListAliasAndValidationAlias(BaseModel): class BodyModelOptionalListAliasAndValidationAlias(BaseModel):
p: Optional[list[str]] = Field( p: list[str] | None = Field(None, alias="p_alias", validation_alias="p_val_alias")
None, alias="p_alias", validation_alias="p_val_alias"
)
@app.post( @app.post(

20
tests/test_request_params/test_body/test_optional_str.py

@ -1,4 +1,4 @@
from typing import Annotated, Optional from typing import Annotated
import pytest import pytest
from fastapi import Body, FastAPI from fastapi import Body, FastAPI
@ -14,12 +14,12 @@ app = FastAPI()
@app.post("/optional-str", operation_id="optional_str") @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} return {"p": p}
class BodyModelOptionalStr(BaseModel): class BodyModelOptionalStr(BaseModel):
p: Optional[str] = None p: str | None = None
@app.post("/model-optional-str", operation_id="model_optional_str") @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") @app.post("/optional-alias", operation_id="optional_alias")
async def read_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} return {"p": p}
class BodyModelOptionalAlias(BaseModel): 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") @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") @app.post("/optional-validation-alias", operation_id="optional_validation_alias")
def read_optional_validation_alias( def read_optional_validation_alias(
p: Annotated[ p: Annotated[str | None, Body(embed=True, validation_alias="p_val_alias")] = None,
Optional[str], Body(embed=True, validation_alias="p_val_alias")
] = None,
): ):
return {"p": p} return {"p": p}
class BodyModelOptionalValidationAlias(BaseModel): 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( @app.post(
@ -309,14 +307,14 @@ def test_optional_validation_alias_by_validation_alias(path: str):
) )
def read_optional_alias_and_validation_alias( def read_optional_alias_and_validation_alias(
p: Annotated[ 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, ] = None,
): ):
return {"p": p} return {"p": p}
class BodyModelOptionalAliasAndValidationAlias(BaseModel): 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( @app.post(

12
tests/test_request_params/test_body/test_required_str.py

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

18
tests/test_request_params/test_cookie/test_optional_str.py

@ -1,4 +1,4 @@
from typing import Annotated, Optional from typing import Annotated
import pytest import pytest
from fastapi import Cookie, FastAPI from fastapi import Cookie, FastAPI
@ -13,12 +13,12 @@ app = FastAPI()
@app.get("/optional-str") @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} return {"p": p}
class CookieModelOptionalStr(BaseModel): class CookieModelOptionalStr(BaseModel):
p: Optional[str] = None p: str | None = None
@app.get("/model-optional-str") @app.get("/model-optional-str")
@ -75,13 +75,13 @@ def test_optional_str(path: str):
@app.get("/optional-alias") @app.get("/optional-alias")
async def read_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} return {"p": p}
class CookieModelOptionalAlias(BaseModel): class CookieModelOptionalAlias(BaseModel):
p: Optional[str] = Field(None, alias="p_alias") p: str | None = Field(None, alias="p_alias")
@app.get("/model-optional-alias") @app.get("/model-optional-alias")
@ -153,13 +153,13 @@ def test_optional_alias_by_alias(path: str):
@app.get("/optional-validation-alias") @app.get("/optional-validation-alias")
def read_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} return {"p": p}
class CookieModelOptionalValidationAlias(BaseModel): 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") @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") @app.get("/optional-alias-and-validation-alias")
def read_optional_alias_and_validation_alias( def read_optional_alias_and_validation_alias(
p: Annotated[ 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, ] = None,
): ):
return {"p": p} return {"p": p}
class CookieModelOptionalAliasAndValidationAlias(BaseModel): 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") @app.get("/model-optional-alias-and-validation-alias")

18
tests/test_request_params/test_file/test_optional.py

@ -1,4 +1,4 @@
from typing import Annotated, Optional from typing import Annotated
import pytest import pytest
from fastapi import FastAPI, File, UploadFile from fastapi import FastAPI, File, UploadFile
@ -13,12 +13,12 @@ app = FastAPI()
@app.post("/optional-bytes", operation_id="optional_bytes") @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} return {"file_size": len(p) if p else None}
@app.post("/optional-uploadfile", operation_id="optional_uploadfile") @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} 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") @app.post("/optional-bytes-alias", operation_id="optional_bytes_alias")
async def read_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} return {"file_size": len(p) if p else None}
@app.post("/optional-uploadfile-alias", operation_id="optional_uploadfile_alias") @app.post("/optional-uploadfile-alias", operation_id="optional_uploadfile_alias")
async def read_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} 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" "/optional-bytes-validation-alias", operation_id="optional_bytes_validation_alias"
) )
def read_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} 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", operation_id="optional_uploadfile_validation_alias",
) )
def read_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} 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( def read_optional_bytes_alias_and_validation_alias(
p: Annotated[ 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, ] = None,
): ):
return {"file_size": len(p) if p else 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( def read_optional_uploadfile_alias_and_validation_alias(
p: Annotated[ 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, ] = None,
): ):
return {"file_size": p.size if p else None} return {"file_size": p.size if p else None}

20
tests/test_request_params/test_file/test_optional_list.py

@ -1,4 +1,4 @@
from typing import Annotated, Optional from typing import Annotated
import pytest import pytest
from fastapi import FastAPI, File, UploadFile from fastapi import FastAPI, File, UploadFile
@ -13,13 +13,13 @@ app = FastAPI()
@app.post("/optional-list-bytes") @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} return {"file_size": [len(file) for file in p] if p else None}
@app.post("/optional-list-uploadfile") @app.post("/optional-list-uploadfile")
async def read_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} 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") @app.post("/optional-list-bytes-alias")
async def read_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} return {"file_size": [len(file) for file in p] if p else None}
@app.post("/optional-list-uploadfile-alias") @app.post("/optional-list-uploadfile-alias")
async def read_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} 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") @app.post("/optional-list-bytes-validation-alias")
def read_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} return {"file_size": [len(file) for file in p] if p else None}
@app.post("/optional-list-uploadfile-validation-alias") @app.post("/optional-list-uploadfile-validation-alias")
def read_optional_list_uploadfile_validation_alias( def read_optional_list_uploadfile_validation_alias(
p: Annotated[ p: Annotated[list[UploadFile] | None, File(validation_alias="p_val_alias")] = None,
Optional[list[UploadFile]], File(validation_alias="p_val_alias")
] = None,
): ):
return {"file_size": [file.size for file in p] if p else 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") @app.post("/optional-list-bytes-alias-and-validation-alias")
def read_optional_list_bytes_alias_and_validation_alias( def read_optional_list_bytes_alias_and_validation_alias(
p: Annotated[ 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, ] = None,
): ):
return {"file_size": [len(file) for file in p] if p else 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") @app.post("/optional-list-uploadfile-alias-and-validation-alias")
def read_optional_list_uploadfile_alias_and_validation_alias( def read_optional_list_uploadfile_alias_and_validation_alias(
p: Annotated[ p: Annotated[
Optional[list[UploadFile]], list[UploadFile] | None,
File(alias="p_alias", validation_alias="p_val_alias"), File(alias="p_alias", validation_alias="p_val_alias"),
] = None, ] = None,
): ):

20
tests/test_request_params/test_form/test_optional_list.py

@ -1,4 +1,4 @@
from typing import Annotated, Optional from typing import Annotated
import pytest import pytest
from fastapi import FastAPI, Form from fastapi import FastAPI, Form
@ -15,13 +15,13 @@ app = FastAPI()
@app.post("/optional-list-str", operation_id="optional_list_str") @app.post("/optional-list-str", operation_id="optional_list_str")
async def read_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} return {"p": p}
class FormModelOptionalListStr(BaseModel): 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") @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") @app.post("/optional-list-alias", operation_id="optional_list_alias")
async def read_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} return {"p": p}
class FormModelOptionalListAlias(BaseModel): 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") @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" "/optional-list-validation-alias", operation_id="optional_list_validation_alias"
) )
def read_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} return {"p": p}
class FormModelOptionalListValidationAlias(BaseModel): 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( @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( def read_optional_list_alias_and_validation_alias(
p: Annotated[ 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, ] = None,
): ):
return {"p": p} return {"p": p}
class FormModelOptionalListAliasAndValidationAlias(BaseModel): class FormModelOptionalListAliasAndValidationAlias(BaseModel):
p: Optional[list[str]] = Field( p: list[str] | None = Field(None, alias="p_alias", validation_alias="p_val_alias")
None, alias="p_alias", validation_alias="p_val_alias"
)
@app.post( @app.post(

18
tests/test_request_params/test_form/test_optional_str.py

@ -1,4 +1,4 @@
from typing import Annotated, Optional from typing import Annotated
import pytest import pytest
from fastapi import FastAPI, Form from fastapi import FastAPI, Form
@ -14,12 +14,12 @@ app = FastAPI()
@app.post("/optional-str", operation_id="optional_str") @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} return {"p": p}
class FormModelOptionalStr(BaseModel): class FormModelOptionalStr(BaseModel):
p: Optional[str] = None p: str | None = None
@app.post("/model-optional-str", operation_id="model_optional_str") @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") @app.post("/optional-alias", operation_id="optional_alias")
async def read_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} return {"p": p}
class FormModelOptionalAlias(BaseModel): 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") @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") @app.post("/optional-validation-alias", operation_id="optional_validation_alias")
def read_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} return {"p": p}
class FormModelOptionalValidationAlias(BaseModel): 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( @app.post(
@ -238,14 +238,14 @@ def test_optional_validation_alias_by_validation_alias(path: str):
) )
def read_optional_alias_and_validation_alias( def read_optional_alias_and_validation_alias(
p: Annotated[ 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, ] = None,
): ):
return {"p": p} return {"p": p}
class FormModelOptionalAliasAndValidationAlias(BaseModel): 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( @app.post(

20
tests/test_request_params/test_header/test_optional_list.py

@ -1,4 +1,4 @@
from typing import Annotated, Optional from typing import Annotated
import pytest import pytest
from fastapi import FastAPI, Header from fastapi import FastAPI, Header
@ -14,13 +14,13 @@ app = FastAPI()
@app.get("/optional-list-str") @app.get("/optional-list-str")
async def read_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} return {"p": p}
class HeaderModelOptionalListStr(BaseModel): class HeaderModelOptionalListStr(BaseModel):
p: Optional[list[str]] = None p: list[str] | None = None
@app.get("/model-optional-list-str") @app.get("/model-optional-list-str")
@ -81,13 +81,13 @@ def test_optional_list_str(path: str):
@app.get("/optional-list-alias") @app.get("/optional-list-alias")
async def read_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} return {"p": p}
class HeaderModelOptionalListAlias(BaseModel): 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") @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") @app.get("/optional-list-validation-alias")
def read_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} return {"p": p}
class HeaderModelOptionalListValidationAlias(BaseModel): 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") @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") @app.get("/optional-list-alias-and-validation-alias")
def read_optional_list_alias_and_validation_alias( def read_optional_list_alias_and_validation_alias(
p: Annotated[ 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, ] = None,
): ):
return {"p": p} return {"p": p}
class HeaderModelOptionalListAliasAndValidationAlias(BaseModel): class HeaderModelOptionalListAliasAndValidationAlias(BaseModel):
p: Optional[list[str]] = Field( p: list[str] | None = Field(None, alias="p_alias", validation_alias="p_val_alias")
None, alias="p_alias", validation_alias="p_val_alias"
)
@app.get("/model-optional-list-alias-and-validation-alias") @app.get("/model-optional-list-alias-and-validation-alias")

18
tests/test_request_params/test_header/test_optional_str.py

@ -1,4 +1,4 @@
from typing import Annotated, Optional from typing import Annotated
import pytest import pytest
from fastapi import FastAPI, Header from fastapi import FastAPI, Header
@ -13,12 +13,12 @@ app = FastAPI()
@app.get("/optional-str") @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} return {"p": p}
class HeaderModelOptionalStr(BaseModel): class HeaderModelOptionalStr(BaseModel):
p: Optional[str] = None p: str | None = None
@app.get("/model-optional-str") @app.get("/model-optional-str")
@ -74,13 +74,13 @@ def test_optional_str(path: str):
@app.get("/optional-alias") @app.get("/optional-alias")
async def read_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} return {"p": p}
class HeaderModelOptionalAlias(BaseModel): class HeaderModelOptionalAlias(BaseModel):
p: Optional[str] = Field(None, alias="p_alias") p: str | None = Field(None, alias="p_alias")
@app.get("/model-optional-alias") @app.get("/model-optional-alias")
@ -150,13 +150,13 @@ def test_optional_alias_by_alias(path: str):
@app.get("/optional-validation-alias") @app.get("/optional-validation-alias")
def read_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} return {"p": p}
class HeaderModelOptionalValidationAlias(BaseModel): 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") @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") @app.get("/optional-alias-and-validation-alias")
def read_optional_alias_and_validation_alias( def read_optional_alias_and_validation_alias(
p: Annotated[ 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, ] = None,
): ):
return {"p": p} return {"p": p}
class HeaderModelOptionalAliasAndValidationAlias(BaseModel): 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") @app.get("/model-optional-alias-and-validation-alias")

20
tests/test_request_params/test_query/test_optional_list.py

@ -1,4 +1,4 @@
from typing import Annotated, Optional from typing import Annotated
import pytest import pytest
from fastapi import FastAPI, Query from fastapi import FastAPI, Query
@ -14,13 +14,13 @@ app = FastAPI()
@app.get("/optional-list-str") @app.get("/optional-list-str")
async def read_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} return {"p": p}
class QueryModelOptionalListStr(BaseModel): class QueryModelOptionalListStr(BaseModel):
p: Optional[list[str]] = None p: list[str] | None = None
@app.get("/model-optional-list-str") @app.get("/model-optional-list-str")
@ -81,13 +81,13 @@ def test_optional_list_str(path: str):
@app.get("/optional-list-alias") @app.get("/optional-list-alias")
async def read_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} return {"p": p}
class QueryModelOptionalListAlias(BaseModel): 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") @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") @app.get("/optional-list-validation-alias")
def read_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} return {"p": p}
class QueryModelOptionalListValidationAlias(BaseModel): 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") @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") @app.get("/optional-list-alias-and-validation-alias")
def read_optional_list_alias_and_validation_alias( def read_optional_list_alias_and_validation_alias(
p: Annotated[ 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, ] = None,
): ):
return {"p": p} return {"p": p}
class QueryModelOptionalListAliasAndValidationAlias(BaseModel): class QueryModelOptionalListAliasAndValidationAlias(BaseModel):
p: Optional[list[str]] = Field( p: list[str] | None = Field(None, alias="p_alias", validation_alias="p_val_alias")
None, alias="p_alias", validation_alias="p_val_alias"
)
@app.get("/model-optional-list-alias-and-validation-alias") @app.get("/model-optional-list-alias-and-validation-alias")

18
tests/test_request_params/test_query/test_optional_str.py

@ -1,4 +1,4 @@
from typing import Annotated, Optional from typing import Annotated
import pytest import pytest
from fastapi import FastAPI, Query from fastapi import FastAPI, Query
@ -13,12 +13,12 @@ app = FastAPI()
@app.get("/optional-str") @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} return {"p": p}
class QueryModelOptionalStr(BaseModel): class QueryModelOptionalStr(BaseModel):
p: Optional[str] = None p: str | None = None
@app.get("/model-optional-str") @app.get("/model-optional-str")
@ -74,13 +74,13 @@ def test_optional_str(path: str):
@app.get("/optional-alias") @app.get("/optional-alias")
async def read_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} return {"p": p}
class QueryModelOptionalAlias(BaseModel): class QueryModelOptionalAlias(BaseModel):
p: Optional[str] = Field(None, alias="p_alias") p: str | None = Field(None, alias="p_alias")
@app.get("/model-optional-alias") @app.get("/model-optional-alias")
@ -150,13 +150,13 @@ def test_optional_alias_by_alias(path: str):
@app.get("/optional-validation-alias") @app.get("/optional-validation-alias")
def read_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} return {"p": p}
class QueryModelOptionalValidationAlias(BaseModel): 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") @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") @app.get("/optional-alias-and-validation-alias")
def read_optional_alias_and_validation_alias( def read_optional_alias_and_validation_alias(
p: Annotated[ 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, ] = None,
): ):
return {"p": p} return {"p": p}
class QueryModelOptionalAliasAndValidationAlias(BaseModel): 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") @app.get("/model-optional-alias-and-validation-alias")

8
tests/test_required_noneable.py

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

12
tests/test_response_model_as_return_annotation.py

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

5
tests/test_router_events.py

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

20
tests/test_schema_extra_examples.py

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

4
tests/test_security_api_key_cookie_optional.py

@ -1,5 +1,3 @@
from typing import Optional
from fastapi import Depends, FastAPI, Security from fastapi import Depends, FastAPI, Security
from fastapi.security import APIKeyCookie from fastapi.security import APIKeyCookie
from fastapi.testclient import TestClient from fastapi.testclient import TestClient
@ -15,7 +13,7 @@ class User(BaseModel):
username: str 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: if oauth_header is None:
return None return None
user = User(username=oauth_header) user = User(username=oauth_header)

6
tests/test_security_api_key_header_optional.py

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

6
tests/test_security_api_key_query_optional.py

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

4
tests/test_security_http_base_optional.py

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

3
tests/test_security_http_basic_optional.py

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

4
tests/test_security_http_bearer_optional.py

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

4
tests/test_security_http_digest_optional.py

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

4
tests/test_security_oauth2_authorization_code_bearer.py

@ -1,5 +1,3 @@
from typing import Optional
from fastapi import FastAPI, Security from fastapi import FastAPI, Security
from fastapi.security import OAuth2AuthorizationCodeBearer from fastapi.security import OAuth2AuthorizationCodeBearer
from fastapi.testclient import TestClient from fastapi.testclient import TestClient
@ -13,7 +11,7 @@ oauth2_scheme = OAuth2AuthorizationCodeBearer(
@app.get("/items/") @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} return {"token": token}

4
tests/test_security_oauth2_authorization_code_bearer_description.py

@ -1,5 +1,3 @@
from typing import Optional
from fastapi import FastAPI, Security from fastapi import FastAPI, Security
from fastapi.security import OAuth2AuthorizationCodeBearer from fastapi.security import OAuth2AuthorizationCodeBearer
from fastapi.testclient import TestClient from fastapi.testclient import TestClient
@ -16,7 +14,7 @@ oauth2_scheme = OAuth2AuthorizationCodeBearer(
@app.get("/items/") @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} return {"token": token}

6
tests/test_security_oauth2_authorization_code_bearer_scopes_openapi.py

@ -1,6 +1,6 @@
# Ref: https://github.com/fastapi/fastapi/issues/14454 # 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 import APIRouter, Depends, FastAPI, Security
from fastapi.security import OAuth2AuthorizationCodeBearer from fastapi.security import OAuth2AuthorizationCodeBearer
@ -46,13 +46,13 @@ router = APIRouter(dependencies=[Security(oauth2_scheme, scopes=["read"])])
@router.get("/items/") @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} return {"token": token}
@router.post("/items/") @router.post("/items/")
async def create_item( 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} return {"token": token}

6
tests/test_security_oauth2_optional.py

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

6
tests/test_security_oauth2_optional_description.py

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

4
tests/test_security_oauth2_password_bearer_optional.py

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

4
tests/test_security_oauth2_password_bearer_optional_description.py

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

6
tests/test_security_openid_connect_optional.py

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

6
tests/test_serialize_response.py

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

5
tests/test_serialize_response_dataclass.py

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

6
tests/test_serialize_response_model.py

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

12
tests/test_skip_defaults.py

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

6
tests/test_sub_callbacks.py

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

6
tests/test_union_body.py

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

5
tests/test_union_body_discriminator.py

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

4
tests/test_union_body_discriminator_annotated.py

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

4
tests/test_union_forms.py

@ -1,4 +1,4 @@
from typing import Annotated, Union from typing import Annotated
from fastapi import FastAPI, Form from fastapi import FastAPI, Form
from fastapi.testclient import TestClient from fastapi.testclient import TestClient
@ -19,7 +19,7 @@ class CompanyForm(BaseModel):
@app.post("/form-union/") @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} return {"received": data}

6
tests/test_union_inherited_body.py

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

8
tests/test_validate_response.py

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

6
tests/test_validate_response_dataclass.py

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

1
tests/utils.py

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

Loading…
Cancel
Save