🔊 Add a custom `FastAPIDeprecationWarning` (#14605)

This commit is contained in:
Sebastián Ramírez 2025-12-26 04:45:20 -08:00 committed by GitHub
parent 6b53786f62
commit 535b5daa31
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
19 changed files with 66 additions and 43 deletions

View File

@ -51,7 +51,7 @@ from fastapi.concurrency import (
contextmanager_in_threadpool,
)
from fastapi.dependencies.models import Dependant
from fastapi.exceptions import DependencyScopeError
from fastapi.exceptions import DependencyScopeError, FastAPIDeprecationWarning
from fastapi.logger import logger
from fastapi.security.oauth2 import SecurityScopes
from fastapi.types import DependencyCacheKey
@ -327,7 +327,7 @@ def get_dependant(
warnings.warn(
"pydantic.v1 is deprecated and will soon stop being supported by FastAPI."
f" Please update the param {param_name}: {param_details.type_annotation!r}.",
category=DeprecationWarning,
category=FastAPIDeprecationWarning,
stacklevel=5,
)
if isinstance(

View File

@ -231,3 +231,10 @@ class ResponseValidationError(ValidationException):
) -> None:
super().__init__(errors, endpoint_ctx=endpoint_ctx)
self.body = body
class FastAPIDeprecationWarning(UserWarning):
"""
A custom deprecation warning as DeprecationWarning is ignored
Ref: https://sethmlarson.dev/deprecations-via-warnings-dont-work-for-python-libraries
"""

View File

@ -23,6 +23,7 @@ from fastapi.dependencies.utils import (
get_validation_alias,
)
from fastapi.encoders import jsonable_encoder
from fastapi.exceptions import FastAPIDeprecationWarning
from fastapi.openapi.constants import METHODS_WITH_BODY, REF_PREFIX
from fastapi.openapi.models import OpenAPI
from fastapi.params import Body, ParamTypes
@ -215,9 +216,9 @@ def generate_operation_id(
*, route: routing.APIRoute, method: str
) -> str: # pragma: nocover
warnings.warn(
"fastapi.openapi.utils.generate_operation_id() was deprecated, "
message="fastapi.openapi.utils.generate_operation_id() was deprecated, "
"it is not used internally, and will be removed soon",
DeprecationWarning,
category=FastAPIDeprecationWarning,
stacklevel=2,
)
if route.operation_id:

View File

@ -4,6 +4,7 @@ from dataclasses import dataclass
from enum import Enum
from typing import Annotated, Any, Callable, Optional, Union
from fastapi.exceptions import FastAPIDeprecationWarning
from fastapi.openapi.models import Example
from pydantic.fields import FieldInfo
from typing_extensions import Literal, deprecated
@ -75,7 +76,7 @@ class Param(FieldInfo): # type: ignore[misc]
if example is not _Unset:
warnings.warn(
"`example` has been deprecated, please use `examples` instead",
category=DeprecationWarning,
category=FastAPIDeprecationWarning,
stacklevel=4,
)
self.example = example
@ -105,7 +106,7 @@ class Param(FieldInfo): # type: ignore[misc]
if regex is not None:
warnings.warn(
"`regex` has been deprecated, please use `pattern` instead",
category=DeprecationWarning,
category=FastAPIDeprecationWarning,
stacklevel=4,
)
current_json_schema_extra = json_schema_extra or extra
@ -530,7 +531,7 @@ class Body(FieldInfo): # type: ignore[misc]
if example is not _Unset:
warnings.warn(
"`example` has been deprecated, please use `examples` instead",
category=DeprecationWarning,
category=FastAPIDeprecationWarning,
stacklevel=4,
)
self.example = example
@ -560,7 +561,7 @@ class Body(FieldInfo): # type: ignore[misc]
if regex is not None:
warnings.warn(
"`regex` has been deprecated, please use `pattern` instead",
category=DeprecationWarning,
category=FastAPIDeprecationWarning,
stacklevel=4,
)
current_json_schema_extra = json_schema_extra or extra

View File

@ -47,6 +47,7 @@ from fastapi.dependencies.utils import (
from fastapi.encoders import jsonable_encoder
from fastapi.exceptions import (
EndpointContext,
FastAPIDeprecationWarning,
FastAPIError,
RequestValidationError,
ResponseValidationError,
@ -640,7 +641,7 @@ class APIRoute(routing.Route):
warnings.warn(
"pydantic.v1 is deprecated and will soon stop being supported by FastAPI."
f" Please update the response model {self.response_model!r}.",
category=DeprecationWarning,
category=FastAPIDeprecationWarning,
stacklevel=4,
)
self.response_field = create_model_field(
@ -680,7 +681,7 @@ class APIRoute(routing.Route):
warnings.warn(
"pydantic.v1 is deprecated and will soon stop being supported by FastAPI."
f" In responses={{}}, please update {model}.",
category=DeprecationWarning,
category=FastAPIDeprecationWarning,
stacklevel=4,
)
response_field = create_model_field(

View File

@ -1,6 +1,7 @@
import warnings
from typing import Annotated, Any, Callable, Optional, Union
from fastapi.exceptions import FastAPIDeprecationWarning
from fastapi.openapi.models import Example
from fastapi.params import ParamTypes
from typing_extensions import deprecated
@ -63,7 +64,7 @@ class Param(FieldInfo):
if example is not _Unset:
warnings.warn(
"`example` has been deprecated, please use `examples` instead",
category=DeprecationWarning,
category=FastAPIDeprecationWarning,
stacklevel=4,
)
self.example = example
@ -93,7 +94,7 @@ class Param(FieldInfo):
if regex is not None:
warnings.warn(
"`regex` has been deprecated, please use `pattern` instead",
category=DeprecationWarning,
category=FastAPIDeprecationWarning,
stacklevel=4,
)
current_json_schema_extra = json_schema_extra or extra
@ -503,7 +504,7 @@ class Body(FieldInfo):
if example is not _Unset:
warnings.warn(
"`example` has been deprecated, please use `examples` instead",
category=DeprecationWarning,
category=FastAPIDeprecationWarning,
stacklevel=4,
)
self.example = example
@ -533,7 +534,7 @@ class Body(FieldInfo):
if regex is not None:
warnings.warn(
"`regex` has been deprecated, please use `pattern` instead",
category=DeprecationWarning,
category=FastAPIDeprecationWarning,
stacklevel=4,
)
current_json_schema_extra = json_schema_extra or extra

View File

@ -23,6 +23,7 @@ from fastapi._compat import (
may_v1,
)
from fastapi.datastructures import DefaultPlaceholder, DefaultType
from fastapi.exceptions import FastAPIDeprecationWarning
from pydantic import BaseModel
from pydantic.fields import FieldInfo
from typing_extensions import Literal
@ -195,9 +196,9 @@ def generate_operation_id_for_path(
*, name: str, path: str, method: str
) -> str: # pragma: nocover
warnings.warn(
"fastapi.utils.generate_operation_id_for_path() was deprecated, "
message="fastapi.utils.generate_operation_id_for_path() was deprecated, "
"it is not used internally, and will be removed soon",
DeprecationWarning,
category=FastAPIDeprecationWarning,
stacklevel=2,
)
operation_id = f"{name}{path}"

View File

@ -6,6 +6,7 @@ from typing import Annotated, Any
import pytest
from fastapi import Depends, FastAPI
from fastapi.exceptions import FastAPIDeprecationWarning
from fastapi.testclient import TestClient
if "--codspeed" not in sys.argv:
@ -89,7 +90,7 @@ def app(basemodel_class: type[Any]) -> FastAPI:
warnings.filterwarnings(
"ignore",
message=r"pydantic\.v1 is deprecated and will soon stop being supported by FastAPI\..*",
category=DeprecationWarning,
category=FastAPIDeprecationWarning,
)
@app.post("/sync/validated", response_model=ItemOut)

View File

@ -3,6 +3,7 @@ import warnings
from typing import Optional
import pytest
from fastapi.exceptions import FastAPIDeprecationWarning
from tests.utils import skip_module_if_py_gte_314
@ -504,23 +505,23 @@ def test_body_repr():
# Deprecation warning tests for regex parameter
def test_query_regex_deprecation_warning():
with pytest.warns(DeprecationWarning, match="`regex` has been deprecated"):
with pytest.warns(FastAPIDeprecationWarning, match="`regex` has been deprecated"):
Query(regex="^test$")
def test_body_regex_deprecation_warning():
with pytest.warns(DeprecationWarning, match="`regex` has been deprecated"):
with pytest.warns(FastAPIDeprecationWarning, match="`regex` has been deprecated"):
Body(regex="^test$")
# Deprecation warning tests for example parameter
def test_query_example_deprecation_warning():
with pytest.warns(DeprecationWarning, match="`example` has been deprecated"):
with pytest.warns(FastAPIDeprecationWarning, match="`example` has been deprecated"):
Query(example="test example")
def test_body_example_deprecation_warning():
with pytest.warns(DeprecationWarning, match="`example` has been deprecated"):
with pytest.warns(FastAPIDeprecationWarning, match="`example` has been deprecated"):
Body(example={"test": "example"})

View File

@ -1,6 +1,7 @@
import sys
import pytest
from fastapi.exceptions import FastAPIDeprecationWarning
from tests.utils import skip_module_if_py_gte_314
@ -19,7 +20,7 @@ def test_warns_pydantic_v1_model_in_endpoint_param() -> None:
app = FastAPI()
with pytest.warns(
DeprecationWarning,
FastAPIDeprecationWarning,
match=r"pydantic\.v1 is deprecated.*Please update the param data:",
):
@ -40,7 +41,7 @@ def test_warns_pydantic_v1_model_in_return_type() -> None:
app = FastAPI()
with pytest.warns(
DeprecationWarning,
FastAPIDeprecationWarning,
match=r"pydantic\.v1 is deprecated.*Please update the response model",
):
@ -61,7 +62,7 @@ def test_warns_pydantic_v1_model_in_response_model() -> None:
app = FastAPI()
with pytest.warns(
DeprecationWarning,
FastAPIDeprecationWarning,
match=r"pydantic\.v1 is deprecated.*Please update the response model",
):
@ -82,7 +83,7 @@ def test_warns_pydantic_v1_model_in_additional_responses_model() -> None:
app = FastAPI()
with pytest.warns(
DeprecationWarning,
FastAPIDeprecationWarning,
match=r"pydantic\.v1 is deprecated.*In responses=\{\}, please update",
):

View File

@ -3,6 +3,7 @@ from typing import Annotated
import pytest
from dirty_equals import IsDict
from fastapi import FastAPI, Form
from fastapi.exceptions import FastAPIDeprecationWarning
from fastapi.testclient import TestClient
from .utils import needs_py310
@ -10,7 +11,7 @@ from .utils import needs_py310
def get_client():
app = FastAPI()
with pytest.warns(DeprecationWarning):
with pytest.warns(FastAPIDeprecationWarning):
@app.post("/items/")
async def read_items(

View File

@ -3,6 +3,7 @@ from typing import Annotated
import pytest
from dirty_equals import IsDict
from fastapi import FastAPI, Query
from fastapi.exceptions import FastAPIDeprecationWarning
from fastapi.testclient import TestClient
from .utils import needs_py310
@ -10,7 +11,7 @@ from .utils import needs_py310
def get_client():
app = FastAPI()
with pytest.warns(DeprecationWarning):
with pytest.warns(FastAPIDeprecationWarning):
@app.get("/items/")
async def read_items(

View File

@ -3,6 +3,7 @@ from typing import Union
import pytest
from dirty_equals import IsDict
from fastapi import Body, Cookie, FastAPI, Header, Path, Query
from fastapi.exceptions import FastAPIDeprecationWarning
from fastapi.testclient import TestClient
from pydantic import BaseModel, ConfigDict
@ -21,7 +22,7 @@ def create_app():
def schema_extra(item: Item):
return item
with pytest.warns(DeprecationWarning):
with pytest.warns(FastAPIDeprecationWarning):
@app.post("/example/")
def example(item: Item = Body(example={"data": "Data in Body example"})):
@ -38,7 +39,7 @@ def create_app():
):
return item
with pytest.warns(DeprecationWarning):
with pytest.warns(FastAPIDeprecationWarning):
@app.post("/example_examples/")
def example_examples(
@ -83,7 +84,7 @@ def create_app():
# ):
# return lastname
with pytest.warns(DeprecationWarning):
with pytest.warns(FastAPIDeprecationWarning):
@app.get("/path_example/{item_id}")
def path_example(
@ -101,7 +102,7 @@ def create_app():
):
return item_id
with pytest.warns(DeprecationWarning):
with pytest.warns(FastAPIDeprecationWarning):
@app.get("/path_example_examples/{item_id}")
def path_example_examples(
@ -112,7 +113,7 @@ def create_app():
):
return item_id
with pytest.warns(DeprecationWarning):
with pytest.warns(FastAPIDeprecationWarning):
@app.get("/query_example/")
def query_example(
@ -132,7 +133,7 @@ def create_app():
):
return data
with pytest.warns(DeprecationWarning):
with pytest.warns(FastAPIDeprecationWarning):
@app.get("/query_example_examples/")
def query_example_examples(
@ -144,7 +145,7 @@ def create_app():
):
return data
with pytest.warns(DeprecationWarning):
with pytest.warns(FastAPIDeprecationWarning):
@app.get("/header_example/")
def header_example(
@ -167,7 +168,7 @@ def create_app():
):
return data
with pytest.warns(DeprecationWarning):
with pytest.warns(FastAPIDeprecationWarning):
@app.get("/header_example_examples/")
def header_example_examples(
@ -179,7 +180,7 @@ def create_app():
):
return data
with pytest.warns(DeprecationWarning):
with pytest.warns(FastAPIDeprecationWarning):
@app.get("/cookie_example/")
def cookie_example(
@ -199,7 +200,7 @@ def create_app():
):
return data
with pytest.warns(DeprecationWarning):
with pytest.warns(FastAPIDeprecationWarning):
@app.get("/cookie_example_examples/")
def cookie_example_examples(

View File

@ -2,6 +2,7 @@ import sys
import warnings
import pytest
from fastapi.exceptions import FastAPIDeprecationWarning
from inline_snapshot import snapshot
from tests.utils import skip_module_if_py_gte_314
@ -29,7 +30,7 @@ def get_client(request: pytest.FixtureRequest):
warnings.filterwarnings(
"ignore",
message=r"pydantic\.v1 is deprecated and will soon stop being supported by FastAPI\..*",
category=DeprecationWarning,
category=FastAPIDeprecationWarning,
)
mod = importlib.import_module(f"docs_src.pydantic_v1_in_v2.{request.param}")

View File

@ -2,6 +2,7 @@ import sys
import warnings
import pytest
from fastapi.exceptions import FastAPIDeprecationWarning
from inline_snapshot import snapshot
from tests.utils import skip_module_if_py_gte_314
@ -29,7 +30,7 @@ def get_client(request: pytest.FixtureRequest):
warnings.filterwarnings(
"ignore",
message=r"pydantic\.v1 is deprecated and will soon stop being supported by FastAPI\..*",
category=DeprecationWarning,
category=FastAPIDeprecationWarning,
)
mod = importlib.import_module(f"docs_src.pydantic_v1_in_v2.{request.param}")

View File

@ -2,6 +2,7 @@ import sys
import warnings
import pytest
from fastapi.exceptions import FastAPIDeprecationWarning
from inline_snapshot import snapshot
from tests.utils import skip_module_if_py_gte_314
@ -29,7 +30,7 @@ def get_client(request: pytest.FixtureRequest):
warnings.filterwarnings(
"ignore",
message=r"pydantic\.v1 is deprecated and will soon stop being supported by FastAPI\..*",
category=DeprecationWarning,
category=FastAPIDeprecationWarning,
)
mod = importlib.import_module(f"docs_src.pydantic_v1_in_v2.{request.param}")

View File

@ -18,7 +18,7 @@ from ...utils import needs_py310
marks=(
needs_py310,
pytest.mark.filterwarnings(
"ignore:`regex` has been deprecated, please use `pattern` instead:DeprecationWarning"
"ignore:`regex` has been deprecated, please use `pattern` instead:fastapi.exceptions.FastAPIDeprecationWarning"
),
),
),

View File

@ -2,6 +2,7 @@ import importlib
import warnings
import pytest
from fastapi.exceptions import FastAPIDeprecationWarning
from fastapi.testclient import TestClient
from ...utils import needs_pydanticv1
@ -19,7 +20,7 @@ def get_client(request: pytest.FixtureRequest):
warnings.filterwarnings(
"ignore",
message=r"pydantic\.v1 is deprecated and will soon stop being supported by FastAPI\..*",
category=DeprecationWarning,
category=FastAPIDeprecationWarning,
)
mod = importlib.import_module(f"docs_src.request_form_models.{request.param}")

View File

@ -2,6 +2,7 @@ import importlib
import warnings
import pytest
from fastapi.exceptions import FastAPIDeprecationWarning
from fastapi.testclient import TestClient
from inline_snapshot import snapshot
@ -20,7 +21,7 @@ def get_client(request: pytest.FixtureRequest):
warnings.filterwarnings(
"ignore",
message=r"pydantic\.v1 is deprecated and will soon stop being supported by FastAPI\..*",
category=DeprecationWarning,
category=FastAPIDeprecationWarning,
)
mod = importlib.import_module(f"docs_src.schema_extra_example.{request.param}")