]> git.ipfire.org Git - thirdparty/fastapi/fastapi.git/commitdiff
🔧 Add ty configs to check docs sources (#15770)
authorSebastián Ramírez <tiangolo@gmail.com>
Mon, 15 Jun 2026 15:53:46 +0000 (17:53 +0200)
committerGitHub <noreply@github.com>
Mon, 15 Jun 2026 15:53:46 +0000 (17:53 +0200)
44 files changed:
.pre-commit-config.yaml
scripts/contributors.py
scripts/deploy_docs_status.py
scripts/doc_parsing_utils.py
scripts/docs.py
scripts/label_approved.py
scripts/lint.sh
scripts/notify_translations.py
scripts/people.py
scripts/sponsors.py
scripts/topic_repos.py
tests/test_compat.py
tests/test_custom_middleware_exception.py
tests/test_datastructures.py
tests/test_default_response_class.py
tests/test_deprecated_responses.py
tests/test_inherited_custom_class.py
tests/test_jsonable_encoder.py
tests/test_local_docs.py
tests/test_openapi_schema_type.py
tests/test_orjson_response_class.py
tests/test_response_model_as_return_annotation.py
tests/test_router_events.py
tests/test_schema_compat_pydantic_v2.py
tests/test_serialize_response_model.py
tests/test_skip_defaults.py
tests/test_sse.py
tests/test_starlette_urlconvertors.py
tests/test_stream_cancellation.py
tests/test_swagger_ui_escape.py
tests/test_tutorial/test_body/test_tutorial001.py
tests/test_tutorial/test_body_nested_models/test_tutorial001_tutorial002_tutorial003.py
tests/test_tutorial/test_custom_response/test_tutorial008.py
tests/test_tutorial/test_custom_response/test_tutorial009.py
tests/test_tutorial/test_custom_response/test_tutorial009b.py
tests/test_tutorial/test_debugging/test_tutorial001.py
tests/test_tutorial/test_openapi_webhooks/test_tutorial001.py
tests/test_tutorial/test_python_types/test_tutorial003.py
tests/test_tutorial/test_python_types/test_tutorial005.py
tests/test_tutorial/test_security/test_tutorial005.py
tests/test_tutorial/test_sql_databases/test_tutorial001.py
tests/test_tutorial/test_sql_databases/test_tutorial002.py
tests/test_webhooks_security.py
tests/utils.py

index de6f56a5378fbf887c84c2b87cbbabe37099cb75..e5c359eebb1588ac9c051acd111b25a45188cff6 100644 (file)
@@ -45,7 +45,7 @@ repos:
 
       - id: local-ty
         name: ty check
-        entry: uv run ty check fastapi docs_src --force-exclude
+        entry: uv run ty check
         require_serial: true
         language: unsupported
         pass_filenames: false
index af1434d7950fda465ef2b4ea2383476fc1f91220..5e9d72d6434626a422d04628bef8fad90ca79121 100644 (file)
@@ -237,7 +237,7 @@ def update_content(*, content_path: Path, new_content: Any) -> bool:
 
 def main() -> None:
     logging.basicConfig(level=logging.INFO)
-    settings = Settings()
+    settings = Settings()  # ty: ignore[missing-argument]
     logging.info(f"Using config: {settings.model_dump_json()}")
     g = Github(settings.github_token.get_secret_value())
     repo = g.get_repo(settings.github_repository)
index e620b15baf0e440e87db387885fd053d24ae2ca5..8e5a2e38bf8225ef04a5cbb872b4199fc13aab49 100644 (file)
@@ -24,7 +24,7 @@ class LinkData(BaseModel):
 
 def main() -> None:
     logging.basicConfig(level=logging.INFO)
-    settings = Settings()
+    settings = Settings()  # ty: ignore[missing-argument]
 
     logging.info(f"Using config: {settings.model_dump_json()}")
     g = Github(auth=Auth.Token(settings.github_token.get_secret_value()))
index 88ff2c50bdd375579d2b23f96d1a0f6a1284f8a8..f2f047c3db16798e2da549ea3e01638b058da71f 100644 (file)
@@ -625,14 +625,14 @@ def replace_multiline_code_block(
             _line_b_code, line_b_comment = _split_hash_comment(line_b)
             res_line = line_b
             if line_b_comment:
-                res_line = res_line.replace(line_b_comment, line_a_comment, 1)
+                res_line = res_line.replace(line_b_comment, line_a_comment or "", 1)
             code_block.append(res_line)
         elif block_language in {"console", "json", "slash-style-comments"}:
             _line_a_code, line_a_comment = _split_slashes_comment(line_a)
             _line_b_code, line_b_comment = _split_slashes_comment(line_b)
             res_line = line_b
             if line_b_comment:
-                res_line = res_line.replace(line_b_comment, line_a_comment, 1)
+                res_line = res_line.replace(line_b_comment, line_a_comment or "", 1)
             code_block.append(res_line)
         else:
             code_block.append(line_b)
index a478d59a0a16e11d935e585c774f1e3b00bfbb76..07c951cc73254b81821c4e998cf7454eee49e972 100644 (file)
@@ -155,7 +155,7 @@ def build_lang(
     """
     build_zensical_lang_to_stage(lang)
     copy_zensical_stage_to_site(lang)
-    typer.secho(f"Successfully built docs for: {lang}", color=typer.colors.GREEN)
+    typer.secho(f"Successfully built docs for: {lang}", fg=typer.colors.GREEN)
 
 
 def split_markdown_header(markdown: str) -> tuple[str, str]:
@@ -408,7 +408,7 @@ def build_all() -> None:
     for lang in langs:
         if lang != "en":
             copy_zensical_stage_to_site(lang)
-    typer.secho("Successfully built all docs", color=typer.colors.GREEN)
+    typer.secho("Successfully built all docs", fg=typer.colors.GREEN)
 
 
 @app.command()
index 81de92efbe84dd2fdc66087e145d0d28ca5126fa..397a796632b89cda2565f9c5df16df2f1a5b9f21 100644 (file)
@@ -22,7 +22,7 @@ class Settings(BaseSettings):
     config: dict[str, LabelSettings] | Literal[""] = default_config
 
 
-settings = Settings()
+settings = Settings()  # ty: ignore[missing-argument]
 if settings.debug:
     logging.basicConfig(level=logging.DEBUG)
 else:
index 291674e32e930846fd3efa9e57da48b0b9ad0ed6..a7d1f2f665bcf93b3ed4ff159302cdc7f9e78c9e 100755 (executable)
@@ -4,6 +4,6 @@ set -e
 set -x
 
 mypy fastapi
-ty check fastapi docs_src --force-exclude
+ty check
 ruff check fastapi tests docs_src scripts
 ruff format fastapi tests --check
index 3484b69c7046cf80d5a404ed762d7f6ddc34335e..22fa633f458a74b4dc268735b86974a9d928984e 100644 (file)
@@ -304,7 +304,7 @@ def update_comment(*, settings: Settings, comment_id: str, body: str) -> Comment
 
 
 def main() -> None:
-    settings = Settings()
+    settings = Settings()  # ty: ignore[missing-argument]
     if settings.debug:
         logging.basicConfig(level=logging.DEBUG)
     else:
@@ -324,6 +324,7 @@ def main() -> None:
     ) or settings.number
     if number is None:
         raise RuntimeError("No PR number available")
+    number = cast(int, number)
 
     # Avoid race conditions with multiple labels
     sleep_time = random.random() * 10  # random number between 0 and 10 seconds
index 5718d65da91bc5f20899d8639b7fcd9cba0472d6..72b591367c62f306525f44cfeea5ba771b17c2c1 100644 (file)
@@ -394,7 +394,7 @@ def update_content(*, content_path: Path, new_content: Any) -> bool:
 
 def main() -> None:
     logging.basicConfig(level=logging.INFO)
-    settings = Settings()
+    settings = Settings()  # ty: ignore[missing-argument]
     logging.info(f"Using config: {settings.model_dump_json()}")
     rate_limiter.speed_multiplier = settings.speed_multiplier
     g = Github(settings.github_token.get_secret_value())
index fdcabc737b2df38dd48f1d6790953f87885dbc4e..38d8cbfa463170ba49721558eeaa041f45dd696e 100644 (file)
@@ -158,7 +158,7 @@ def update_content(*, content_path: Path, new_content: Any) -> bool:
 
 def main() -> None:
     logging.basicConfig(level=logging.INFO)
-    settings = Settings()
+    settings = Settings()  # ty: ignore[missing-argument]
     logging.info(f"Using config: {settings.model_dump_json()}")
     g = Github(settings.pr_token.get_secret_value())
     repo = g.get_repo(settings.github_repository)
index b7afc0864ac9c2fc33fcc0740e718913e2bc7db0..94379d38487ec28b5f1201b3912db6e69db7a85a 100644 (file)
@@ -24,7 +24,7 @@ class Repo(BaseModel):
 
 def main() -> None:
     logging.basicConfig(level=logging.INFO)
-    settings = Settings()
+    settings = Settings()  # ty: ignore[missing-argument]
 
     logging.info(f"Using config: {settings.model_dump_json()}")
     g = Github(settings.github_token.get_secret_value(), per_page=100)
index 772bd305eb41559f722f15e1abf0ecbeae2273a1..76151fac94af03e6191564acf7a4803f9a316291 100644 (file)
@@ -1,3 +1,5 @@
+from typing import Any, cast
+
 from fastapi import FastAPI, UploadFile
 from fastapi._compat import (
     Undefined,
@@ -56,9 +58,15 @@ def test_propagates_pydantic2_model_config():
 
     @app.post("/")
     def foo(req: Model) -> dict[str, str | None]:
+        value = req.value
+        if isinstance(value, Missing):
+            value = None
+        embedded_value = req.embedded_model.value
+        if isinstance(embedded_value, Missing):
+            embedded_value = None
         return {
-            "value": req.value or None,
-            "embedded_value": req.embedded_model.value or None,
+            "value": value,
+            "embedded_value": embedded_value,
         }
 
     client = TestClient(app)
@@ -100,7 +108,7 @@ def test_serialize_sequence_value_with_optional_list():
     """Test that serialize_sequence_value handles optional lists correctly."""
     from fastapi._compat import v2
 
-    field_info = FieldInfo(annotation=list[str] | None)
+    field_info = FieldInfo(annotation=cast(Any, list[str] | None))
     field = v2.ModelField(name="items", field_info=field_info)
     result = v2.serialize_sequence_value(field=field, value=["a", "b", "c"])
     assert result == ["a", "b", "c"]
@@ -111,7 +119,7 @@ def test_serialize_sequence_value_with_optional_list_pipe_union():
     """Test that serialize_sequence_value handles optional lists correctly (with new syntax)."""
     from fastapi._compat import v2
 
-    field_info = FieldInfo(annotation=list[str] | None)
+    field_info = FieldInfo(annotation=cast(Any, list[str] | None))
     field = v2.ModelField(name="items", field_info=field_info)
     result = v2.serialize_sequence_value(field=field, value=["a", "b", "c"])
     assert result == ["a", "b", "c"]
@@ -125,7 +133,7 @@ def test_serialize_sequence_value_with_none_first_in_union():
     from fastapi._compat import v2
 
     # 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_info = FieldInfo(annotation=cast(Any, Union[None, list[str]]))  # noqa: UP007
     field = v2.ModelField(name="items", field_info=field_info)
     result = v2.serialize_sequence_value(field=field, value=["x", "y"])
     assert result == ["x", "y"]
index cf548f4aed7f90cbd4db6b3d13f780b1a1fc5bdb..989ab58bc72ccd13bd5cfefd7adbc2e2c593769b 100644 (file)
@@ -3,6 +3,7 @@ from pathlib import Path
 from fastapi import APIRouter, FastAPI, File, UploadFile
 from fastapi.exceptions import HTTPException
 from fastapi.testclient import TestClient
+from starlette.types import ASGIApp
 
 app = FastAPI()
 
@@ -16,7 +17,7 @@ class ContentSizeLimitMiddleware:
       max_content_size (optional): the maximum content size allowed in bytes, None for no limit
     """
 
-    def __init__(self, app: APIRouter, max_content_size: int | None = None):
+    def __init__(self, app: ASGIApp, max_content_size: int | None = None):
         self.app = app
         self.max_content_size = max_content_size
 
@@ -31,6 +32,7 @@ class ContentSizeLimitMiddleware:
 
             body_len = len(message.get("body", b""))
             received += body_len
+            assert self.max_content_size is not None
             if received > self.max_content_size:
                 raise HTTPException(
                     422,
index 29a70cae0c3d96cd0c11587dc2a5425abefb8397..1b5335ea9c5398806146541a9198ab79fd3f785e 100644 (file)
@@ -1,9 +1,10 @@
 import io
 from pathlib import Path
+from typing import cast
 
 import pytest
 from fastapi import FastAPI, UploadFile
-from fastapi.datastructures import Default
+from fastapi.datastructures import Default, DefaultPlaceholder
 from fastapi.testclient import TestClient
 
 
@@ -13,8 +14,8 @@ def test_upload_file_invalid_pydantic_v2():
 
 
 def test_default_placeholder_equals():
-    placeholder_1 = Default("a")
-    placeholder_2 = Default("a")
+    placeholder_1 = cast(DefaultPlaceholder, Default("a"))
+    placeholder_2 = cast(DefaultPlaceholder, Default("a"))
     assert placeholder_1 == placeholder_2
     assert placeholder_1.value == placeholder_2.value
 
index 88498e5601e299b4de798cf64a8255ff1de0f8f0..bc60e0b14e688c59e0b617f44377d2f937ed6de9 100644 (file)
@@ -11,7 +11,7 @@ class ORJSONResponse(JSONResponse):
     media_type = "application/x-orjson"
 
     def render(self, content: Any) -> bytes:
-        import orjson
+        import orjson  # ty: ignore[unresolved-import]
 
         return orjson.dumps(content)
 
index 8cbd9c11feb35387f6c3a74c272f42ca8b2cafee..8a566374474f75419547f5d288e2c1b2cbb63534 100644 (file)
@@ -3,7 +3,7 @@ import warnings
 import pytest
 from fastapi import FastAPI
 from fastapi.exceptions import FastAPIDeprecationWarning
-from fastapi.responses import ORJSONResponse, UJSONResponse
+from fastapi.responses import ORJSONResponse, UJSONResponse  # ty: ignore[deprecated]
 from fastapi.testclient import TestClient
 from pydantic import BaseModel
 
@@ -21,7 +21,7 @@ class Item(BaseModel):
 def _make_orjson_app() -> FastAPI:
     with warnings.catch_warnings():
         warnings.simplefilter("ignore", FastAPIDeprecationWarning)
-        app = FastAPI(default_response_class=ORJSONResponse)
+        app = FastAPI(default_response_class=ORJSONResponse)  # ty: ignore[deprecated]
 
     @app.get("/items")
     def get_items() -> Item:
@@ -44,7 +44,7 @@ def test_orjson_response_returns_correct_data():
 @needs_orjson
 def test_orjson_response_emits_deprecation_warning():
     with pytest.warns(FastAPIDeprecationWarning, match="ORJSONResponse is deprecated"):
-        ORJSONResponse(content={"hello": "world"})
+        ORJSONResponse(content={"hello": "world"})  # ty: ignore[deprecated]
 
 
 # UJSON
@@ -53,7 +53,7 @@ def test_orjson_response_emits_deprecation_warning():
 def _make_ujson_app() -> FastAPI:
     with warnings.catch_warnings():
         warnings.simplefilter("ignore", FastAPIDeprecationWarning)
-        app = FastAPI(default_response_class=UJSONResponse)
+        app = FastAPI(default_response_class=UJSONResponse)  # ty: ignore[deprecated]
 
     @app.get("/items")
     def get_items() -> Item:
@@ -76,4 +76,4 @@ def test_ujson_response_returns_correct_data():
 @needs_ujson
 def test_ujson_response_emits_deprecation_warning():
     with pytest.warns(FastAPIDeprecationWarning, match="UJSONResponse is deprecated"):
-        UJSONResponse(content={"hello": "world"})
+        UJSONResponse(content={"hello": "world"})  # ty: ignore[deprecated]
index 8cf8952f92447c90bf40f4b4c3536e68f878a03a..54c6566a03073d715336d754ba0b69b2931927ce 100644 (file)
@@ -13,7 +13,7 @@ class MyUuid:
     def __str__(self):
         return self.uuid
 
-    @property  # type: ignore
+    @property
     def __class__(self):
         return uuid.UUID
 
index c23a9e5d7918aee9783a2157b06b188f0900ea9f..8f8bd3fcbbab68404286210f1a911a6f20fcce6f 100644 (file)
@@ -87,10 +87,10 @@ def test_encode_dict():
 def test_encode_dict_include_exclude_list():
     pet = {"name": "Firulais", "owner": {"name": "Foo"}}
     assert jsonable_encoder(pet) == {"name": "Firulais", "owner": {"name": "Foo"}}
-    assert jsonable_encoder(pet, include=["name"]) == {"name": "Firulais"}
-    assert jsonable_encoder(pet, exclude=["owner"]) == {"name": "Firulais"}
-    assert jsonable_encoder(pet, include=[]) == {}
-    assert jsonable_encoder(pet, exclude=[]) == {
+    assert jsonable_encoder(pet, include=["name"]) == {"name": "Firulais"}  # ty: ignore[invalid-argument-type]
+    assert jsonable_encoder(pet, exclude=["owner"]) == {"name": "Firulais"}  # ty: ignore[invalid-argument-type]
+    assert jsonable_encoder(pet, include=[]) == {}  # ty: ignore[invalid-argument-type]
+    assert jsonable_encoder(pet, exclude=[]) == {  # ty: ignore[invalid-argument-type]
         "name": "Firulais",
         "owner": {"name": "Foo"},
     }
@@ -176,7 +176,7 @@ def test_encode_model_with_config():
 
 def test_encode_model_with_alias_raises():
     with pytest.raises(ValidationError):
-        ModelWithAlias(foo="Bar")
+        ModelWithAlias(foo="Bar")  # ty: ignore[missing-argument, unknown-argument]
 
 
 def test_encode_model_with_alias():
index 5f102edf1ae8dbe874e143f40ecae858d01f5cd9..3511611827dc1187e8afedabc07868a4e330eafa 100644 (file)
@@ -9,7 +9,7 @@ def test_strings_in_generated_swagger():
     swagger_css_url = sig.parameters.get("swagger_css_url").default  # type: ignore
     swagger_favicon_url = sig.parameters.get("swagger_favicon_url").default  # type: ignore
     html = get_swagger_ui_html(openapi_url="/docs", title="title")
-    body_content = html.body.decode()
+    body_content = bytes(html.body).decode()
     assert swagger_js_url in body_content
     assert swagger_css_url in body_content
     assert swagger_favicon_url in body_content
@@ -26,7 +26,7 @@ def test_strings_in_custom_swagger():
         swagger_css_url=swagger_css_url,
         swagger_favicon_url=swagger_favicon_url,
     )
-    body_content = html.body.decode()
+    body_content = bytes(html.body).decode()
     assert swagger_js_url in body_content
     assert swagger_css_url in body_content
     assert swagger_favicon_url in body_content
@@ -37,7 +37,7 @@ def test_strings_in_generated_redoc():
     redoc_js_url = sig.parameters.get("redoc_js_url").default  # type: ignore
     redoc_favicon_url = sig.parameters.get("redoc_favicon_url").default  # type: ignore
     html = get_redoc_html(openapi_url="/docs", title="title")
-    body_content = html.body.decode()
+    body_content = bytes(html.body).decode()
     assert redoc_js_url in body_content
     assert redoc_favicon_url in body_content
 
@@ -51,17 +51,17 @@ def test_strings_in_custom_redoc():
         redoc_js_url=redoc_js_url,
         redoc_favicon_url=redoc_favicon_url,
     )
-    body_content = html.body.decode()
+    body_content = bytes(html.body).decode()
     assert redoc_js_url in body_content
     assert redoc_favicon_url in body_content
 
 
 def test_google_fonts_in_generated_redoc():
-    body_with_google_fonts = get_redoc_html(
-        openapi_url="/docs", title="title"
-    ).body.decode()
+    body_with_google_fonts = bytes(
+        get_redoc_html(openapi_url="/docs", title="title").body
+    ).decode()
     assert "fonts.googleapis.com" in body_with_google_fonts
-    body_without_google_fonts = get_redoc_html(
-        openapi_url="/docs", title="title", with_google_fonts=False
-    ).body.decode()
+    body_without_google_fonts = bytes(
+        get_redoc_html(openapi_url="/docs", title="title", with_google_fonts=False).body
+    ).decode()
     assert "fonts.googleapis.com" not in body_without_google_fonts
index e8166d2fb989fe5c5c54f90dcabca8d0b14e284c..610375b77cd135cffcd164af6ede677d88493174 100644 (file)
@@ -21,4 +21,4 @@ def test_allowed_schema_type(
 def test_invalid_type_value() -> None:
     """Test that Schema raises ValueError for invalid type values."""
     with pytest.raises(ValueError, match="2 validation errors for Schema"):
-        Schema(type=True)  # type: ignore[arg-type]
+        Schema(type=True)  # type: ignore[arg-type]  # ty: ignore[invalid-argument-type]
index 3e34041dc98c4bb50a66b75e91ee392d8dd3e974..499b3e585e03fbe6a4fb43cbb71b3f9fcdeb8fd8 100644 (file)
@@ -6,13 +6,13 @@ pytest.importorskip("orjson")
 
 from fastapi import FastAPI
 from fastapi.exceptions import FastAPIDeprecationWarning
-from fastapi.responses import ORJSONResponse
+from fastapi.responses import ORJSONResponse  # ty: ignore[deprecated]
 from fastapi.testclient import TestClient
 from sqlalchemy.sql.elements import quoted_name
 
 with warnings.catch_warnings():
     warnings.simplefilter("ignore", FastAPIDeprecationWarning)
-    app = FastAPI(default_response_class=ORJSONResponse)
+    app = FastAPI(default_response_class=ORJSONResponse)  # ty: ignore[deprecated]
 
 
 @app.get("/orjson_non_str_keys")
index 7be7902adaeb63e2463e4736a6320129feefa1eb..36d50afa9b17eb118e9c044a8827f6277c1eab99 100644 (file)
@@ -78,22 +78,22 @@ def no_response_model_annotation_return_same_model() -> User:
 
 @app.get("/no_response_model-annotation-return_exact_dict")
 def no_response_model_annotation_return_exact_dict() -> User:
-    return {"name": "John", "surname": "Doe"}
+    return {"name": "John", "surname": "Doe"}  # ty: ignore[invalid-return-type]
 
 
 @app.get("/no_response_model-annotation-return_invalid_dict")
 def no_response_model_annotation_return_invalid_dict() -> User:
-    return {"name": "John"}
+    return {"name": "John"}  # ty: ignore[invalid-return-type]
 
 
 @app.get("/no_response_model-annotation-return_invalid_model")
 def no_response_model_annotation_return_invalid_model() -> User:
-    return Item(name="Foo", price=42.0)
+    return Item(name="Foo", price=42.0)  # ty: ignore[invalid-return-type]
 
 
 @app.get("/no_response_model-annotation-return_dict_with_extra_data")
 def no_response_model_annotation_return_dict_with_extra_data() -> User:
-    return {"name": "John", "surname": "Doe", "password_hash": "secret"}
+    return {"name": "John", "surname": "Doe", "password_hash": "secret"}  # ty: ignore[invalid-return-type]
 
 
 @app.get("/no_response_model-annotation-return_submodel_with_extra_data")
@@ -108,24 +108,24 @@ def response_model_none_annotation_return_same_model() -> User:
 
 @app.get("/response_model_none-annotation-return_exact_dict", response_model=None)
 def response_model_none_annotation_return_exact_dict() -> User:
-    return {"name": "John", "surname": "Doe"}
+    return {"name": "John", "surname": "Doe"}  # ty: ignore[invalid-return-type]
 
 
 @app.get("/response_model_none-annotation-return_invalid_dict", response_model=None)
 def response_model_none_annotation_return_invalid_dict() -> User:
-    return {"name": "John"}
+    return {"name": "John"}  # ty: ignore[invalid-return-type]
 
 
 @app.get("/response_model_none-annotation-return_invalid_model", response_model=None)
 def response_model_none_annotation_return_invalid_model() -> User:
-    return Item(name="Foo", price=42.0)
+    return Item(name="Foo", price=42.0)  # ty: ignore[invalid-return-type]
 
 
 @app.get(
     "/response_model_none-annotation-return_dict_with_extra_data", response_model=None
 )
 def response_model_none_annotation_return_dict_with_extra_data() -> User:
-    return {"name": "John", "surname": "Doe", "password_hash": "secret"}
+    return {"name": "John", "surname": "Doe", "password_hash": "secret"}  # ty: ignore[invalid-return-type]
 
 
 @app.get(
@@ -140,21 +140,21 @@ def response_model_none_annotation_return_submodel_with_extra_data() -> User:
     "/response_model_model1-annotation_model2-return_same_model", response_model=User
 )
 def response_model_model1_annotation_model2_return_same_model() -> Item:
-    return User(name="John", surname="Doe")
+    return User(name="John", surname="Doe")  # ty: ignore[invalid-return-type]
 
 
 @app.get(
     "/response_model_model1-annotation_model2-return_exact_dict", response_model=User
 )
 def response_model_model1_annotation_model2_return_exact_dict() -> Item:
-    return {"name": "John", "surname": "Doe"}
+    return {"name": "John", "surname": "Doe"}  # ty: ignore[invalid-return-type]
 
 
 @app.get(
     "/response_model_model1-annotation_model2-return_invalid_dict", response_model=User
 )
 def response_model_model1_annotation_model2_return_invalid_dict() -> Item:
-    return {"name": "John"}
+    return {"name": "John"}  # ty: ignore[invalid-return-type]
 
 
 @app.get(
@@ -169,7 +169,7 @@ def response_model_model1_annotation_model2_return_invalid_model() -> Item:
     response_model=User,
 )
 def response_model_model1_annotation_model2_return_dict_with_extra_data() -> Item:
-    return {"name": "John", "surname": "Doe", "password_hash": "secret"}
+    return {"name": "John", "surname": "Doe", "password_hash": "secret"}  # ty: ignore[invalid-return-type]
 
 
 @app.get(
@@ -177,7 +177,7 @@ def response_model_model1_annotation_model2_return_dict_with_extra_data() -> Ite
     response_model=User,
 )
 def response_model_model1_annotation_model2_return_submodel_with_extra_data() -> Item:
-    return DBUser(name="John", surname="Doe", password_hash="secret")
+    return DBUser(name="John", surname="Doe", password_hash="secret")  # ty: ignore[invalid-return-type]
 
 
 @app.get(
index 7869a7afcdc9f4b78fd7a5c775e6312fbfcf7e5c..e4f5a58f06bc0a6a96293ef954284f765c2cf8d6 100644 (file)
@@ -31,31 +31,31 @@ def test_router_events(state: State) -> None:
     def main() -> dict[str, str]:
         return {"message": "Hello World"}
 
-    @app.on_event("startup")
+    @app.on_event("startup")  # ty: ignore[deprecated]
     def app_startup() -> None:
         state.app_startup = True
 
-    @app.on_event("shutdown")
+    @app.on_event("shutdown")  # ty: ignore[deprecated]
     def app_shutdown() -> None:
         state.app_shutdown = True
 
     router = APIRouter()
 
-    @router.on_event("startup")
+    @router.on_event("startup")  # ty: ignore[deprecated]
     def router_startup() -> None:
         state.router_startup = True
 
-    @router.on_event("shutdown")
+    @router.on_event("shutdown")  # ty: ignore[deprecated]
     def router_shutdown() -> None:
         state.router_shutdown = True
 
     sub_router = APIRouter()
 
-    @sub_router.on_event("startup")
+    @sub_router.on_event("startup")  # ty: ignore[deprecated]
     def sub_router_startup() -> None:
         state.sub_router_startup = True
 
-    @sub_router.on_event("shutdown")
+    @sub_router.on_event("shutdown")  # ty: ignore[deprecated]
     def sub_router_shutdown() -> None:
         state.sub_router_shutdown = True
 
@@ -253,7 +253,7 @@ def test_router_async_shutdown_handler(state: State) -> None:
     def main() -> dict[str, str]:
         return {"message": "Hello World"}
 
-    @app.on_event("shutdown")
+    @app.on_event("shutdown")  # ty: ignore[deprecated]
     async def app_shutdown() -> None:
         state.app_shutdown = True
 
@@ -274,7 +274,7 @@ def test_router_sync_generator_lifespan(state: State) -> None:
         yield
         state.app_shutdown = True
 
-    app = FastAPI(lifespan=lifespan)  # type: ignore[arg-type]
+    app = FastAPI(lifespan=lifespan)  # type: ignore[invalid-argument-type]  # ty: ignore[invalid-argument-type]
 
     @app.get("/")
     def main() -> dict[str, str]:
@@ -300,7 +300,7 @@ def test_router_async_generator_lifespan(state: State) -> None:
         yield
         state.app_shutdown = True
 
-    app = FastAPI(lifespan=lifespan)  # type: ignore[arg-type]
+    app = FastAPI(lifespan=lifespan)  # type: ignore[invalid-argument-type]  # ty: ignore[invalid-argument-type]
 
     @app.get("/")
     def main() -> dict[str, str]:
index 7612c6ab5ba12e5e8f2fc7d7766ebb17c4e5dc27..bf47e62b2c5dc0040c6b76b77d4102dae0ef9522 100644 (file)
@@ -26,7 +26,7 @@ def get_client():
 
     @app.get("/users")
     async def get_user() -> User:
-        return {"username": "alice", "role": "admin"}
+        return {"username": "alice", "role": "admin"}  # ty: ignore[invalid-return-type]
 
     client = TestClient(app)
     return client
index bb05f7bc402fff0ef8af9a7fe171a0aece4e8feb..6ee55ead8a7bc82eb11203f7fb03d9efd63d46ac 100644 (file)
@@ -18,7 +18,7 @@ def get_valid():
 
 @app.get("/items/coerce", response_model=Item)
 def get_coerce():
-    return Item(aliased_name="coerce", price="1.0")
+    return Item(aliased_name="coerce", price="1.0")  # ty: ignore[invalid-argument-type]
 
 
 @app.get("/items/validlist", response_model=list[Item])
@@ -52,7 +52,7 @@ def get_valid_exclude_unset():
     response_model_exclude_unset=True,
 )
 def get_coerce_exclude_unset():
-    return Item(aliased_name="coerce", price="1.0")
+    return Item(aliased_name="coerce", price="1.0")  # ty: ignore[invalid-argument-type]
 
 
 @app.get(
index 238da7392f06c86d43f7d99aba92682544e169bd..170cf21e3d19c0b2270c5ce99a6c79a08578a2a3 100644 (file)
@@ -29,7 +29,7 @@ class ModelDefaults(BaseModel):
 
 @app.get("/", response_model=Model, response_model_exclude_unset=True)
 def get_root() -> ModelSubclass:
-    return ModelSubclass(sub={}, y=1, z=0)
+    return ModelSubclass(sub={}, y=1, z=0)  # ty: ignore[invalid-argument-type]
 
 
 @app.get(
index 86a67f8f9f010d118f3e94fc65cbe9c653716c98..6a9d669fecbb28d47ba71f28d391633111befadc 100644 (file)
@@ -227,7 +227,7 @@ def test_server_sent_event_single_line_fields_reject_newlines(
     field_name: str, value: str
 ):
     with pytest.raises(ValueError, match=f"SSE '{field_name}' must be a single line"):
-        ServerSentEvent(data="test", **{field_name: value})
+        ServerSentEvent(data="test", **{field_name: value})  # ty: ignore[invalid-argument-type]
 
 
 def test_server_sent_event_negative_retry_rejected():
@@ -237,7 +237,7 @@ def test_server_sent_event_negative_retry_rejected():
 
 def test_server_sent_event_float_retry_rejected():
     with pytest.raises(ValueError):
-        ServerSentEvent(data="test", retry=1.5)  # type: ignore[arg-type]
+        ServerSentEvent(data="test", retry=1.5)  # type: ignore[arg-type]  # ty: ignore[invalid-argument-type]
 
 
 def test_raw_data_sent_without_json_encoding(client: TestClient):
index 5ef1b819cda70680287655d71ac9cc47d6f3271c..cebe3dbe8577fc98225f8ec73c9e3f0615ef7a1d 100644 (file)
@@ -32,7 +32,7 @@ def test_route_converters_int():
     response = client.get("/int/5")
     assert response.status_code == 200, response.text
     assert response.json() == {"int": 5}
-    assert app.url_path_for("int_convertor", param=5) == "/int/5"  # type: ignore
+    assert app.url_path_for("int_convertor", param=5) == "/int/5"
 
 
 def test_route_converters_float():
@@ -40,7 +40,7 @@ def test_route_converters_float():
     response = client.get("/float/25.5")
     assert response.status_code == 200, response.text
     assert response.json() == {"float": 25.5}
-    assert app.url_path_for("float_convertor", param=25.5) == "/float/25.5"  # type: ignore
+    assert app.url_path_for("float_convertor", param=25.5) == "/float/25.5"
 
 
 def test_route_converters_path():
index 20069c5f6b18f30cd61c879b38efb483e6518d70..18e6d67d50579c8772a54d0c30fa11fb8c459c4f 100644 (file)
@@ -10,6 +10,7 @@ import anyio
 import pytest
 from fastapi import FastAPI
 from fastapi.responses import StreamingResponse
+from starlette.types import Message, Scope
 
 pytestmark = [
     pytest.mark.anyio,
@@ -45,16 +46,16 @@ async def _run_asgi_and_cancel(app: FastAPI, path: str, timeout: float) -> bool:
     """
     chunks: list[bytes] = []
 
-    async def receive():  # type: ignore[no-untyped-def]
+    async def receive() -> Message:
         # Simulate a client that never disconnects, rely on cancellation
         await anyio.sleep(float("inf"))
         return {"type": "http.disconnect"}  # pragma: no cover
 
-    async def send(message: dict) -> None:  # type: ignore[type-arg]
+    async def send(message: Message) -> None:
         if message["type"] == "http.response.body":
             chunks.append(message.get("body", b""))
 
-    scope = {
+    scope: Scope = {
         "type": "http",
         "asgi": {"version": "3.0", "spec_version": "2.0"},
         "http_version": "1.1",
@@ -67,7 +68,7 @@ async def _run_asgi_and_cancel(app: FastAPI, path: str, timeout: float) -> bool:
     }
 
     with anyio.move_on_after(timeout) as cancel_scope:
-        await app(scope, receive, send)  # type: ignore[arg-type]
+        await app(scope, receive, send)
 
     # If we got here within the timeout the generator was cancellable.
     # cancel_scope.cancelled_caught is True when move_on_after fired.
index 072d219522185efead6021b533a6113af940d06b..6b9851abd12f57f2cb7226f8f8b9de7f1e8a9089 100644 (file)
@@ -8,7 +8,7 @@ def test_init_oauth_html_chars_are_escaped():
         title="Test",
         init_oauth={"appName": xss_payload},
     )
-    body = html.body.decode()
+    body = bytes(html.body).decode()
 
     assert "</script><script>" not in body
     assert "\\u003c/script\\u003e\\u003cscript\\u003e" in body
@@ -20,7 +20,7 @@ def test_swagger_ui_parameters_html_chars_are_escaped():
         title="Test",
         swagger_ui_parameters={"customKey": "<img src=x onerror=alert(1)>"},
     )
-    body = html.body.decode()
+    body = bytes(html.body).decode()
     assert "<img src=x onerror=alert(1)>" not in body
     assert "\\u003cimg" in body
 
@@ -31,7 +31,7 @@ def test_normal_init_oauth_still_works():
         title="Test",
         init_oauth={"clientId": "my-client", "appName": "My App"},
     )
-    body = html.body.decode()
+    body = bytes(html.body).decode()
     assert '"clientId": "my-client"' in body
     assert '"appName": "My App"' in body
     assert "ui.initOAuth" in body
index 8c883708a3fb1c92b2cff85009e04c1fcdbaebf3..c77b80426bfbc7cb3feb90232729211b0fccb130 100644 (file)
@@ -157,7 +157,7 @@ def test_post_broken_body(client: TestClient):
 
 
 def test_post_form_for_json(client: TestClient):
-    response = client.post("/items/", data={"name": "Foo", "price": 50.5})
+    response = client.post("/items/", data={"name": "Foo", "price": "50.5"})
     assert response.status_code == 422, response.text
     assert response.json() == {
         "detail": [
index 2e2f329f7937af7b048c787bc9de55447c5e91c8..3777fe98604e6b4e4502cbb33015840068086e21 100644 (file)
@@ -1,4 +1,5 @@
 import importlib
+from typing import Any
 
 import pytest
 from dirty_equals import IsList
@@ -130,7 +131,7 @@ def test_put_missing_required(client: TestClient):
 
 
 def test_openapi_schema(client: TestClient, mod_name: str):
-    tags_schema = {"default": [], "title": "Tags"}
+    tags_schema: dict[str, Any] = {"default": [], "title": "Tags"}
     if mod_name.startswith("tutorial001"):
         tags_schema.update(UNTYPED_LIST_SCHEMA)
     elif mod_name.startswith("tutorial002"):
index 1be7c1a622619fec96521f99466fa4639b93db4a..48f3101b56d6c41acf01306b2571b7a6bfe8e37c 100644 (file)
@@ -1,4 +1,5 @@
 from pathlib import Path
+from typing import Any, cast
 
 from fastapi.testclient import TestClient
 
@@ -10,7 +11,7 @@ client = TestClient(app)
 
 def test_get(tmp_path: Path):
     file_path: Path = tmp_path / "large-video-file.mp4"
-    tutorial008_py310.some_file_path = str(file_path)
+    cast(Any, tutorial008_py310).some_file_path = str(file_path)
     test_content = b"Fake video bytes"
     file_path.write_bytes(test_content)
     response = client.get("/")
index a6564d55163558657fa7ac9f83b7e473bf4f501e..f400aad8f3aec2355aeed5d463d9af1f689eb78c 100644 (file)
@@ -1,4 +1,5 @@
 from pathlib import Path
+from typing import Any, cast
 
 from fastapi.testclient import TestClient
 
@@ -10,7 +11,7 @@ client = TestClient(app)
 
 def test_get(tmp_path: Path):
     file_path: Path = tmp_path / "large-video-file.mp4"
-    tutorial009_py310.some_file_path = str(file_path)
+    cast(Any, tutorial009_py310).some_file_path = str(file_path)
     test_content = b"Fake video bytes"
     file_path.write_bytes(test_content)
     response = client.get("/")
index 05ac62f3d0d2b09ec00580dfcd77cbcaf58ef02e..d498d9e480bf127c439830574461ee2ad70a1a05 100644 (file)
@@ -1,4 +1,5 @@
 from pathlib import Path
+from typing import Any, cast
 
 from fastapi.testclient import TestClient
 
@@ -10,7 +11,7 @@ client = TestClient(app)
 
 def test_get(tmp_path: Path):
     file_path: Path = tmp_path / "large-video-file.mp4"
-    tutorial009b_py310.some_file_path = str(file_path)
+    cast(Any, tutorial009b_py310).some_file_path = str(file_path)
     test_content = b"Fake video bytes"
     file_path.write_bytes(test_content)
     response = client.get("/")
index fbf2bfa025560b9618d5cce720204393f9c71faa..e9afdd5e515ce6eb4d2983c26fb94e6ffcd7a357 100644 (file)
@@ -1,7 +1,7 @@
 import importlib
 import runpy
 import sys
-import unittest
+from unittest import mock
 
 import pytest
 from fastapi.testclient import TestClient
@@ -20,7 +20,7 @@ def get_client():
 def test_uvicorn_run_is_not_called_on_import():
     if sys.modules.get(MOD_NAME):
         del sys.modules[MOD_NAME]  # pragma: no cover
-    with unittest.mock.patch("uvicorn.run") as uvicorn_run_mock:
+    with mock.patch("uvicorn.run") as uvicorn_run_mock:
         importlib.import_module(MOD_NAME)
     uvicorn_run_mock.assert_not_called()
 
@@ -34,12 +34,10 @@ def test_get_root(client: TestClient):
 def test_uvicorn_run_called_when_run_as_main():  # Just for coverage
     if sys.modules.get(MOD_NAME):
         del sys.modules[MOD_NAME]
-    with unittest.mock.patch("uvicorn.run") as uvicorn_run_mock:
+    with mock.patch("uvicorn.run") as uvicorn_run_mock:
         runpy.run_module(MOD_NAME, run_name="__main__")
 
-    uvicorn_run_mock.assert_called_once_with(
-        unittest.mock.ANY, host="0.0.0.0", port=8000
-    )
+    uvicorn_run_mock.assert_called_once_with(mock.ANY, host="0.0.0.0", port=8000)
 
 
 def test_openapi_schema(client: TestClient):
index 696a2bed535b3804b8cae52d9f54157fe8f7ffe2..4eea308bafdb01be5f6a765f1ebfd8e28c82d94e 100644 (file)
@@ -1,3 +1,4 @@
+from fastapi.routing import APIRoute
 from fastapi.testclient import TestClient
 from inline_snapshot import snapshot
 
@@ -14,7 +15,9 @@ def test_get():
 
 def test_dummy_webhook():
     # Just for coverage
-    app.webhooks.routes[0].endpoint({})
+    route = app.webhooks.routes[0]
+    assert isinstance(route, APIRoute)
+    route.endpoint({})
 
 
 def test_openapi_schema():
index 20b1e7101dc8042f2b4a2e1f2e80312488ab9fc7..21ccad61208de1a522ac6b6ddd39083a85eae69b 100644 (file)
@@ -9,4 +9,4 @@ def test_get_name_with_age_pass_int():
 
 
 def test_get_name_with_age_pass_str():
-    assert get_name_with_age("John", "30") == "John is this old: 30"
+    assert get_name_with_age("John", "30") == "John is this old: 30"  # ty: ignore[invalid-argument-type]
index b5c847523d7855a317fee862570ef33be76e45b5..88b9b3601b8184045fe479da59e43bb0caa54467 100644 (file)
@@ -4,9 +4,9 @@ from docs_src.python_types.tutorial005_py310 import get_items
 def test_get_items():
     res = get_items(
         "item_a",
-        "item_b",
-        "item_c",
-        "item_d",
-        "item_e",
+        "item_b",  # ty: ignore[invalid-argument-type]
+        "item_c",  # ty: ignore[invalid-argument-type]
+        "item_d",  # ty: ignore[invalid-argument-type]
+        "item_e",  # ty: ignore[invalid-argument-type]
     )
     assert res == ("item_a", "item_b", "item_c", "item_d", "item_e")
index 0d25a1d2419b074c7b3968237dbb1bd703ab8c16..d1bb7e77b0e5cf0ac7067d4e6672d554963a0dd4 100644 (file)
@@ -1,6 +1,7 @@
 import importlib
 from functools import lru_cache
 from types import ModuleType
+from typing import Any, cast
 
 import pytest
 from fastapi.testclient import TestClient
@@ -29,12 +30,13 @@ def cache_verify_password(mod: ModuleType):
         f"Module {mod.__name__} does not have attribute 'verify_password'"
     )
 
-    original_func = mod.verify_password
+    mod_any = cast(Any, mod)
+    original_func = mod_any.verify_password
     cached_func = lru_cache()(original_func)
 
-    mod.verify_password = cached_func
+    mod_any.verify_password = cached_func
     yield
-    mod.verify_password = original_func
+    mod_any.verify_password = original_func
 
 
 def get_access_token(
index 787bb331631ff75f02019203a430e4970c215e47..ef556b868eb9fc35113a424e8863cabcc309375b 100644 (file)
@@ -1,5 +1,6 @@
 import importlib
 import warnings
+from typing import Any, cast
 
 import pytest
 from dirty_equals import IsInt
@@ -35,15 +36,18 @@ def get_client(request: pytest.FixtureRequest):
         mod = importlib.import_module(f"docs_src.sql_databases.{request.param}")
         clear_sqlmodel()
         importlib.reload(mod)
-    mod.sqlite_url = "sqlite://"
-    mod.engine = create_engine(
-        mod.sqlite_url, connect_args={"check_same_thread": False}, poolclass=StaticPool
+    mod_any = cast(Any, mod)
+    mod_any.sqlite_url = "sqlite://"
+    mod_any.engine = create_engine(
+        mod_any.sqlite_url,
+        connect_args={"check_same_thread": False},
+        poolclass=StaticPool,
     )
 
-    with TestClient(mod.app) as c:
+    with TestClient(mod_any.app) as c:
         yield c
     # Clean up connection explicitly to avoid resource warning
-    mod.engine.dispose()
+    mod_any.engine.dispose()
 
 
 def test_crud_app(client: TestClient):
index fb1c67922492aa3793800d9549eb92290a318bf6..cf8822b13d949878d3cce1935a2422130a247e87 100644 (file)
@@ -1,5 +1,6 @@
 import importlib
 import warnings
+from typing import Any, cast
 
 import pytest
 from dirty_equals import IsInt
@@ -35,15 +36,18 @@ def get_client(request: pytest.FixtureRequest):
         mod = importlib.import_module(f"docs_src.sql_databases.{request.param}")
         clear_sqlmodel()
         importlib.reload(mod)
-    mod.sqlite_url = "sqlite://"
-    mod.engine = create_engine(
-        mod.sqlite_url, connect_args={"check_same_thread": False}, poolclass=StaticPool
+    mod_any = cast(Any, mod)
+    mod_any.sqlite_url = "sqlite://"
+    mod_any.engine = create_engine(
+        mod_any.sqlite_url,
+        connect_args={"check_same_thread": False},
+        poolclass=StaticPool,
     )
 
-    with TestClient(mod.app) as c:
+    with TestClient(mod_any.app) as c:
         yield c
     # Clean up connection explicitly to avoid resource warning
-    mod.engine.dispose()
+    mod_any.engine.dispose()
 
 
 def test_crud_app(client: TestClient):
index 267e450d12c5d92d46403807f793e566773fc154..19cda33fa93098d8986651e3231f70b0f108090b 100644 (file)
@@ -33,7 +33,10 @@ client = TestClient(app)
 
 def test_dummy_webhook():
     # Just for coverage
-    new_subscription(body={}, token="Bearer 123")
+    new_subscription(
+        body=Subscription(username="rick", monthly_fee=9.99, start_date=datetime.now()),
+        token="Bearer 123",
+    )
 
 
 def test_openapi_schema():
index 934d3b0fa4e7fb7bcae98fe7ba8ef4a76453986a..970c40fdbf051a1e49eb4d8be4be767c2055d175 100644 (file)
@@ -1,5 +1,5 @@
-import importlib
 import sys
+from importlib.util import find_spec
 
 import pytest
 
@@ -11,12 +11,12 @@ needs_py314 = pytest.mark.skipif(
 )
 
 needs_orjson = pytest.mark.skipif(
-    importlib.util.find_spec("orjson") is None,
+    find_spec("orjson") is None,
     reason="requires orjson",
 )
 
 needs_ujson = pytest.mark.skipif(
-    importlib.util.find_spec("ujson") is None,
+    find_spec("ujson") is None,
     reason="requires ujson",
 )