[packages]
starlette = "==0.12.9"
-pydantic = "==0.32.2"
+pydantic = "==1.0.0"
databases = {extras = ["sqlite"],version = "*"}
hypercorn = "*"
orjson = "*"
from fastapi import Body, FastAPI
-from pydantic import BaseModel, Schema
+from pydantic import BaseModel, Field
app = FastAPI()
class Item(BaseModel):
name: str
- description: str = Schema(None, title="The description of the item", max_length=300)
- price: float = Schema(..., gt=0, description="The price must be greater than zero")
+ description: str = Field(None, title="The description of the item", max_length=300)
+ price: float = Field(..., gt=0, description="The price must be greater than zero")
tax: float = None
async def update_item(item_id: str, item: Item):
stored_item_data = items[item_id]
stored_item_model = Item(**stored_item_data)
- update_data = item.dict(skip_defaults=True)
+ update_data = item.dict(exclude_unset=True)
updated_item = stored_item_model.copy(update=update_data)
items[item_id] = jsonable_encoder(updated_item)
return updated_item
from fastapi import FastAPI
-from pydantic import BaseModel
-from pydantic.types import EmailStr
+from pydantic import BaseModel, EmailStr
app = FastAPI()
from fastapi import FastAPI
-from pydantic import BaseModel
-from pydantic.types import EmailStr
+from pydantic import BaseModel, EmailStr
app = FastAPI()
from fastapi import FastAPI
-from pydantic import BaseModel
-from pydantic.types import EmailStr
+from pydantic import BaseModel, EmailStr
app = FastAPI()
from fastapi import FastAPI
-from pydantic import BaseModel
-from pydantic.types import EmailStr
+from pydantic import BaseModel, EmailStr
app = FastAPI()
}
-@app.get("/items/{item_id}", response_model=Item, response_model_skip_defaults=True)
+@app.get("/items/{item_id}", response_model=Item, response_model_exclude_unset=True)
async def read_item(item_id: str):
return items[item_id]
-The same way you can declare additional validation and metadata in path operation function parameters with `Query`, `Path` and `Body`, you can declare validation and metadata inside of Pydantic models using `Schema`.
+The same way you can declare additional validation and metadata in path operation function parameters with `Query`, `Path` and `Body`, you can declare validation and metadata inside of Pydantic models using Pydantic's `Field`.
-## Import Schema
+## Import `Field`
First, you have to import it:
```
!!! warning
- Notice that `Schema` is imported directly from `pydantic`, not from `fastapi` as are all the rest (`Query`, `Path`, `Body`, etc).
+ Notice that `Field` is imported directly from `pydantic`, not from `fastapi` as are all the rest (`Query`, `Path`, `Body`, etc).
## Declare model attributes
-You can then use `Schema` with model attributes:
+You can then use `Field` with model attributes:
```Python hl_lines="9 10"
{!./src/body_schema/tutorial001.py!}
```
-`Schema` works the same way as `Query`, `Path` and `Body`, it has all the same parameters, etc.
+`Field` works the same way as `Query`, `Path` and `Body`, it has all the same parameters, etc.
!!! note "Technical Details"
- Actually, `Query`, `Path` and others you'll see next are subclasses of a common `Param` which is itself a subclass of Pydantic's `Schema`.
+ Actually, `Query`, `Path` and others you'll see next create objects of subclasses of a common `Param` class, which is itself a subclass of Pydantic's `FieldInfo` class.
- `Body` is also a subclass of `Schema` directly. And there are others you will see later that are subclasses of `Body`.
+ And Pydantic's `Field` returns an instance of `FieldInfo` as well.
- But remember that when you import `Query`, `Path` and others from `fastapi`, <a href="https://fastapi.tiangolo.com/tutorial/path-params-numeric-validations/#recap" target="_blank">those are actually functions that return classes of the same name</a>.
+ `Body` also returns objects of a subclass of `FieldInfo` directly. And there are others you will see later that are subclasses of the `Body` class.
+
+ Remember that when you import `Query`, `Path`, and others from `fastapi`, <a href="https://fastapi.tiangolo.com/tutorial/path-params-numeric-validations/#recap" target="_blank">those are actually functions that return classes of the same name</a>.
!!! tip
- Notice how each model's attribute with a type, default value and `Schema` has the same structure as a path operation function's parameter, with `Schema` instead of `Path`, `Query` and `Body`.
+ Notice how each model's attribute with a type, default value and `Field` has the same structure as a path operation function's parameter, with `Field` instead of `Path`, `Query` and `Body`.
## Schema extras
-In `Schema`, `Path`, `Query`, `Body` and others you'll see later, you can declare extra parameters apart from those described before.
+In `Field`, `Path`, `Query`, `Body` and others you'll see later, you can declare extra parameters apart from those described before.
Those parameters will be added as-is to the output JSON Schema.
## Recap
-You can use Pydantic's `Schema` to declare extra validations and metadata for model attributes.
+You can use Pydantic's `Field` to declare extra validations and metadata for model attributes.
-You can also use the extra keyword arguments to pass additional JSON Schema metadata.
\ No newline at end of file
+You can also use the extra keyword arguments to pass additional JSON Schema metadata.
But this guide shows you, more or less, how they are intended to be used.
-### Using Pydantic's `skip_defaults` parameter
+### Using Pydantic's `exclude_unset` parameter
-If you want to receive partial updates, it's very useful to use the parameter `skip_defaults` in Pydantic's model's `.dict()`.
+If you want to receive partial updates, it's very useful to use the parameter `exclude_unset` in Pydantic's model's `.dict()`.
-Like `item.dict(skip_defaults=True)`.
+Like `item.dict(exclude_unset=True)`.
That would generate a `dict` with only the data that was set when creating the `item` model, excluding default values.
-Then you can use this to generate a `dict` with only the data that was set, omitting default values:
+Then you can use this to generate a `dict` with only the data that was set (sent in the request), omitting default values:
```Python hl_lines="34"
{!./src/body_updates/tutorial002.py!}
* (Optionally) use `PATCH` instead of `PUT`.
* Retrieve the stored data.
* Put that data in a Pydantic model.
-* Generate a `dict` without default values from the input model (using `skip_defaults`).
+* Generate a `dict` without default values from the input model (using `exclude_unset`).
* This way you can update only the values actually set by the user, instead of overriding values already stored with default values in your model.
* Create a copy of the stored model, updating it's attributes with the received partial updates (using the `update` parameter).
* Convert the copied model to something that can be stored in your DB (for example, using the `jsonable_encoder`).
Here's a general idea of how the models could look like with their password fields and the places where they are used:
-```Python hl_lines="8 10 15 21 23 32 34 39 40"
+```Python hl_lines="7 9 14 20 22 27 28 31 32 33 38 39"
{!./src/extra_models/tutorial001.py!}
```
That way, we can declare just the differences between the models (with plaintext `password`, with `hashed_password` and without password):
-```Python hl_lines="8 14 15 18 19 22 23"
+```Python hl_lines="7 13 14 17 18 21 22"
{!./src/extra_models/tutorial002.py!}
```
Here we are declaring a `UserIn` model, it will contain a plaintext password:
-```Python hl_lines="8 10"
+```Python hl_lines="7 9"
{!./src/response_model/tutorial002.py!}
```
And we are using this model to declare our input and the same model to declare our output:
-```Python hl_lines="16 17"
+```Python hl_lines="15 16"
{!./src/response_model/tutorial002.py!}
```
We can instead create an input model with the plaintext password and an output model without it:
-```Python hl_lines="8 10 15"
+```Python hl_lines="7 9 14"
{!./src/response_model/tutorial003.py!}
```
Here, even though our path operation function is returning the same input user that contains the password:
-```Python hl_lines="23"
+```Python hl_lines="22"
{!./src/response_model/tutorial003.py!}
```
...we declared the `response_model` to be our model `UserOut`, that doesn't include the password:
-```Python hl_lines="21"
+```Python hl_lines="20"
{!./src/response_model/tutorial003.py!}
```
For example, if you have models with many optional attributes in a NoSQL database, but you don't want to send very long JSON responses full of default values.
-### Use the `response_model_skip_defaults` parameter
+### Use the `response_model_exclude_unset` parameter
-You can set the *path operation decorator* parameter `response_model_skip_defaults=True`:
+You can set the *path operation decorator* parameter `response_model_exclude_unset=True`:
```Python hl_lines="24"
{!./src/response_model/tutorial004.py!}
```
-and those default values won't be included in the response.
+and those default values won't be included in the response, only the values actually set.
So, if you send a request to that *path operation* for the item with ID `foo`, the response (not including default values) will be:
```
!!! info
- FastAPI uses Pydantic model's `.dict()` with <a href="https://pydantic-docs.helpmanual.io/#copying" target="_blank">its `skip_defaults` parameter</a> to achieve this.
+ FastAPI uses Pydantic model's `.dict()` with <a href="https://pydantic-docs.helpmanual.io/usage/exporting_models/#modeldict" target="_blank">its `exclude_unset` parameter</a> to achieve this.
#### Data with values for fields with defaults
Use the path operation decorator's parameter `response_model` to define response models and especially to ensure private data is filtered out.
-Use `response_model_skip_defaults` to return only the values explicitly set.
+Use `response_model_exclude_unset` to return only the values explicitly set.
)
from fastapi.openapi.utils import get_openapi
from fastapi.params import Depends
+from fastapi.utils import warning_response_model_skip_defaults_deprecated
from starlette.applications import Starlette
from starlette.datastructures import State
from starlette.exceptions import ExceptionMiddleware, HTTPException
response_model_include: Union[SetIntStr, DictIntStrAny] = None,
response_model_exclude: Union[SetIntStr, DictIntStrAny] = set(),
response_model_by_alias: bool = True,
- response_model_skip_defaults: bool = False,
+ response_model_skip_defaults: bool = None,
+ response_model_exclude_unset: bool = False,
include_in_schema: bool = True,
response_class: Type[Response] = None,
name: str = None,
) -> None:
+ if response_model_skip_defaults is not None:
+ warning_response_model_skip_defaults_deprecated() # pragma: nocover
self.router.add_api_route(
path,
endpoint=endpoint,
response_model_include=response_model_include,
response_model_exclude=response_model_exclude,
response_model_by_alias=response_model_by_alias,
- response_model_skip_defaults=response_model_skip_defaults,
+ response_model_exclude_unset=bool(
+ response_model_exclude_unset or response_model_skip_defaults
+ ),
include_in_schema=include_in_schema,
response_class=response_class or self.default_response_class,
name=name,
response_model_include: Union[SetIntStr, DictIntStrAny] = None,
response_model_exclude: Union[SetIntStr, DictIntStrAny] = set(),
response_model_by_alias: bool = True,
- response_model_skip_defaults: bool = False,
+ response_model_skip_defaults: bool = None,
+ response_model_exclude_unset: bool = False,
include_in_schema: bool = True,
response_class: Type[Response] = None,
name: str = None,
) -> Callable:
+ if response_model_skip_defaults is not None:
+ warning_response_model_skip_defaults_deprecated() # pragma: nocover
+
def decorator(func: Callable) -> Callable:
self.router.add_api_route(
path,
response_model_include=response_model_include,
response_model_exclude=response_model_exclude,
response_model_by_alias=response_model_by_alias,
- response_model_skip_defaults=response_model_skip_defaults,
+ response_model_exclude_unset=bool(
+ response_model_exclude_unset or response_model_skip_defaults
+ ),
include_in_schema=include_in_schema,
response_class=response_class or self.default_response_class,
name=name,
response_model_include: Union[SetIntStr, DictIntStrAny] = None,
response_model_exclude: Union[SetIntStr, DictIntStrAny] = set(),
response_model_by_alias: bool = True,
- response_model_skip_defaults: bool = False,
+ response_model_skip_defaults: bool = None,
+ response_model_exclude_unset: bool = False,
include_in_schema: bool = True,
response_class: Type[Response] = None,
name: str = None,
) -> Callable:
+ if response_model_skip_defaults is not None:
+ warning_response_model_skip_defaults_deprecated() # pragma: nocover
return self.router.get(
path,
response_model=response_model,
response_model_include=response_model_include,
response_model_exclude=response_model_exclude,
response_model_by_alias=response_model_by_alias,
- response_model_skip_defaults=response_model_skip_defaults,
+ response_model_exclude_unset=bool(
+ response_model_exclude_unset or response_model_skip_defaults
+ ),
include_in_schema=include_in_schema,
response_class=response_class or self.default_response_class,
name=name,
response_model_include: Union[SetIntStr, DictIntStrAny] = None,
response_model_exclude: Union[SetIntStr, DictIntStrAny] = set(),
response_model_by_alias: bool = True,
- response_model_skip_defaults: bool = False,
+ response_model_skip_defaults: bool = None,
+ response_model_exclude_unset: bool = False,
include_in_schema: bool = True,
response_class: Type[Response] = None,
name: str = None,
) -> Callable:
+ if response_model_skip_defaults is not None:
+ warning_response_model_skip_defaults_deprecated() # pragma: nocover
return self.router.put(
path,
response_model=response_model,
response_model_include=response_model_include,
response_model_exclude=response_model_exclude,
response_model_by_alias=response_model_by_alias,
- response_model_skip_defaults=response_model_skip_defaults,
+ response_model_exclude_unset=bool(
+ response_model_exclude_unset or response_model_skip_defaults
+ ),
include_in_schema=include_in_schema,
response_class=response_class or self.default_response_class,
name=name,
response_model_include: Union[SetIntStr, DictIntStrAny] = None,
response_model_exclude: Union[SetIntStr, DictIntStrAny] = set(),
response_model_by_alias: bool = True,
- response_model_skip_defaults: bool = False,
+ response_model_skip_defaults: bool = None,
+ response_model_exclude_unset: bool = False,
include_in_schema: bool = True,
response_class: Type[Response] = None,
name: str = None,
) -> Callable:
+ if response_model_skip_defaults is not None:
+ warning_response_model_skip_defaults_deprecated() # pragma: nocover
return self.router.post(
path,
response_model=response_model,
response_model_include=response_model_include,
response_model_exclude=response_model_exclude,
response_model_by_alias=response_model_by_alias,
- response_model_skip_defaults=response_model_skip_defaults,
+ response_model_exclude_unset=bool(
+ response_model_exclude_unset or response_model_skip_defaults
+ ),
include_in_schema=include_in_schema,
response_class=response_class or self.default_response_class,
name=name,
response_model_include: Union[SetIntStr, DictIntStrAny] = None,
response_model_exclude: Union[SetIntStr, DictIntStrAny] = set(),
response_model_by_alias: bool = True,
- response_model_skip_defaults: bool = False,
+ response_model_skip_defaults: bool = None,
+ response_model_exclude_unset: bool = False,
include_in_schema: bool = True,
response_class: Type[Response] = None,
name: str = None,
) -> Callable:
+ if response_model_skip_defaults is not None:
+ warning_response_model_skip_defaults_deprecated() # pragma: nocover
return self.router.delete(
path,
response_model=response_model,
response_model_exclude=response_model_exclude,
response_model_by_alias=response_model_by_alias,
operation_id=operation_id,
- response_model_skip_defaults=response_model_skip_defaults,
+ response_model_exclude_unset=bool(
+ response_model_exclude_unset or response_model_skip_defaults
+ ),
include_in_schema=include_in_schema,
response_class=response_class or self.default_response_class,
name=name,
response_model_include: Union[SetIntStr, DictIntStrAny] = None,
response_model_exclude: Union[SetIntStr, DictIntStrAny] = set(),
response_model_by_alias: bool = True,
- response_model_skip_defaults: bool = False,
+ response_model_skip_defaults: bool = None,
+ response_model_exclude_unset: bool = False,
include_in_schema: bool = True,
response_class: Type[Response] = None,
name: str = None,
) -> Callable:
+ if response_model_skip_defaults is not None:
+ warning_response_model_skip_defaults_deprecated() # pragma: nocover
return self.router.options(
path,
response_model=response_model,
response_model_include=response_model_include,
response_model_exclude=response_model_exclude,
response_model_by_alias=response_model_by_alias,
- response_model_skip_defaults=response_model_skip_defaults,
+ response_model_exclude_unset=bool(
+ response_model_exclude_unset or response_model_skip_defaults
+ ),
include_in_schema=include_in_schema,
response_class=response_class or self.default_response_class,
name=name,
response_model_include: Union[SetIntStr, DictIntStrAny] = None,
response_model_exclude: Union[SetIntStr, DictIntStrAny] = set(),
response_model_by_alias: bool = True,
- response_model_skip_defaults: bool = False,
+ response_model_skip_defaults: bool = None,
+ response_model_exclude_unset: bool = False,
include_in_schema: bool = True,
response_class: Type[Response] = None,
name: str = None,
) -> Callable:
+ if response_model_skip_defaults is not None:
+ warning_response_model_skip_defaults_deprecated() # pragma: nocover
return self.router.head(
path,
response_model=response_model,
response_model_include=response_model_include,
response_model_exclude=response_model_exclude,
response_model_by_alias=response_model_by_alias,
- response_model_skip_defaults=response_model_skip_defaults,
+ response_model_exclude_unset=bool(
+ response_model_exclude_unset or response_model_skip_defaults
+ ),
include_in_schema=include_in_schema,
response_class=response_class or self.default_response_class,
name=name,
response_model_include: Union[SetIntStr, DictIntStrAny] = None,
response_model_exclude: Union[SetIntStr, DictIntStrAny] = set(),
response_model_by_alias: bool = True,
- response_model_skip_defaults: bool = False,
+ response_model_skip_defaults: bool = None,
+ response_model_exclude_unset: bool = False,
include_in_schema: bool = True,
response_class: Type[Response] = None,
name: str = None,
) -> Callable:
+ if response_model_skip_defaults is not None:
+ warning_response_model_skip_defaults_deprecated() # pragma: nocover
return self.router.patch(
path,
response_model=response_model,
response_model_include=response_model_include,
response_model_exclude=response_model_exclude,
response_model_by_alias=response_model_by_alias,
- response_model_skip_defaults=response_model_skip_defaults,
+ response_model_exclude_unset=bool(
+ response_model_exclude_unset or response_model_skip_defaults
+ ),
include_in_schema=include_in_schema,
response_class=response_class or self.default_response_class,
name=name,
response_model_include: Union[SetIntStr, DictIntStrAny] = None,
response_model_exclude: Union[SetIntStr, DictIntStrAny] = set(),
response_model_by_alias: bool = True,
- response_model_skip_defaults: bool = False,
+ response_model_skip_defaults: bool = None,
+ response_model_exclude_unset: bool = False,
include_in_schema: bool = True,
response_class: Type[Response] = None,
name: str = None,
) -> Callable:
+ if response_model_skip_defaults is not None:
+ warning_response_model_skip_defaults_deprecated() # pragma: nocover
return self.router.trace(
path,
response_model=response_model,
response_model_include=response_model_include,
response_model_exclude=response_model_exclude,
response_model_by_alias=response_model_by_alias,
- response_model_skip_defaults=response_model_skip_defaults,
+ response_model_exclude_unset=bool(
+ response_model_exclude_unset or response_model_skip_defaults
+ ),
include_in_schema=include_in_schema,
response_class=response_class or self.default_response_class,
name=name,
from typing import Any, Callable
-from starlette.concurrency import iterate_in_threadpool, run_in_threadpool # noqa
+from starlette.concurrency import iterate_in_threadpool # noqa
+from starlette.concurrency import run_in_threadpool
asynccontextmanager_error_message = """
FastAPI's contextmanager_in_threadpool require Python 3.7 or above,
from typing import Callable, List, Sequence
from fastapi.security.base import SecurityBase
-from pydantic.fields import Field
+
+try:
+ from pydantic.fields import ModelField
+except ImportError: # pragma: nocover
+ # TODO: remove when removing support for Pydantic < 1.0.0
+ from pydantic.fields import Field as ModelField # type: ignore
param_supported_types = (str, int, float, bool)
def __init__(
self,
*,
- path_params: List[Field] = None,
- query_params: List[Field] = None,
- header_params: List[Field] = None,
- cookie_params: List[Field] = None,
- body_params: List[Field] = None,
+ path_params: List[ModelField] = None,
+ query_params: List[ModelField] = None,
+ header_params: List[ModelField] = None,
+ cookie_params: List[ModelField] = None,
+ body_params: List[ModelField] = None,
dependencies: List["Dependant"] = None,
security_schemes: List[SecurityRequirement] = None,
name: str = None,
from fastapi.security.base import SecurityBase
from fastapi.security.oauth2 import OAuth2, SecurityScopes
from fastapi.security.open_id_connect_url import OpenIdConnect
-from fastapi.utils import get_path_param_names
-from pydantic import BaseConfig, BaseModel, Schema, create_model
+from fastapi.utils import PYDANTIC_1, get_field_info, get_path_param_names
+from pydantic import BaseConfig, BaseModel, create_model
from pydantic.error_wrappers import ErrorWrapper
from pydantic.errors import MissingError
-from pydantic.fields import Field, Required, Shape
-from pydantic.schema import get_annotation_from_schema
-from pydantic.utils import ForwardRef, evaluate_forwardref, lenient_issubclass
+from pydantic.utils import lenient_issubclass
from starlette.background import BackgroundTasks
from starlette.concurrency import run_in_threadpool
from starlette.datastructures import FormData, Headers, QueryParams, UploadFile
from starlette.responses import Response
from starlette.websockets import WebSocket
+try:
+ from pydantic.fields import (
+ SHAPE_LIST,
+ SHAPE_SEQUENCE,
+ SHAPE_SET,
+ SHAPE_SINGLETON,
+ SHAPE_TUPLE,
+ SHAPE_TUPLE_ELLIPSIS,
+ FieldInfo,
+ ModelField,
+ Required,
+ )
+ from pydantic.schema import get_annotation_from_field_info
+ from pydantic.typing import ForwardRef, evaluate_forwardref
+except ImportError: # pragma: nocover
+ # TODO: remove when removing support for Pydantic < 1.0.0
+ from pydantic.fields import Field as ModelField # type: ignore
+ from pydantic.fields import Required, Shape # type: ignore
+ from pydantic import Schema as FieldInfo # type: ignore
+ from pydantic.schema import get_annotation_from_schema # type: ignore
+ from pydantic.utils import ForwardRef, evaluate_forwardref # type: ignore
+
+ SHAPE_LIST = Shape.LIST
+ SHAPE_SEQUENCE = Shape.SEQUENCE
+ SHAPE_SET = Shape.SET
+ SHAPE_SINGLETON = Shape.SINGLETON
+ SHAPE_TUPLE = Shape.TUPLE
+ SHAPE_TUPLE_ELLIPSIS = Shape.TUPLE_ELLIPS
+
+ def get_annotation_from_field_info(
+ annotation: Any, field_info: FieldInfo, field_name: str
+ ) -> Type[Any]:
+ return get_annotation_from_schema(annotation, field_info)
+
+
sequence_shapes = {
- Shape.LIST,
- Shape.SET,
- Shape.TUPLE,
- Shape.SEQUENCE,
- Shape.TUPLE_ELLIPS,
+ SHAPE_LIST,
+ SHAPE_SET,
+ SHAPE_TUPLE,
+ SHAPE_SEQUENCE,
+ SHAPE_TUPLE_ELLIPSIS,
}
sequence_types = (list, set, tuple)
sequence_shape_to_type = {
- Shape.LIST: list,
- Shape.SET: set,
- Shape.TUPLE: tuple,
- Shape.SEQUENCE: list,
- Shape.TUPLE_ELLIPS: list,
+ SHAPE_LIST: list,
+ SHAPE_SET: set,
+ SHAPE_TUPLE: tuple,
+ SHAPE_SEQUENCE: list,
+ SHAPE_TUPLE_ELLIPSIS: list,
}
return flat_dependant
-def is_scalar_field(field: Field) -> bool:
+def is_scalar_field(field: ModelField) -> bool:
+ field_info = get_field_info(field)
if not (
- field.shape == Shape.SINGLETON
+ field.shape == SHAPE_SINGLETON
and not lenient_issubclass(field.type_, BaseModel)
and not lenient_issubclass(field.type_, sequence_types + (dict,))
- and not isinstance(field.schema, params.Body)
+ and not isinstance(field_info, params.Body)
):
return False
if field.sub_fields:
return True
-def is_scalar_sequence_field(field: Field) -> bool:
+def is_scalar_sequence_field(field: ModelField) -> bool:
if (field.shape in sequence_shapes) and not lenient_issubclass(
field.type_, BaseModel
):
continue
if add_non_field_param_to_dependency(param=param, dependant=dependant):
continue
- param_field = get_param_field(param=param, default_schema=params.Query)
+ param_field = get_param_field(
+ param=param, default_field_info=params.Query, param_name=param_name
+ )
if param_name in path_param_names:
assert is_scalar_field(
field=param_field
ignore_default = True
param_field = get_param_field(
param=param,
- default_schema=params.Path,
+ param_name=param_name,
+ default_field_info=params.Path,
force_type=params.ParamTypes.path,
ignore_default=ignore_default,
)
) and is_scalar_sequence_field(param_field):
add_param_to_fields(field=param_field, dependant=dependant)
else:
+ field_info = get_field_info(param_field)
assert isinstance(
- param_field.schema, params.Body
+ field_info, params.Body
), f"Param: {param_field.name} can only be a request body, using Body(...)"
dependant.body_params.append(param_field)
return dependant
def get_param_field(
*,
param: inspect.Parameter,
- default_schema: Type[params.Param] = params.Param,
+ param_name: str,
+ default_field_info: Type[params.Param] = params.Param,
force_type: params.ParamTypes = None,
ignore_default: bool = False,
-) -> Field:
+) -> ModelField:
default_value = Required
had_schema = False
if not param.default == param.empty and ignore_default is False:
default_value = param.default
- if isinstance(default_value, Schema):
+ if isinstance(default_value, FieldInfo):
had_schema = True
- schema = default_value
- default_value = schema.default
- if isinstance(schema, params.Param) and getattr(schema, "in_", None) is None:
- schema.in_ = default_schema.in_
+ field_info = default_value
+ default_value = field_info.default
+ if (
+ isinstance(field_info, params.Param)
+ and getattr(field_info, "in_", None) is None
+ ):
+ field_info.in_ = default_field_info.in_
if force_type:
- schema.in_ = force_type # type: ignore
+ field_info.in_ = force_type # type: ignore
else:
- schema = default_schema(default_value)
+ field_info = default_field_info(default_value)
required = default_value == Required
annotation: Any = Any
if not param.annotation == param.empty:
annotation = param.annotation
- annotation = get_annotation_from_schema(annotation, schema)
- if not schema.alias and getattr(schema, "convert_underscores", None):
+ annotation = get_annotation_from_field_info(annotation, field_info, param_name)
+ if not field_info.alias and getattr(field_info, "convert_underscores", None):
alias = param.name.replace("_", "-")
else:
- alias = schema.alias or param.name
- field = Field(
- name=param.name,
- type_=annotation,
- default=None if required else default_value,
- alias=alias,
- required=required,
- model_config=BaseConfig,
- class_validators={},
- schema=schema,
- )
+ alias = field_info.alias or param.name
+ if PYDANTIC_1:
+ field = ModelField(
+ name=param.name,
+ type_=annotation,
+ default=None if required else default_value,
+ alias=alias,
+ required=required,
+ model_config=BaseConfig,
+ class_validators={},
+ field_info=field_info,
+ )
+ # TODO: remove when removing support for Pydantic < 1.2.0
+ field.required = required
+ else: # pragma: nocover
+ field = ModelField( # type: ignore
+ name=param.name,
+ type_=annotation,
+ default=None if required else default_value,
+ alias=alias,
+ required=required,
+ model_config=BaseConfig,
+ class_validators={},
+ schema=field_info,
+ )
+ field.required = required
if not had_schema and not is_scalar_field(field=field):
- field.schema = params.Body(schema.default)
+ if PYDANTIC_1:
+ field.field_info = params.Body(field_info.default)
+ else:
+ field.schema = params.Body(field_info.default) # type: ignore # pragma: nocover
+
return field
-def add_param_to_fields(*, field: Field, dependant: Dependant) -> None:
- field.schema = cast(params.Param, field.schema)
- if field.schema.in_ == params.ParamTypes.path:
+def add_param_to_fields(*, field: ModelField, dependant: Dependant) -> None:
+ field_info = cast(params.Param, get_field_info(field))
+ if field_info.in_ == params.ParamTypes.path:
dependant.path_params.append(field)
- elif field.schema.in_ == params.ParamTypes.query:
+ elif field_info.in_ == params.ParamTypes.query:
dependant.query_params.append(field)
- elif field.schema.in_ == params.ParamTypes.header:
+ elif field_info.in_ == params.ParamTypes.header:
dependant.header_params.append(field)
else:
assert (
- field.schema.in_ == params.ParamTypes.cookie
+ field_info.in_ == params.ParamTypes.cookie
), f"non-body parameters must be in path, query, header or cookie: {field.name}"
dependant.cookie_params.append(field)
def request_params_to_args(
- required_params: Sequence[Field],
+ required_params: Sequence[ModelField],
received_params: Union[Mapping[str, Any], QueryParams, Headers],
) -> Tuple[Dict[str, Any], List[ErrorWrapper]]:
values = {}
value = received_params.getlist(field.alias) or field.default
else:
value = received_params.get(field.alias)
- schema = field.schema
- assert isinstance(schema, params.Param), "Params must be subclasses of Param"
+ field_info = get_field_info(field)
+ assert isinstance(
+ field_info, params.Param
+ ), "Params must be subclasses of Param"
if value is None:
if field.required:
- errors.append(
- ErrorWrapper(
- MissingError(),
- loc=(schema.in_.value, field.alias),
- config=BaseConfig,
+ if PYDANTIC_1:
+ errors.append(
+ ErrorWrapper(
+ MissingError(), loc=(field_info.in_.value, field.alias)
+ )
+ )
+ else: # pragma: nocover
+ errors.append(
+ ErrorWrapper( # type: ignore
+ MissingError(),
+ loc=(field_info.in_.value, field.alias),
+ config=BaseConfig,
+ )
)
- )
else:
values[field.name] = deepcopy(field.default)
continue
- v_, errors_ = field.validate(value, values, loc=(schema.in_.value, field.alias))
+ v_, errors_ = field.validate(
+ value, values, loc=(field_info.in_.value, field.alias)
+ )
if isinstance(errors_, ErrorWrapper):
errors.append(errors_)
elif isinstance(errors_, list):
async def request_body_to_args(
- required_params: List[Field],
+ required_params: List[ModelField],
received_body: Optional[Union[Dict[str, Any], FormData]],
) -> Tuple[Dict[str, Any], List[ErrorWrapper]]:
values = {}
errors = []
if required_params:
field = required_params[0]
- embed = getattr(field.schema, "embed", None)
+ field_info = get_field_info(field)
+ embed = getattr(field_info, "embed", None)
if len(required_params) == 1 and not embed:
received_body = {field.alias: received_body}
for field in required_params:
value = received_body.get(field.alias)
if (
value is None
- or (isinstance(field.schema, params.Form) and value == "")
+ or (isinstance(field_info, params.Form) and value == "")
or (
- isinstance(field.schema, params.Form)
+ isinstance(field_info, params.Form)
and field.shape in sequence_shapes
and len(value) == 0
)
):
if field.required:
- errors.append(
- ErrorWrapper(
- MissingError(), loc=("body", field.alias), config=BaseConfig
+ if PYDANTIC_1:
+ errors.append(
+ ErrorWrapper(MissingError(), loc=("body", field.alias))
+ )
+ else: # pragma: nocover
+ errors.append(
+ ErrorWrapper( # type: ignore
+ MissingError(),
+ loc=("body", field.alias),
+ config=BaseConfig,
+ )
)
- )
else:
values[field.name] = deepcopy(field.default)
continue
if (
- isinstance(field.schema, params.File)
+ isinstance(field_info, params.File)
and lenient_issubclass(field.type_, bytes)
and isinstance(value, UploadFile)
):
value = await value.read()
elif (
field.shape in sequence_shapes
- and isinstance(field.schema, params.File)
+ and isinstance(field_info, params.File)
and lenient_issubclass(field.type_, bytes)
and isinstance(value, sequence_types)
):
return values, errors
-def get_schema_compatible_field(*, field: Field) -> Field:
+def get_schema_compatible_field(*, field: ModelField) -> ModelField:
out_field = field
if lenient_issubclass(field.type_, UploadFile):
use_type: type = bytes
if field.shape in sequence_shapes:
use_type = List[bytes]
- out_field = Field(
- name=field.name,
- type_=use_type,
- class_validators=field.class_validators,
- model_config=field.model_config,
- default=field.default,
- required=field.required,
- alias=field.alias,
- schema=field.schema,
- )
+ if PYDANTIC_1:
+ out_field = ModelField(
+ name=field.name,
+ type_=use_type,
+ class_validators=field.class_validators,
+ model_config=field.model_config,
+ default=field.default,
+ required=field.required,
+ alias=field.alias,
+ field_info=field.field_info,
+ )
+ else: # pragma: nocover
+ out_field = ModelField( # type: ignore
+ name=field.name,
+ type_=use_type,
+ class_validators=field.class_validators,
+ model_config=field.model_config,
+ default=field.default,
+ required=field.required,
+ alias=field.alias,
+ schema=field.schema, # type: ignore
+ )
+
return out_field
-def get_body_field(*, dependant: Dependant, name: str) -> Optional[Field]:
+def get_body_field(*, dependant: Dependant, name: str) -> Optional[ModelField]:
flat_dependant = get_flat_dependant(dependant)
if not flat_dependant.body_params:
return None
first_param = flat_dependant.body_params[0]
- embed = getattr(first_param.schema, "embed", None)
+ field_info = get_field_info(first_param)
+ embed = getattr(field_info, "embed", None)
if len(flat_dependant.body_params) == 1 and not embed:
return get_schema_compatible_field(field=first_param)
model_name = "Body_" + name
BodyModel.__fields__[f.name] = get_schema_compatible_field(field=f)
required = any(True for f in flat_dependant.body_params if f.required)
- BodySchema_kwargs: Dict[str, Any] = dict(default=None)
- if any(isinstance(f.schema, params.File) for f in flat_dependant.body_params):
- BodySchema: Type[params.Body] = params.File
- elif any(isinstance(f.schema, params.Form) for f in flat_dependant.body_params):
- BodySchema = params.Form
+ BodyFieldInfo_kwargs: Dict[str, Any] = dict(default=None)
+ if any(
+ isinstance(get_field_info(f), params.File) for f in flat_dependant.body_params
+ ):
+ BodyFieldInfo: Type[params.Body] = params.File
+ elif any(
+ isinstance(get_field_info(f), params.Form) for f in flat_dependant.body_params
+ ):
+ BodyFieldInfo = params.Form
else:
- BodySchema = params.Body
+ BodyFieldInfo = params.Body
body_param_media_types = [
- getattr(f.schema, "media_type")
+ getattr(get_field_info(f), "media_type")
for f in flat_dependant.body_params
- if isinstance(f.schema, params.Body)
+ if isinstance(get_field_info(f), params.Body)
]
if len(set(body_param_media_types)) == 1:
- BodySchema_kwargs["media_type"] = body_param_media_types[0]
-
- field = Field(
- name="body",
- type_=BodyModel,
- default=None,
- required=required,
- model_config=BaseConfig,
- class_validators={},
- alias="body",
- schema=BodySchema(**BodySchema_kwargs),
- )
+ BodyFieldInfo_kwargs["media_type"] = body_param_media_types[0]
+ if PYDANTIC_1:
+ field = ModelField(
+ name="body",
+ type_=BodyModel,
+ default=None,
+ required=required,
+ model_config=BaseConfig,
+ class_validators={},
+ alias="body",
+ field_info=BodyFieldInfo(**BodyFieldInfo_kwargs),
+ )
+ else: # pragma: nocover
+ field = ModelField( # type: ignore
+ name="body",
+ type_=BodyModel,
+ default=None,
+ required=required,
+ model_config=BaseConfig,
+ class_validators={},
+ alias="body",
+ schema=BodyFieldInfo(**BodyFieldInfo_kwargs),
+ )
return field
from types import GeneratorType
from typing import Any, Dict, List, Set, Union
+from fastapi.utils import PYDANTIC_1, logger
from pydantic import BaseModel
from pydantic.json import ENCODERS_BY_TYPE
include: Union[SetIntStr, DictIntStrAny] = None,
exclude: Union[SetIntStr, DictIntStrAny] = set(),
by_alias: bool = True,
- skip_defaults: bool = False,
+ skip_defaults: bool = None,
+ exclude_unset: bool = False,
include_none: bool = True,
custom_encoder: dict = {},
sqlalchemy_safe: bool = True,
) -> Any:
+ if skip_defaults is not None:
+ logger.warning( # pragma: nocover
+ "skip_defaults in jsonable_encoder has been deprecated in \
+ favor of exclude_unset to keep in line with Pydantic v1, support for it \
+ will be removed soon."
+ )
if include is not None and not isinstance(include, set):
include = set(include)
if exclude is not None and not isinstance(exclude, set):
exclude = set(exclude)
if isinstance(obj, BaseModel):
encoder = getattr(obj.Config, "json_encoders", custom_encoder)
- return jsonable_encoder(
- obj.dict(
+ if PYDANTIC_1:
+ obj_dict = obj.dict(
+ include=include,
+ exclude=exclude,
+ by_alias=by_alias,
+ exclude_unset=bool(exclude_unset or skip_defaults),
+ )
+ else: # pragma: nocover
+ obj_dict = obj.dict(
include=include,
exclude=exclude,
by_alias=by_alias,
- skip_defaults=skip_defaults,
- ),
+ skip_defaults=bool(exclude_unset or skip_defaults),
+ )
+ return jsonable_encoder(
+ obj_dict,
include_none=include_none,
custom_encoder=encoder,
sqlalchemy_safe=sqlalchemy_safe,
encoded_key = jsonable_encoder(
key,
by_alias=by_alias,
- skip_defaults=skip_defaults,
+ exclude_unset=exclude_unset,
include_none=include_none,
custom_encoder=custom_encoder,
sqlalchemy_safe=sqlalchemy_safe,
encoded_value = jsonable_encoder(
value,
by_alias=by_alias,
- skip_defaults=skip_defaults,
+ exclude_unset=exclude_unset,
include_none=include_none,
custom_encoder=custom_encoder,
sqlalchemy_safe=sqlalchemy_safe,
include=include,
exclude=exclude,
by_alias=by_alias,
- skip_defaults=skip_defaults,
+ exclude_unset=exclude_unset,
include_none=include_none,
custom_encoder=custom_encoder,
sqlalchemy_safe=sqlalchemy_safe,
return jsonable_encoder(
data,
by_alias=by_alias,
- skip_defaults=skip_defaults,
+ exclude_unset=exclude_unset,
include_none=include_none,
custom_encoder=custom_encoder,
sqlalchemy_safe=sqlalchemy_safe,
from typing import Any, Sequence
-from pydantic import ValidationError
+from fastapi.utils import PYDANTIC_1
+from pydantic import ValidationError, create_model
from pydantic.error_wrappers import ErrorList
from starlette.exceptions import HTTPException as StarletteHTTPException
from starlette.requests import Request
self.headers = headers
+RequestErrorModel = create_model("Request")
+WebSocketErrorModel = create_model("WebSocket")
+
+
class RequestValidationError(ValidationError):
def __init__(self, errors: Sequence[ErrorList]) -> None:
- super().__init__(errors, Request)
+ if PYDANTIC_1:
+ super().__init__(errors, RequestErrorModel)
+ else:
+ super().__init__(errors, Request) # type: ignore # pragma: nocover
class WebSocketRequestValidationError(ValidationError):
def __init__(self, errors: Sequence[ErrorList]) -> None:
- super().__init__(errors, WebSocket)
+ if PYDANTIC_1:
+ super().__init__(errors, WebSocketErrorModel)
+ else:
+ super().__init__(errors, WebSocket) # type: ignore # pragma: nocover
-import logging
from enum import Enum
from typing import Any, Dict, List, Optional, Union
-from pydantic import BaseModel, Schema as PSchema
-from pydantic.types import UrlStr
+from fastapi.utils import logger
+from pydantic import BaseModel
-logger = logging.getLogger("fastapi")
+try:
+ from pydantic import AnyUrl, Field
+except ImportError: # pragma: nocover
+ # TODO: remove when removing support for Pydantic < 1.0.0
+ from pydantic import Schema as Field # type: ignore
+ from pydantic import UrlStr as AnyUrl # type: ignore
try:
import email_validator
assert email_validator # make autoflake ignore the unused import
- from pydantic.types import EmailStr
+ try:
+ from pydantic import EmailStr
+ except ImportError: # pragma: nocover
+ # TODO: remove when removing support for Pydantic < 1.0.0
+ from pydantic.types import EmailStr # type: ignore
except ImportError: # pragma: no cover
logger.warning(
"email-validator not installed, email fields will be treated as str.\n"
class Contact(BaseModel):
name: Optional[str] = None
- url: Optional[UrlStr] = None
+ url: Optional[AnyUrl] = None
email: Optional[EmailStr] = None
class License(BaseModel):
name: str
- url: Optional[UrlStr] = None
+ url: Optional[AnyUrl] = None
class Info(BaseModel):
class Server(BaseModel):
- url: UrlStr
+ url: AnyUrl
description: Optional[str] = None
variables: Optional[Dict[str, ServerVariable]] = None
class Reference(BaseModel):
- ref: str = PSchema(..., alias="$ref") # type: ignore
+ ref: str = Field(..., alias="$ref")
class Discriminator(BaseModel):
class ExternalDocumentation(BaseModel):
description: Optional[str] = None
- url: UrlStr
+ url: AnyUrl
class SchemaBase(BaseModel):
- ref: Optional[str] = PSchema(None, alias="$ref") # type: ignore
+ ref: Optional[str] = Field(None, alias="$ref")
title: Optional[str] = None
multipleOf: Optional[float] = None
maximum: Optional[float] = None
exclusiveMaximum: Optional[float] = None
minimum: Optional[float] = None
exclusiveMinimum: Optional[float] = None
- maxLength: Optional[int] = PSchema(None, gte=0) # type: ignore
- minLength: Optional[int] = PSchema(None, gte=0) # type: ignore
+ maxLength: Optional[int] = Field(None, gte=0)
+ minLength: Optional[int] = Field(None, gte=0)
pattern: Optional[str] = None
- maxItems: Optional[int] = PSchema(None, gte=0) # type: ignore
- minItems: Optional[int] = PSchema(None, gte=0) # type: ignore
+ maxItems: Optional[int] = Field(None, gte=0)
+ minItems: Optional[int] = Field(None, gte=0)
uniqueItems: Optional[bool] = None
- maxProperties: Optional[int] = PSchema(None, gte=0) # type: ignore
- minProperties: Optional[int] = PSchema(None, gte=0) # type: ignore
+ maxProperties: Optional[int] = Field(None, gte=0)
+ minProperties: Optional[int] = Field(None, gte=0)
required: Optional[List[str]] = None
enum: Optional[List[str]] = None
type: Optional[str] = None
allOf: Optional[List[Any]] = None
oneOf: Optional[List[Any]] = None
anyOf: Optional[List[Any]] = None
- not_: Optional[List[Any]] = PSchema(None, alias="not") # type: ignore
+ not_: Optional[List[Any]] = Field(None, alias="not")
items: Optional[Any] = None
properties: Optional[Dict[str, Any]] = None
additionalProperties: Optional[Union[Dict[str, Any], bool]] = None
allOf: Optional[List[SchemaBase]] = None
oneOf: Optional[List[SchemaBase]] = None
anyOf: Optional[List[SchemaBase]] = None
- not_: Optional[List[SchemaBase]] = PSchema(None, alias="not") # type: ignore
+ not_: Optional[List[SchemaBase]] = Field(None, alias="not")
items: Optional[SchemaBase] = None
properties: Optional[Dict[str, SchemaBase]] = None
- additionalProperties: Optional[Union[SchemaBase, bool]] = None # type: ignore
+ additionalProperties: Optional[Union[Dict[str, Any], bool]] = None
class Example(BaseModel):
summary: Optional[str] = None
description: Optional[str] = None
value: Optional[Any] = None
- externalValue: Optional[UrlStr] = None
+ externalValue: Optional[AnyUrl] = None
class ParameterInType(Enum):
class MediaType(BaseModel):
- schema_: Optional[Union[Schema, Reference]] = PSchema( # type: ignore
- None, alias="schema"
- )
+ schema_: Optional[Union[Schema, Reference]] = Field(None, alias="schema")
example: Optional[Any] = None
examples: Optional[Dict[str, Union[Example, Reference]]] = None
encoding: Optional[Dict[str, Encoding]] = None
style: Optional[str] = None
explode: Optional[bool] = None
allowReserved: Optional[bool] = None
- schema_: Optional[Union[Schema, Reference]] = PSchema( # type: ignore
- None, alias="schema"
- )
+ schema_: Optional[Union[Schema, Reference]] = Field(None, alias="schema")
example: Optional[Any] = None
examples: Optional[Dict[str, Union[Example, Reference]]] = None
# Serialization rules for more complex scenarios
class Parameter(ParameterBase):
name: str
- in_: ParameterInType = PSchema(..., alias="in") # type: ignore
+ in_: ParameterInType = Field(..., alias="in")
class Header(ParameterBase):
class PathItem(BaseModel):
- ref: Optional[str] = PSchema(None, alias="$ref") # type: ignore
+ ref: Optional[str] = Field(None, alias="$ref")
summary: Optional[str] = None
description: Optional[str] = None
get: Optional[Operation] = None
class SecurityBase(BaseModel):
- type_: SecuritySchemeType = PSchema(..., alias="type") # type: ignore
+ type_: SecuritySchemeType = Field(..., alias="type")
description: Optional[str] = None
class APIKey(SecurityBase):
- type_ = PSchema(SecuritySchemeType.apiKey, alias="type") # type: ignore
- in_: APIKeyIn = PSchema(..., alias="in") # type: ignore
+ type_ = Field(SecuritySchemeType.apiKey, alias="type")
+ in_: APIKeyIn = Field(..., alias="in")
name: str
class HTTPBase(SecurityBase):
- type_ = PSchema(SecuritySchemeType.http, alias="type") # type: ignore
+ type_ = Field(SecuritySchemeType.http, alias="type")
scheme: str
class OAuth2(SecurityBase):
- type_ = PSchema(SecuritySchemeType.oauth2, alias="type") # type: ignore
+ type_ = Field(SecuritySchemeType.oauth2, alias="type")
flows: OAuthFlows
class OpenIdConnect(SecurityBase):
- type_ = PSchema(SecuritySchemeType.openIdConnect, alias="type") # type: ignore
+ type_ = Field(SecuritySchemeType.openIdConnect, alias="type")
openIdConnectUrl: str
from fastapi.params import Body, Param
from fastapi.utils import (
generate_operation_id_for_path,
+ get_field_info,
get_flat_models_from_routes,
get_model_definitions,
)
-from pydantic.fields import Field
+from pydantic import BaseModel
from pydantic.schema import field_schema, get_model_name_map
from pydantic.utils import lenient_issubclass
from starlette.responses import JSONResponse
from starlette.routing import BaseRoute
from starlette.status import HTTP_422_UNPROCESSABLE_ENTITY
+try:
+ from pydantic.fields import ModelField
+except ImportError: # pragma: nocover
+ # TODO: remove when removing support for Pydantic < 1.0.0
+ from pydantic.fields import Field as ModelField # type: ignore
+
validation_error_definition = {
"title": "ValidationError",
"type": "object",
}
-def get_openapi_params(dependant: Dependant) -> List[Field]:
+def get_openapi_params(dependant: Dependant) -> List[ModelField]:
flat_dependant = get_flat_dependant(dependant, skip_repeats=True)
return (
flat_dependant.path_params
def get_openapi_operation_parameters(
- all_route_params: Sequence[Field],
+ all_route_params: Sequence[ModelField],
) -> List[Dict[str, Any]]:
parameters = []
for param in all_route_params:
- schema = param.schema
- schema = cast(Param, schema)
+ field_info = get_field_info(param)
+ field_info = cast(Param, field_info)
parameter = {
"name": param.alias,
- "in": schema.in_.value,
+ "in": field_info.in_.value,
"required": param.required,
"schema": field_schema(param, model_name_map={})[0],
}
- if schema.description:
- parameter["description"] = schema.description
- if schema.deprecated:
- parameter["deprecated"] = schema.deprecated
+ if field_info.description:
+ parameter["description"] = field_info.description
+ if field_info.deprecated:
+ parameter["deprecated"] = field_info.deprecated
parameters.append(parameter)
return parameters
def get_openapi_operation_request_body(
- *, body_field: Optional[Field], model_name_map: Dict[Type, str]
+ *, body_field: Optional[ModelField], model_name_map: Dict[Type[BaseModel], str]
) -> Optional[Dict]:
if not body_field:
return None
- assert isinstance(body_field, Field)
+ assert isinstance(body_field, ModelField)
body_schema, _, _ = field_schema(
body_field, model_name_map=model_name_map, ref_prefix=REF_PREFIX
)
- body_field.schema = cast(Body, body_field.schema)
- request_media_type = body_field.schema.media_type
+ field_info = cast(Body, get_field_info(body_field))
+ request_media_type = field_info.media_type
required = body_field.required
request_body_oai: Dict[str, Any] = {}
if required:
from enum import Enum
from typing import Any, Callable, Sequence
-from pydantic import Schema
+try:
+ from pydantic.fields import FieldInfo
+except ImportError: # pragma: nocover
+ # TODO: remove when removing support for Pydantic < 1.0.0
+ from pydantic import Schema as FieldInfo # type: ignore
class ParamTypes(Enum):
cookie = "cookie"
-class Param(Schema):
+class Param(FieldInfo):
in_: ParamTypes
def __init__(
)
-class Body(Schema):
+class Body(FieldInfo):
def __init__(
self,
default: Any,
from fastapi.encoders import DictIntStrAny, SetIntStr, jsonable_encoder
from fastapi.exceptions import RequestValidationError, WebSocketRequestValidationError
from fastapi.openapi.constants import STATUS_CODES_WITH_NO_BODY
-from fastapi.utils import create_cloned_field, generate_operation_id_for_path
-from pydantic import BaseConfig, BaseModel, Schema
+from fastapi.utils import (
+ PYDANTIC_1,
+ create_cloned_field,
+ generate_operation_id_for_path,
+ get_field_info,
+ warning_response_model_skip_defaults_deprecated,
+)
+from pydantic import BaseConfig, BaseModel
from pydantic.error_wrappers import ErrorWrapper, ValidationError
-from pydantic.fields import Field
from pydantic.utils import lenient_issubclass
from starlette import routing
from starlette.concurrency import run_in_threadpool
from starlette.types import ASGIApp
from starlette.websockets import WebSocket
+try:
+ from pydantic.fields import FieldInfo, ModelField
+except ImportError: # pragma: nocover
+ # TODO: remove when removing support for Pydantic < 1.0.0
+ from pydantic import Schema as FieldInfo # type: ignore
+ from pydantic.fields import Field as ModelField # type: ignore
+
def serialize_response(
*,
- field: Field = None,
+ field: ModelField = None,
response: Response,
include: Union[SetIntStr, DictIntStrAny] = None,
exclude: Union[SetIntStr, DictIntStrAny] = set(),
by_alias: bool = True,
- skip_defaults: bool = False,
+ exclude_unset: bool = False,
) -> Any:
if field:
errors = []
- if skip_defaults and isinstance(response, BaseModel):
- response = response.dict(skip_defaults=skip_defaults)
+ if exclude_unset and isinstance(response, BaseModel):
+ if PYDANTIC_1:
+ response = response.dict(exclude_unset=exclude_unset)
+ else:
+ response = response.dict(skip_defaults=exclude_unset) # pragma: nocover
value, errors_ = field.validate(response, {}, loc=("response",))
if isinstance(errors_, ErrorWrapper):
errors.append(errors_)
include=include,
exclude=exclude,
by_alias=by_alias,
- skip_defaults=skip_defaults,
+ exclude_unset=exclude_unset,
)
else:
return jsonable_encoder(response)
def get_request_handler(
dependant: Dependant,
- body_field: Field = None,
+ body_field: ModelField = None,
status_code: int = 200,
response_class: Type[Response] = JSONResponse,
- response_field: Field = None,
+ response_field: ModelField = None,
response_model_include: Union[SetIntStr, DictIntStrAny] = None,
response_model_exclude: Union[SetIntStr, DictIntStrAny] = set(),
response_model_by_alias: bool = True,
- response_model_skip_defaults: bool = False,
+ response_model_exclude_unset: bool = False,
dependency_overrides_provider: Any = None,
) -> Callable:
assert dependant.call is not None, "dependant.call must be a function"
is_coroutine = asyncio.iscoroutinefunction(dependant.call)
- is_body_form = body_field and isinstance(body_field.schema, params.Form)
+ is_body_form = body_field and isinstance(get_field_info(body_field), params.Form)
async def app(request: Request) -> Response:
try:
include=response_model_include,
exclude=response_model_exclude,
by_alias=response_model_by_alias,
- skip_defaults=response_model_skip_defaults,
+ exclude_unset=response_model_exclude_unset,
)
response = response_class(
content=response_data,
response_model_include: Union[SetIntStr, DictIntStrAny] = None,
response_model_exclude: Union[SetIntStr, DictIntStrAny] = set(),
response_model_by_alias: bool = True,
- response_model_skip_defaults: bool = False,
+ response_model_exclude_unset: bool = False,
include_in_schema: bool = True,
response_class: Optional[Type[Response]] = None,
dependency_overrides_provider: Any = None,
status_code not in STATUS_CODES_WITH_NO_BODY
), f"Status code {status_code} must not have a response body"
response_name = "Response_" + self.unique_id
- self.response_field: Optional[Field] = Field(
- name=response_name,
- type_=self.response_model,
- class_validators={},
- default=None,
- required=False,
- model_config=BaseConfig,
- schema=Schema(None),
- )
+ if PYDANTIC_1:
+ self.response_field: Optional[ModelField] = ModelField(
+ name=response_name,
+ type_=self.response_model,
+ class_validators={},
+ default=None,
+ required=False,
+ model_config=BaseConfig,
+ field_info=FieldInfo(None),
+ )
+ else:
+ self.response_field: Optional[ModelField] = ModelField( # type: ignore # pragma: nocover
+ name=response_name,
+ type_=self.response_model,
+ class_validators={},
+ default=None,
+ required=False,
+ model_config=BaseConfig,
+ schema=FieldInfo(None),
+ )
# Create a clone of the field, so that a Pydantic submodel is not returned
# as is just because it's an instance of a subclass of a more limited class
# e.g. UserInDB (containing hashed_password) could be a subclass of User
# would pass the validation and be returned as is.
# By being a new field, no inheritance will be passed as is. A new model
# will be always created.
- self.secure_cloned_response_field: Optional[Field] = create_cloned_field(
- self.response_field
- )
+ self.secure_cloned_response_field: Optional[
+ ModelField
+ ] = create_cloned_field(self.response_field)
else:
self.response_field = None
self.secure_cloned_response_field = None
model, BaseModel
), "A response model must be a Pydantic model"
response_name = f"Response_{additional_status_code}_{self.unique_id}"
- response_field = Field(
- name=response_name,
- type_=model,
- class_validators=None,
- default=None,
- required=False,
- model_config=BaseConfig,
- schema=Schema(None),
- )
+ if PYDANTIC_1:
+ response_field = ModelField(
+ name=response_name,
+ type_=model,
+ class_validators=None,
+ default=None,
+ required=False,
+ model_config=BaseConfig,
+ field_info=FieldInfo(None),
+ )
+ else:
+ response_field = ModelField( # type: ignore # pragma: nocover
+ name=response_name,
+ type_=model,
+ class_validators=None,
+ default=None,
+ required=False,
+ model_config=BaseConfig,
+ schema=FieldInfo(None),
+ )
response_fields[additional_status_code] = response_field
if response_fields:
- self.response_fields: Dict[Union[int, str], Field] = response_fields
+ self.response_fields: Dict[Union[int, str], ModelField] = response_fields
else:
self.response_fields = {}
self.deprecated = deprecated
self.response_model_include = response_model_include
self.response_model_exclude = response_model_exclude
self.response_model_by_alias = response_model_by_alias
- self.response_model_skip_defaults = response_model_skip_defaults
+ self.response_model_exclude_unset = response_model_exclude_unset
self.include_in_schema = include_in_schema
self.response_class = response_class
response_model_include=self.response_model_include,
response_model_exclude=self.response_model_exclude,
response_model_by_alias=self.response_model_by_alias,
- response_model_skip_defaults=self.response_model_skip_defaults,
+ response_model_exclude_unset=self.response_model_exclude_unset,
dependency_overrides_provider=self.dependency_overrides_provider,
)
response_model_include: Union[SetIntStr, DictIntStrAny] = None,
response_model_exclude: Union[SetIntStr, DictIntStrAny] = set(),
response_model_by_alias: bool = True,
- response_model_skip_defaults: bool = False,
+ response_model_skip_defaults: bool = None,
+ response_model_exclude_unset: bool = False,
include_in_schema: bool = True,
response_class: Type[Response] = None,
name: str = None,
route_class_override: Optional[Type[APIRoute]] = None,
) -> None:
+ if response_model_skip_defaults is not None:
+ warning_response_model_skip_defaults_deprecated() # pragma: nocover
route_class = route_class_override or self.route_class
route = route_class(
path,
response_model_include=response_model_include,
response_model_exclude=response_model_exclude,
response_model_by_alias=response_model_by_alias,
- response_model_skip_defaults=response_model_skip_defaults,
+ response_model_exclude_unset=bool(
+ response_model_exclude_unset or response_model_skip_defaults
+ ),
include_in_schema=include_in_schema,
response_class=response_class,
name=name,
response_model_include: Union[SetIntStr, DictIntStrAny] = None,
response_model_exclude: Union[SetIntStr, DictIntStrAny] = set(),
response_model_by_alias: bool = True,
- response_model_skip_defaults: bool = False,
+ response_model_skip_defaults: bool = None,
+ response_model_exclude_unset: bool = False,
include_in_schema: bool = True,
response_class: Type[Response] = None,
name: str = None,
) -> Callable:
+ if response_model_skip_defaults is not None:
+ warning_response_model_skip_defaults_deprecated() # pragma: nocover
+
def decorator(func: Callable) -> Callable:
self.add_api_route(
path,
response_model_include=response_model_include,
response_model_exclude=response_model_exclude,
response_model_by_alias=response_model_by_alias,
- response_model_skip_defaults=response_model_skip_defaults,
+ response_model_exclude_unset=bool(
+ response_model_exclude_unset or response_model_skip_defaults
+ ),
include_in_schema=include_in_schema,
response_class=response_class,
name=name,
response_model_include=route.response_model_include,
response_model_exclude=route.response_model_exclude,
response_model_by_alias=route.response_model_by_alias,
- response_model_skip_defaults=route.response_model_skip_defaults,
+ response_model_exclude_unset=route.response_model_exclude_unset,
include_in_schema=route.include_in_schema,
response_class=route.response_class or default_response_class,
name=route.name,
response_model_include: Union[SetIntStr, DictIntStrAny] = None,
response_model_exclude: Union[SetIntStr, DictIntStrAny] = set(),
response_model_by_alias: bool = True,
- response_model_skip_defaults: bool = False,
+ response_model_skip_defaults: bool = None,
+ response_model_exclude_unset: bool = False,
include_in_schema: bool = True,
response_class: Type[Response] = None,
name: str = None,
) -> Callable:
+ if response_model_skip_defaults is not None:
+ warning_response_model_skip_defaults_deprecated() # pragma: nocover
return self.api_route(
path=path,
response_model=response_model,
response_model_include=response_model_include,
response_model_exclude=response_model_exclude,
response_model_by_alias=response_model_by_alias,
- response_model_skip_defaults=response_model_skip_defaults,
+ response_model_exclude_unset=bool(
+ response_model_exclude_unset or response_model_skip_defaults
+ ),
include_in_schema=include_in_schema,
response_class=response_class,
name=name,
response_model_include: Union[SetIntStr, DictIntStrAny] = None,
response_model_exclude: Union[SetIntStr, DictIntStrAny] = set(),
response_model_by_alias: bool = True,
- response_model_skip_defaults: bool = False,
+ response_model_skip_defaults: bool = None,
+ response_model_exclude_unset: bool = False,
include_in_schema: bool = True,
response_class: Type[Response] = None,
name: str = None,
) -> Callable:
+ if response_model_skip_defaults is not None:
+ warning_response_model_skip_defaults_deprecated() # pragma: nocover
return self.api_route(
path=path,
response_model=response_model,
response_model_include=response_model_include,
response_model_exclude=response_model_exclude,
response_model_by_alias=response_model_by_alias,
- response_model_skip_defaults=response_model_skip_defaults,
+ response_model_exclude_unset=bool(
+ response_model_exclude_unset or response_model_skip_defaults
+ ),
include_in_schema=include_in_schema,
response_class=response_class,
name=name,
response_model_include: Union[SetIntStr, DictIntStrAny] = None,
response_model_exclude: Union[SetIntStr, DictIntStrAny] = set(),
response_model_by_alias: bool = True,
- response_model_skip_defaults: bool = False,
+ response_model_skip_defaults: bool = None,
+ response_model_exclude_unset: bool = False,
include_in_schema: bool = True,
response_class: Type[Response] = None,
name: str = None,
) -> Callable:
+ if response_model_skip_defaults is not None:
+ warning_response_model_skip_defaults_deprecated() # pragma: nocover
return self.api_route(
path=path,
response_model=response_model,
response_model_include=response_model_include,
response_model_exclude=response_model_exclude,
response_model_by_alias=response_model_by_alias,
- response_model_skip_defaults=response_model_skip_defaults,
+ response_model_exclude_unset=bool(
+ response_model_exclude_unset or response_model_skip_defaults
+ ),
include_in_schema=include_in_schema,
response_class=response_class,
name=name,
response_model_include: Union[SetIntStr, DictIntStrAny] = None,
response_model_exclude: Union[SetIntStr, DictIntStrAny] = set(),
response_model_by_alias: bool = True,
- response_model_skip_defaults: bool = False,
+ response_model_skip_defaults: bool = None,
+ response_model_exclude_unset: bool = False,
include_in_schema: bool = True,
response_class: Type[Response] = None,
name: str = None,
) -> Callable:
+ if response_model_skip_defaults is not None:
+ warning_response_model_skip_defaults_deprecated() # pragma: nocover
return self.api_route(
path=path,
response_model=response_model,
response_model_include=response_model_include,
response_model_exclude=response_model_exclude,
response_model_by_alias=response_model_by_alias,
- response_model_skip_defaults=response_model_skip_defaults,
+ response_model_exclude_unset=bool(
+ response_model_exclude_unset or response_model_skip_defaults
+ ),
include_in_schema=include_in_schema,
response_class=response_class,
name=name,
response_model_include: Union[SetIntStr, DictIntStrAny] = None,
response_model_exclude: Union[SetIntStr, DictIntStrAny] = set(),
response_model_by_alias: bool = True,
- response_model_skip_defaults: bool = False,
+ response_model_skip_defaults: bool = None,
+ response_model_exclude_unset: bool = False,
include_in_schema: bool = True,
response_class: Type[Response] = None,
name: str = None,
) -> Callable:
+ if response_model_skip_defaults is not None:
+ warning_response_model_skip_defaults_deprecated() # pragma: nocover
return self.api_route(
path=path,
response_model=response_model,
response_model_include=response_model_include,
response_model_exclude=response_model_exclude,
response_model_by_alias=response_model_by_alias,
- response_model_skip_defaults=response_model_skip_defaults,
+ response_model_exclude_unset=bool(
+ response_model_exclude_unset or response_model_skip_defaults
+ ),
include_in_schema=include_in_schema,
response_class=response_class,
name=name,
response_model_include: Union[SetIntStr, DictIntStrAny] = None,
response_model_exclude: Union[SetIntStr, DictIntStrAny] = set(),
response_model_by_alias: bool = True,
- response_model_skip_defaults: bool = False,
+ response_model_skip_defaults: bool = None,
+ response_model_exclude_unset: bool = False,
include_in_schema: bool = True,
response_class: Type[Response] = None,
name: str = None,
) -> Callable:
+ if response_model_skip_defaults is not None:
+ warning_response_model_skip_defaults_deprecated() # pragma: nocover
return self.api_route(
path=path,
response_model=response_model,
response_model_include=response_model_include,
response_model_exclude=response_model_exclude,
response_model_by_alias=response_model_by_alias,
- response_model_skip_defaults=response_model_skip_defaults,
+ response_model_exclude_unset=bool(
+ response_model_exclude_unset or response_model_skip_defaults
+ ),
include_in_schema=include_in_schema,
response_class=response_class,
name=name,
response_model_include: Union[SetIntStr, DictIntStrAny] = None,
response_model_exclude: Union[SetIntStr, DictIntStrAny] = set(),
response_model_by_alias: bool = True,
- response_model_skip_defaults: bool = False,
+ response_model_skip_defaults: bool = None,
+ response_model_exclude_unset: bool = False,
include_in_schema: bool = True,
response_class: Type[Response] = None,
name: str = None,
) -> Callable:
+ if response_model_skip_defaults is not None:
+ warning_response_model_skip_defaults_deprecated() # pragma: nocover
return self.api_route(
path=path,
response_model=response_model,
response_model_include=response_model_include,
response_model_exclude=response_model_exclude,
response_model_by_alias=response_model_by_alias,
- response_model_skip_defaults=response_model_skip_defaults,
+ response_model_exclude_unset=bool(
+ response_model_exclude_unset or response_model_skip_defaults
+ ),
include_in_schema=include_in_schema,
response_class=response_class,
name=name,
response_model_include: Union[SetIntStr, DictIntStrAny] = None,
response_model_exclude: Union[SetIntStr, DictIntStrAny] = set(),
response_model_by_alias: bool = True,
- response_model_skip_defaults: bool = False,
+ response_model_skip_defaults: bool = None,
+ response_model_exclude_unset: bool = False,
include_in_schema: bool = True,
response_class: Type[Response] = None,
name: str = None,
) -> Callable:
+ if response_model_skip_defaults is not None:
+ warning_response_model_skip_defaults_deprecated() # pragma: nocover
return self.api_route(
path=path,
response_model=response_model,
response_model_include=response_model_include,
response_model_exclude=response_model_exclude,
response_model_by_alias=response_model_by_alias,
- response_model_skip_defaults=response_model_skip_defaults,
+ response_model_exclude_unset=bool(
+ response_model_exclude_unset or response_model_skip_defaults
+ ),
include_in_schema=include_in_schema,
response_class=response_class,
name=name,
+import logging
import re
from dataclasses import is_dataclass
from typing import Any, Dict, List, Sequence, Set, Type, cast
from fastapi import routing
from fastapi.openapi.constants import REF_PREFIX
-from pydantic import BaseConfig, BaseModel, Schema, create_model
-from pydantic.fields import Field
+from pydantic import BaseConfig, BaseModel, create_model
from pydantic.schema import get_flat_models_from_fields, model_process_schema
from pydantic.utils import lenient_issubclass
from starlette.routing import BaseRoute
+logger = logging.getLogger("fastapi")
+
+try:
+ from pydantic.fields import FieldInfo, ModelField
+
+ PYDANTIC_1 = True
+except ImportError: # pragma: nocover
+ # TODO: remove when removing support for Pydantic < 1.0.0
+ from pydantic.fields import Field as ModelField # type: ignore
+ from pydantic import Schema as FieldInfo # type: ignore
+
+ logger.warning(
+ "Pydantic versions < 1.0.0 are deprecated in FastAPI and support will be \
+ removed soon"
+ )
+ PYDANTIC_1 = False
+
+
+# TODO: remove when removing support for Pydantic < 1.0.0
+def get_field_info(field: ModelField) -> FieldInfo:
+ if PYDANTIC_1:
+ return field.field_info # type: ignore
+ else:
+ return field.schema # type: ignore # pragma: nocover
+
+
+# TODO: remove when removing support for Pydantic < 1.0.0
+def warning_response_model_skip_defaults_deprecated() -> None:
+ logger.warning( # pragma: nocover
+ "response_model_skip_defaults has been deprecated in favor \
+ of response_model_exclude_unset to keep in line with Pydantic v1, \
+ support for it will be removed soon."
+ )
+
def get_flat_models_from_routes(routes: Sequence[BaseRoute]) -> Set[Type[BaseModel]]:
- body_fields_from_routes: List[Field] = []
- responses_from_routes: List[Field] = []
+ body_fields_from_routes: List[ModelField] = []
+ responses_from_routes: List[ModelField] = []
for route in routes:
if getattr(route, "include_in_schema", None) and isinstance(
route, routing.APIRoute
):
if route.body_field:
assert isinstance(
- route.body_field, Field
+ route.body_field, ModelField
), "A request body must be a Pydantic Field"
body_fields_from_routes.append(route.body_field)
if route.response_field:
return {item.strip("{}") for item in re.findall("{[^}]*}", path)}
-def create_cloned_field(field: Field) -> Field:
+def create_cloned_field(field: ModelField) -> ModelField:
original_type = field.type_
if is_dataclass(original_type) and hasattr(original_type, "__pydantic_model__"):
original_type = original_type.__pydantic_model__ # type: ignore
for f in original_type.__fields__.values():
use_type.__fields__[f.name] = f
use_type.__validators__ = original_type.__validators__
- new_field = Field(
- name=field.name,
- type_=use_type,
- class_validators={},
- default=None,
- required=False,
- model_config=BaseConfig,
- schema=Schema(None),
- )
+ if PYDANTIC_1:
+ new_field = ModelField(
+ name=field.name,
+ type_=use_type,
+ class_validators={},
+ default=None,
+ required=False,
+ model_config=BaseConfig,
+ field_info=FieldInfo(None),
+ )
+ else: # pragma: nocover
+ new_field = ModelField( # type: ignore
+ name=field.name,
+ type_=use_type,
+ class_validators={},
+ default=None,
+ required=False,
+ model_config=BaseConfig,
+ schema=FieldInfo(None),
+ )
new_field.has_alias = field.has_alias
new_field.alias = field.alias
new_field.class_validators = field.class_validators
new_field.default = field.default
new_field.required = field.required
new_field.model_config = field.model_config
- new_field.schema = field.schema
+ if PYDANTIC_1:
+ new_field.field_info = field.field_info
+ else: # pragma: nocover
+ new_field.schema = field.schema # type: ignore
new_field.allow_none = field.allow_none
new_field.validate_always = field.validate_always
if field.sub_fields:
if field.key_field:
new_field.key_field = create_cloned_field(field.key_field)
new_field.validators = field.validators
- new_field.whole_pre_validators = field.whole_pre_validators
- new_field.whole_post_validators = field.whole_post_validators
+ if PYDANTIC_1:
+ new_field.pre_validators = field.pre_validators
+ new_field.post_validators = field.post_validators
+ else: # pragma: nocover
+ new_field.whole_pre_validators = field.whole_pre_validators # type: ignore
+ new_field.whole_post_validators = field.whole_post_validators # type: ignore
new_field.parse_json = field.parse_json
new_field.shape = field.shape
- new_field._populate_validators()
+ try:
+ new_field.populate_validators()
+ except AttributeError: # pragma: nocover
+ # TODO: remove when removing support for Pydantic < 1.0.0
+ new_field._populate_validators() # type: ignore
return new_field
]
requires = [
"starlette >=0.12.9,<=0.12.9",
- "pydantic >=0.32.2,<=0.32.2"
+ "pydantic >=0.32.2,<2.0.0"
]
description-file = "README.md"
requires-python = ">=3.6"
"parameters": [
{
"required": True,
- "schema": {"title": "Item_Id"},
+ "schema": {"title": "Item Id"},
"name": "item_id",
"in": "path",
}
"parameters": [
{
"required": True,
- "schema": {"title": "Item_Id", "type": "string"},
+ "schema": {"title": "Item Id", "type": "string"},
"name": "item_id",
"in": "path",
}
"parameters": [
{
"required": True,
- "schema": {"title": "Item_Id", "type": "integer"},
+ "schema": {"title": "Item Id", "type": "integer"},
"name": "item_id",
"in": "path",
}
"parameters": [
{
"required": True,
- "schema": {"title": "Item_Id", "type": "number"},
+ "schema": {"title": "Item Id", "type": "number"},
"name": "item_id",
"in": "path",
}
"parameters": [
{
"required": True,
- "schema": {"title": "Item_Id", "type": "boolean"},
+ "schema": {"title": "Item Id", "type": "boolean"},
"name": "item_id",
"in": "path",
}
"parameters": [
{
"required": True,
- "schema": {"title": "Item_Id", "type": "string"},
+ "schema": {"title": "Item Id", "type": "string"},
"name": "item_id",
"in": "path",
}
"parameters": [
{
"required": True,
- "schema": {"title": "Item_Id", "type": "string"},
+ "schema": {"title": "Item Id", "type": "string"},
"name": "item_id",
"in": "path",
}
{
"required": True,
"schema": {
- "title": "Item_Id",
+ "title": "Item Id",
"minLength": 3,
"type": "string",
},
{
"required": True,
"schema": {
- "title": "Item_Id",
+ "title": "Item Id",
"maxLength": 3,
"type": "string",
},
{
"required": True,
"schema": {
- "title": "Item_Id",
+ "title": "Item Id",
"maxLength": 3,
"minLength": 2,
"type": "string",
{
"required": True,
"schema": {
- "title": "Item_Id",
+ "title": "Item Id",
"exclusiveMinimum": 3.0,
"type": "number",
},
{
"required": True,
"schema": {
- "title": "Item_Id",
+ "title": "Item Id",
"exclusiveMinimum": 0.0,
"type": "number",
},
{
"required": True,
"schema": {
- "title": "Item_Id",
+ "title": "Item Id",
"minimum": 3.0,
"type": "number",
},
{
"required": True,
"schema": {
- "title": "Item_Id",
+ "title": "Item Id",
"exclusiveMaximum": 3.0,
"type": "number",
},
{
"required": True,
"schema": {
- "title": "Item_Id",
+ "title": "Item Id",
"exclusiveMaximum": 0.0,
"type": "number",
},
{
"required": True,
"schema": {
- "title": "Item_Id",
+ "title": "Item Id",
"maximum": 3.0,
"type": "number",
},
{
"required": True,
"schema": {
- "title": "Item_Id",
+ "title": "Item Id",
"exclusiveMaximum": 3.0,
"exclusiveMinimum": 1.0,
"type": "number",
{
"required": True,
"schema": {
- "title": "Item_Id",
+ "title": "Item Id",
"maximum": 3.0,
"minimum": 1.0,
"type": "number",
{
"required": True,
"schema": {
- "title": "Item_Id",
+ "title": "Item Id",
"exclusiveMaximum": 3.0,
"type": "integer",
},
{
"required": True,
"schema": {
- "title": "Item_Id",
+ "title": "Item Id",
"exclusiveMinimum": 3.0,
"type": "integer",
},
{
"required": True,
"schema": {
- "title": "Item_Id",
+ "title": "Item Id",
"maximum": 3.0,
"type": "integer",
},
{
"required": True,
"schema": {
- "title": "Item_Id",
+ "title": "Item Id",
"minimum": 3.0,
"type": "integer",
},
{
"required": True,
"schema": {
- "title": "Item_Id",
+ "title": "Item Id",
"exclusiveMaximum": 3.0,
"exclusiveMinimum": 1.0,
"type": "integer",
{
"required": True,
"schema": {
- "title": "Item_Id",
+ "title": "Item Id",
"maximum": 3.0,
"minimum": 1.0,
"type": "integer",
"parameters": [
{
"required": True,
- "schema": {"title": "Item_Id", "type": "string"},
+ "schema": {"title": "Item Id", "type": "string"},
"name": "item_id",
"in": "path",
}
"parameters": [
{
"required": True,
- "schema": {"title": "Item_Id", "type": "string"},
+ "schema": {"title": "Item Id", "type": "string"},
"name": "item_id",
"in": "path",
}
"parameters": [
{
"required": True,
- "schema": {"title": "Item_Id", "type": "string"},
+ "schema": {"title": "Item Id", "type": "string"},
"name": "item_id",
"in": "path",
}
"parameters": [
{
"required": True,
- "schema": {"title": "Item_Id", "type": "string"},
+ "schema": {"title": "Item Id", "type": "string"},
"name": "item_id",
"in": "path",
}
"parameters": [
{
"required": True,
- "schema": {"title": "Item_Id", "type": "string"},
+ "schema": {"title": "Item Id", "type": "string"},
"name": "item_id",
"in": "path",
}
"parameters": [
{
"required": True,
- "schema": {"title": "Item_Id", "type": "string"},
+ "schema": {"title": "Item Id", "type": "string"},
"name": "item_id",
"in": "path",
}
"parameters": [
{
"required": True,
- "schema": {"title": "Item_Id", "type": "string"},
+ "schema": {"title": "Item Id", "type": "string"},
"name": "item_id",
"in": "path",
}
d = {
"required": True,
- "schema": {"title": "User_Id", "type": "string"},
+ "schema": {"title": "User Id", "type": "string"},
"name": "user_id",
"in": "path",
}
d = {
"required": False,
- "schema": {"title": "User_Id", "type": "string"},
+ "schema": {"title": "User Id", "type": "string"},
"name": "user_id",
"in": "query",
}
import pytest
from fastapi.encoders import jsonable_encoder
-from pydantic import BaseModel, Schema, ValidationError
+from pydantic import BaseModel, ValidationError
+
+try:
+ from pydantic import Field
+except ImportError: # pragma: nocover
+ # TODO: remove when removing support for Pydantic < 1.0.0
+ from pydantic import Schema as Field
class Person:
class ModelWithAlias(BaseModel):
- foo: str = Schema(..., alias="Foo")
+ foo: str = Field(..., alias="Foo")
def test_encode_class():
assert response.json() == {"detail": "Not Found"}
+response_not_valid_bool = {
+ "detail": [
+ {
+ "loc": ["path", "item_id"],
+ "msg": "value could not be parsed to a boolean",
+ "type": "type_error.bool",
+ }
+ ]
+}
+
response_not_valid_int = {
"detail": [
{
("/path/float/True", 422, response_not_valid_float),
("/path/float/42", 200, 42),
("/path/float/42.5", 200, 42.5),
- ("/path/bool/foobar", 200, False),
+ ("/path/bool/foobar", 422, response_not_valid_bool),
("/path/bool/True", 200, True),
- ("/path/bool/42", 200, False),
- ("/path/bool/42.5", 200, False),
+ ("/path/bool/42", 422, response_not_valid_bool),
+ ("/path/bool/42.5", 422, response_not_valid_bool),
("/path/bool/1", 200, True),
("/path/bool/0", 200, False),
("/path/bool/true", 200, True),
"parameters": [
{
"required": True,
- "schema": {"title": "Item_Id", "type": "string"},
+ "schema": {"title": "Item Id", "type": "string"},
"name": "item_id",
"in": "path",
}
"type": "object",
"properties": {
"grant_type": {
- "title": "Grant_Type",
+ "title": "Grant Type",
"pattern": "password",
"type": "string",
},
"username": {"title": "Username", "type": "string"},
"password": {"title": "Password", "type": "string"},
"scope": {"title": "Scope", "type": "string", "default": ""},
- "client_id": {"title": "Client_Id", "type": "string"},
- "client_secret": {"title": "Client_Secret", "type": "string"},
+ "client_id": {"title": "Client Id", "type": "string"},
+ "client_secret": {"title": "Client Secret", "type": "string"},
},
},
"ValidationError": {
"type": "object",
"properties": {
"grant_type": {
- "title": "Grant_Type",
+ "title": "Grant Type",
"pattern": "password",
"type": "string",
},
"username": {"title": "Username", "type": "string"},
"password": {"title": "Password", "type": "string"},
"scope": {"title": "Scope", "type": "string", "default": ""},
- "client_id": {"title": "Client_Id", "type": "string"},
- "client_secret": {"title": "Client_Secret", "type": "string"},
+ "client_id": {"title": "Client Id", "type": "string"},
+ "client_secret": {"title": "Client Secret", "type": "string"},
},
},
"ValidationError": {
y: int
-@app.get("/", response_model=Model, response_model_skip_defaults=True)
+@app.get("/", response_model=Model, response_model_exclude_unset=True)
def get() -> ModelSubclass:
return ModelSubclass(sub={}, y=1)
"parameters": [
{
"required": True,
- "schema": {"title": "Item_Id", "type": "string"},
+ "schema": {"title": "Item Id", "type": "string"},
"name": "item_id",
"in": "path",
}
"parameters": [
{
"required": True,
- "schema": {"title": "Item_Id", "type": "string"},
+ "schema": {"title": "Item Id", "type": "string"},
"name": "item_id",
"in": "path",
}
"parameters": [
{
"required": True,
- "schema": {"title": "Item_Id", "type": "string"},
+ "schema": {"title": "Item Id", "type": "string"},
"name": "item_id",
"in": "path",
}
"parameters": [
{
"required": True,
- "schema": {"title": "Item_Id", "type": "string"},
+ "schema": {"title": "Item Id", "type": "string"},
"name": "item_id",
"in": "path",
},
"parameters": [
{
"required": True,
- "schema": {"title": "Item_Id", "type": "string"},
+ "schema": {"title": "Item Id", "type": "string"},
"name": "item_id",
"in": "path",
}
"parameters": [
{
"required": True,
- "schema": {"title": "Item_Id", "type": "string"},
+ "schema": {"title": "Item Id", "type": "string"},
"name": "item_id",
"in": "path",
},
"content": {
"application/json": {
"schema": {
- "title": "Response_Read_Notes_Notes__Get",
+ "title": "Response Read Notes Notes Get",
"type": "array",
"items": {"$ref": "#/components/schemas/Note"},
}
"parameters": [
{
"required": True,
- "schema": {"title": "Item_Id", "type": "string"},
+ "schema": {"title": "Item Id", "type": "string"},
"name": "item_id",
"in": "path",
},
"parameters": [
{
"required": True,
- "schema": {"title": "Item_Id", "type": "string"},
+ "schema": {"title": "Item Id", "type": "string"},
"name": "item_id",
"in": "path",
},
"parameters": [
{
"required": True,
- "schema": {"title": "Item_Id", "type": "integer"},
+ "schema": {"title": "Item Id", "type": "integer"},
"name": "item_id",
"in": "path",
}
"type": "object",
"properties": {
"username": {"title": "Username", "type": "string"},
- "full_name": {"title": "Full_Name", "type": "string"},
+ "full_name": {"title": "Full Name", "type": "string"},
},
},
"Body_update_item_items__item_id__put": {
from body_schema.tutorial001 import app
+# TODO: remove when removing support for Pydantic < 1.0.0
+try:
+ from pydantic import Field # noqa
+except ImportError: # pragma: nocover
+ import pydantic
+
+ pydantic.Field = pydantic.Schema
+
+
client = TestClient(app)
"parameters": [
{
"required": True,
- "schema": {"title": "Item_Id", "type": "integer"},
+ "schema": {"title": "Item Id", "type": "integer"},
"name": "item_id",
"in": "path",
}
"parameters": [
{
"required": True,
- "schema": {"title": "Item_Id", "type": "string"},
+ "schema": {"title": "Item Id", "type": "string"},
"name": "item_id",
"in": "path",
}
"parameters": [
{
"required": True,
- "schema": {"title": "Item_Id", "type": "string"},
+ "schema": {"title": "Item Id", "type": "string"},
"name": "item_id",
"in": "path",
}
"parameters": [
{
"required": False,
- "schema": {"title": "Ads_Id", "type": "string"},
+ "schema": {"title": "Ads Id", "type": "string"},
"name": "ads_id",
"in": "cookie",
}
"parameters": [
{
"required": True,
- "schema": {"title": "Item_Id", "type": "string"},
+ "schema": {"title": "Item Id", "type": "string"},
"name": "item_id",
"in": "path",
}
{
"required": True,
"schema": {
- "title": "Item_Id",
+ "title": "Item Id",
"type": "string",
"format": "uuid",
},
"type": "object",
"properties": {
"start_datetime": {
- "title": "Start_Datetime",
+ "title": "Start Datetime",
"type": "string",
"format": "date-time",
},
"end_datetime": {
- "title": "End_Datetime",
+ "title": "End Datetime",
"type": "string",
"format": "date-time",
},
"repeat_at": {
- "title": "Repeat_At",
+ "title": "Repeat At",
"type": "string",
"format": "time",
},
"process_after": {
- "title": "Process_After",
+ "title": "Process After",
"type": "number",
"format": "time-delta",
},
"content": {
"application/json": {
"schema": {
- "title": "Response_Read_Item_Items__Item_Id__Get",
+ "title": "Response Read Item Items Item Id Get",
"anyOf": [
{"$ref": "#/components/schemas/PlaneItem"},
{"$ref": "#/components/schemas/CarItem"},
"parameters": [
{
"required": True,
- "schema": {"title": "Item_Id", "type": "string"},
+ "schema": {"title": "Item Id", "type": "string"},
"name": "item_id",
"in": "path",
}
"content": {
"application/json": {
"schema": {
- "title": "Response_Read_Items_Items__Get",
+ "title": "Response Read Items Items Get",
"type": "array",
"items": {"$ref": "#/components/schemas/Item"},
}
"content": {
"application/json": {
"schema": {
- "title": "Response_Read_Keyword_Weights_Keyword-Weights__Get",
+ "title": "Response Read Keyword Weights Keyword-Weights Get",
"type": "object",
"additionalProperties": {"type": "number"},
}
"parameters": [
{
"required": True,
- "schema": {"title": "Item_Id", "type": "string"},
+ "schema": {"title": "Item Id", "type": "string"},
"name": "item_id",
"in": "path",
}
"parameters": [
{
"required": True,
- "schema": {"title": "Item_Id", "type": "string"},
+ "schema": {"title": "Item Id", "type": "string"},
"name": "item_id",
"in": "path",
}
"parameters": [
{
"required": True,
- "schema": {"title": "Item_Id", "type": "integer"},
+ "schema": {"title": "Item Id", "type": "integer"},
"name": "item_id",
"in": "path",
}
"parameters": [
{
"required": True,
- "schema": {"title": "Item_Id", "type": "integer"},
+ "schema": {"title": "Item Id", "type": "integer"},
"name": "item_id",
"in": "path",
}
"parameters": [
{
"required": True,
- "schema": {"title": "File_Path", "type": "string"},
+ "schema": {"title": "File Path", "type": "string"},
"name": "file_path",
"in": "path",
}
{
"required": True,
"schema": {
- "title": "Model_Name",
+ "title": "Model Name",
"enum": ["alexnet", "resnet", "lenet"],
"type": "string",
},
"parameters": [
{
"required": True,
- "schema": {"title": "Item_Id", "type": "string"},
+ "schema": {"title": "Item Id", "type": "string"},
"name": "item_id",
"in": "path",
},
"parameters": [
{
"required": True,
- "schema": {"title": "Item_Id", "type": "string"},
+ "schema": {"title": "Item Id", "type": "string"},
"name": "item_id",
"in": "path",
},
"parameters": [
{
"required": True,
- "schema": {"title": "Item_Id", "type": "string"},
+ "schema": {"title": "Item Id", "type": "string"},
"name": "item_id",
"in": "path",
},
"properties": {
"username": {"title": "Username", "type": "string"},
"email": {"title": "Email", "type": "string", "format": "email"},
- "full_name": {"title": "Full_Name", "type": "string"},
+ "full_name": {"title": "Full Name", "type": "string"},
},
},
"UserIn": {
"username": {"title": "Username", "type": "string"},
"password": {"title": "Password", "type": "string"},
"email": {"title": "Email", "type": "string", "format": "email"},
- "full_name": {"title": "Full_Name", "type": "string"},
+ "full_name": {"title": "Full Name", "type": "string"},
},
},
"ValidationError": {
"parameters": [
{
"required": True,
- "schema": {"title": "Item_Id", "type": "string"},
+ "schema": {"title": "Item Id", "type": "string"},
"name": "item_id",
"in": "path",
}
"parameters": [
{
"required": True,
- "schema": {"title": "Item_Id", "type": "string"},
+ "schema": {"title": "Item Id", "type": "string"},
"name": "item_id",
"in": "path",
}
"parameters": [
{
"required": True,
- "schema": {"title": "Item_Id", "type": "string"},
+ "schema": {"title": "Item Id", "type": "string"},
"name": "item_id",
"in": "path",
}
"parameters": [
{
"required": True,
- "schema": {"title": "Item_Id", "type": "string"},
+ "schema": {"title": "Item Id", "type": "string"},
"name": "item_id",
"in": "path",
}
"parameters": [
{
"required": True,
- "schema": {"title": "Item_Id", "type": "string"},
+ "schema": {"title": "Item Id", "type": "string"},
"name": "item_id",
"in": "path",
}
"type": "object",
"properties": {
"grant_type": {
- "title": "Grant_Type",
+ "title": "Grant Type",
"pattern": "password",
"type": "string",
},
"username": {"title": "Username", "type": "string"},
"password": {"title": "Password", "type": "string"},
"scope": {"title": "Scope", "type": "string", "default": ""},
- "client_id": {"title": "Client_Id", "type": "string"},
- "client_secret": {"title": "Client_Secret", "type": "string"},
+ "client_id": {"title": "Client Id", "type": "string"},
+ "client_secret": {"title": "Client Secret", "type": "string"},
},
},
"ValidationError": {
"properties": {
"username": {"title": "Username", "type": "string"},
"email": {"title": "Email", "type": "string"},
- "full_name": {"title": "Full_Name", "type": "string"},
+ "full_name": {"title": "Full Name", "type": "string"},
"disabled": {"title": "Disabled", "type": "boolean"},
},
},
"required": ["access_token", "token_type"],
"type": "object",
"properties": {
- "access_token": {"title": "Access_Token", "type": "string"},
- "token_type": {"title": "Token_Type", "type": "string"},
+ "access_token": {"title": "Access Token", "type": "string"},
+ "token_type": {"title": "Token Type", "type": "string"},
},
},
"Body_login_for_access_token_token_post": {
"type": "object",
"properties": {
"grant_type": {
- "title": "Grant_Type",
+ "title": "Grant Type",
"pattern": "password",
"type": "string",
},
"username": {"title": "Username", "type": "string"},
"password": {"title": "Password", "type": "string"},
"scope": {"title": "Scope", "type": "string", "default": ""},
- "client_id": {"title": "Client_Id", "type": "string"},
- "client_secret": {"title": "Client_Secret", "type": "string"},
+ "client_id": {"title": "Client Id", "type": "string"},
+ "client_secret": {"title": "Client Secret", "type": "string"},
},
},
"ValidationError": {
"content": {
"application/json": {
"schema": {
- "title": "Response_Read_Users_Users__Get",
+ "title": "Response Read Users Users Get",
"type": "array",
"items": {"$ref": "#/components/schemas/User"},
}
"parameters": [
{
"required": True,
- "schema": {"title": "User_Id", "type": "integer"},
+ "schema": {"title": "User Id", "type": "integer"},
"name": "user_id",
"in": "path",
}
"parameters": [
{
"required": True,
- "schema": {"title": "User_Id", "type": "integer"},
+ "schema": {"title": "User Id", "type": "integer"},
"name": "user_id",
"in": "path",
}
"content": {
"application/json": {
"schema": {
- "title": "Response_Read_Items_Items__Get",
+ "title": "Response Read Items Items Get",
"type": "array",
"items": {"$ref": "#/components/schemas/Item"},
}
"title": {"title": "Title", "type": "string"},
"description": {"title": "Description", "type": "string"},
"id": {"title": "Id", "type": "integer"},
- "owner_id": {"title": "Owner_Id", "type": "integer"},
+ "owner_id": {"title": "Owner Id", "type": "integer"},
},
},
"User": {
"properties": {
"email": {"title": "Email", "type": "string"},
"id": {"title": "Id", "type": "integer"},
- "is_active": {"title": "Is_Active", "type": "boolean"},
+ "is_active": {"title": "Is Active", "type": "boolean"},
"items": {
"title": "Items",
"type": "array",
"content": {
"application/json": {
"schema": {
- "title": "Response_Read_Users_Users__Get",
+ "title": "Response Read Users Users Get",
"type": "array",
"items": {"$ref": "#/components/schemas/User"},
}
"parameters": [
{
"required": True,
- "schema": {"title": "User_Id", "type": "integer"},
+ "schema": {"title": "User Id", "type": "integer"},
"name": "user_id",
"in": "path",
}
"parameters": [
{
"required": True,
- "schema": {"title": "User_Id", "type": "integer"},
+ "schema": {"title": "User Id", "type": "integer"},
"name": "user_id",
"in": "path",
}
"content": {
"application/json": {
"schema": {
- "title": "Response_Read_Items_Items__Get",
+ "title": "Response Read Items Items Get",
"type": "array",
"items": {"$ref": "#/components/schemas/Item"},
}
"title": {"title": "Title", "type": "string"},
"description": {"title": "Description", "type": "string"},
"id": {"title": "Id", "type": "integer"},
- "owner_id": {"title": "Owner_Id", "type": "integer"},
+ "owner_id": {"title": "Owner Id", "type": "integer"},
},
},
"User": {
"properties": {
"email": {"title": "Email", "type": "string"},
"id": {"title": "Id", "type": "integer"},
- "is_active": {"title": "Is_Active", "type": "boolean"},
+ "is_active": {"title": "Is Active", "type": "boolean"},
"items": {
"title": "Items",
"type": "array",