diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index de6f56a537..e5c359eebb 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 af1434d795..5e9d72d643 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 e620b15baf..8e5a2e38bf 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 88ff2c50bd..f2f047c3db 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 a478d59a0a..07c951cc73 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 81de92efbe..397a796632 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 291674e32e..a7d1f2f665 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 3484b69c70..22fa633f45 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 5718d65da9..72b591367c 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 fdcabc737b..38d8cbfa46 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 b7afc0864a..94379d3848 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 772bd305eb..76151fac94 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 cf548f4aed..989ab58bc7 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 29a70cae0c..1b5335ea9c 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 88498e5601..bc60e0b14e 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 8cbd9c11fe..8a56637447 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 8cf8952f92..54c6566a03 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 c23a9e5d79..8f8bd3fcbb 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 5f102edf1a..3511611827 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 e8166d2fb9..610375b77c 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 3e34041dc9..499b3e585e 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 7be7902ada..36d50afa9b 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 7869a7afcd..e4f5a58f06 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 7612c6ab5b..bf47e62b2c 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 bb05f7bc40..6ee55ead8a 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 238da7392f..170cf21e3d 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 86a67f8f9f..6a9d669fec 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 5ef1b819cd..cebe3dbe85 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 20069c5f6b..18e6d67d50 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 072d219522..6b9851abd1 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 "