From b7de2b7feb207b0a157b81c237b5a0cba6e21cb9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebasti=C3=A1n=20Ram=C3=ADrez?= Date: Mon, 15 Jun 2026 17:53:46 +0200 Subject: [PATCH] =?UTF-8?q?=F0=9F=94=A7=20Add=20ty=20configs=20to=20check?= =?UTF-8?q?=20docs=20sources=20(#15770)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .pre-commit-config.yaml | 2 +- scripts/contributors.py | 2 +- scripts/deploy_docs_status.py | 2 +- scripts/doc_parsing_utils.py | 4 +-- scripts/docs.py | 4 +-- scripts/label_approved.py | 2 +- scripts/lint.sh | 2 +- scripts/notify_translations.py | 3 ++- scripts/people.py | 2 +- scripts/sponsors.py | 2 +- scripts/topic_repos.py | 2 +- tests/test_compat.py | 18 +++++++++---- tests/test_custom_middleware_exception.py | 4 ++- tests/test_datastructures.py | 7 ++--- tests/test_default_response_class.py | 2 +- tests/test_deprecated_responses.py | 10 +++---- tests/test_inherited_custom_class.py | 2 +- tests/test_jsonable_encoder.py | 10 +++---- tests/test_local_docs.py | 20 +++++++------- tests/test_openapi_schema_type.py | 2 +- tests/test_orjson_response_class.py | 4 +-- ...est_response_model_as_return_annotation.py | 26 +++++++++---------- tests/test_router_events.py | 18 ++++++------- tests/test_schema_compat_pydantic_v2.py | 2 +- tests/test_serialize_response_model.py | 4 +-- tests/test_skip_defaults.py | 2 +- tests/test_sse.py | 4 +-- tests/test_starlette_urlconvertors.py | 4 +-- tests/test_stream_cancellation.py | 9 ++++--- tests/test_swagger_ui_escape.py | 6 ++--- .../test_body/test_tutorial001.py | 2 +- ...est_tutorial001_tutorial002_tutorial003.py | 3 ++- .../test_custom_response/test_tutorial008.py | 3 ++- .../test_custom_response/test_tutorial009.py | 3 ++- .../test_custom_response/test_tutorial009b.py | 3 ++- .../test_debugging/test_tutorial001.py | 10 +++---- .../test_openapi_webhooks/test_tutorial001.py | 5 +++- .../test_python_types/test_tutorial003.py | 2 +- .../test_python_types/test_tutorial005.py | 8 +++--- .../test_security/test_tutorial005.py | 8 +++--- .../test_sql_databases/test_tutorial001.py | 14 ++++++---- .../test_sql_databases/test_tutorial002.py | 14 ++++++---- tests/test_webhooks_security.py | 5 +++- tests/utils.py | 6 ++--- 44 files changed, 149 insertions(+), 118 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index de6f56a53..e5c359eeb 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -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 diff --git a/scripts/contributors.py b/scripts/contributors.py index af1434d79..5e9d72d64 100644 --- a/scripts/contributors.py +++ b/scripts/contributors.py @@ -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) diff --git a/scripts/deploy_docs_status.py b/scripts/deploy_docs_status.py index e620b15ba..8e5a2e38b 100644 --- a/scripts/deploy_docs_status.py +++ b/scripts/deploy_docs_status.py @@ -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())) diff --git a/scripts/doc_parsing_utils.py b/scripts/doc_parsing_utils.py index 88ff2c50b..f2f047c3d 100644 --- a/scripts/doc_parsing_utils.py +++ b/scripts/doc_parsing_utils.py @@ -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) diff --git a/scripts/docs.py b/scripts/docs.py index a478d59a0..07c951cc7 100644 --- a/scripts/docs.py +++ b/scripts/docs.py @@ -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() diff --git a/scripts/label_approved.py b/scripts/label_approved.py index 81de92efb..397a79663 100644 --- a/scripts/label_approved.py +++ b/scripts/label_approved.py @@ -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: diff --git a/scripts/lint.sh b/scripts/lint.sh index 291674e32..a7d1f2f66 100755 --- a/scripts/lint.sh +++ b/scripts/lint.sh @@ -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 diff --git a/scripts/notify_translations.py b/scripts/notify_translations.py index 3484b69c7..22fa633f4 100644 --- a/scripts/notify_translations.py +++ b/scripts/notify_translations.py @@ -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 diff --git a/scripts/people.py b/scripts/people.py index 5718d65da..72b591367 100644 --- a/scripts/people.py +++ b/scripts/people.py @@ -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()) diff --git a/scripts/sponsors.py b/scripts/sponsors.py index fdcabc737..38d8cbfa4 100644 --- a/scripts/sponsors.py +++ b/scripts/sponsors.py @@ -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) diff --git a/scripts/topic_repos.py b/scripts/topic_repos.py index b7afc0864..94379d384 100644 --- a/scripts/topic_repos.py +++ b/scripts/topic_repos.py @@ -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) diff --git a/tests/test_compat.py b/tests/test_compat.py index 772bd305e..76151fac9 100644 --- a/tests/test_compat.py +++ b/tests/test_compat.py @@ -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"] diff --git a/tests/test_custom_middleware_exception.py b/tests/test_custom_middleware_exception.py index cf548f4ae..989ab58bc 100644 --- a/tests/test_custom_middleware_exception.py +++ b/tests/test_custom_middleware_exception.py @@ -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, diff --git a/tests/test_datastructures.py b/tests/test_datastructures.py index 29a70cae0..1b5335ea9 100644 --- a/tests/test_datastructures.py +++ b/tests/test_datastructures.py @@ -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 diff --git a/tests/test_default_response_class.py b/tests/test_default_response_class.py index 88498e560..bc60e0b14 100644 --- a/tests/test_default_response_class.py +++ b/tests/test_default_response_class.py @@ -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) diff --git a/tests/test_deprecated_responses.py b/tests/test_deprecated_responses.py index 8cbd9c11f..8a5663744 100644 --- a/tests/test_deprecated_responses.py +++ b/tests/test_deprecated_responses.py @@ -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] diff --git a/tests/test_inherited_custom_class.py b/tests/test_inherited_custom_class.py index 8cf8952f9..54c6566a0 100644 --- a/tests/test_inherited_custom_class.py +++ b/tests/test_inherited_custom_class.py @@ -13,7 +13,7 @@ class MyUuid: def __str__(self): return self.uuid - @property # type: ignore + @property def __class__(self): return uuid.UUID diff --git a/tests/test_jsonable_encoder.py b/tests/test_jsonable_encoder.py index c23a9e5d7..8f8bd3fcb 100644 --- a/tests/test_jsonable_encoder.py +++ b/tests/test_jsonable_encoder.py @@ -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(): diff --git a/tests/test_local_docs.py b/tests/test_local_docs.py index 5f102edf1..351161182 100644 --- a/tests/test_local_docs.py +++ b/tests/test_local_docs.py @@ -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 diff --git a/tests/test_openapi_schema_type.py b/tests/test_openapi_schema_type.py index e8166d2fb..610375b77 100644 --- a/tests/test_openapi_schema_type.py +++ b/tests/test_openapi_schema_type.py @@ -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] diff --git a/tests/test_orjson_response_class.py b/tests/test_orjson_response_class.py index 3e34041dc..499b3e585 100644 --- a/tests/test_orjson_response_class.py +++ b/tests/test_orjson_response_class.py @@ -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") diff --git a/tests/test_response_model_as_return_annotation.py b/tests/test_response_model_as_return_annotation.py index 7be7902ad..36d50afa9 100644 --- a/tests/test_response_model_as_return_annotation.py +++ b/tests/test_response_model_as_return_annotation.py @@ -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( diff --git a/tests/test_router_events.py b/tests/test_router_events.py index 7869a7afc..e4f5a58f0 100644 --- a/tests/test_router_events.py +++ b/tests/test_router_events.py @@ -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]: diff --git a/tests/test_schema_compat_pydantic_v2.py b/tests/test_schema_compat_pydantic_v2.py index 7612c6ab5..bf47e62b2 100644 --- a/tests/test_schema_compat_pydantic_v2.py +++ b/tests/test_schema_compat_pydantic_v2.py @@ -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 diff --git a/tests/test_serialize_response_model.py b/tests/test_serialize_response_model.py index bb05f7bc4..6ee55ead8 100644 --- a/tests/test_serialize_response_model.py +++ b/tests/test_serialize_response_model.py @@ -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( diff --git a/tests/test_skip_defaults.py b/tests/test_skip_defaults.py index 238da7392..170cf21e3 100644 --- a/tests/test_skip_defaults.py +++ b/tests/test_skip_defaults.py @@ -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( diff --git a/tests/test_sse.py b/tests/test_sse.py index 86a67f8f9..6a9d669fe 100644 --- a/tests/test_sse.py +++ b/tests/test_sse.py @@ -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): diff --git a/tests/test_starlette_urlconvertors.py b/tests/test_starlette_urlconvertors.py index 5ef1b819c..cebe3dbe8 100644 --- a/tests/test_starlette_urlconvertors.py +++ b/tests/test_starlette_urlconvertors.py @@ -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(): diff --git a/tests/test_stream_cancellation.py b/tests/test_stream_cancellation.py index 20069c5f6..18e6d67d5 100644 --- a/tests/test_stream_cancellation.py +++ b/tests/test_stream_cancellation.py @@ -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. diff --git a/tests/test_swagger_ui_escape.py b/tests/test_swagger_ui_escape.py index 072d21952..6b9851abd 100644 --- a/tests/test_swagger_ui_escape.py +++ b/tests/test_swagger_ui_escape.py @@ -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 "