]> git.ipfire.org Git - thirdparty/fastapi/fastapi.git/commitdiff
:sparkles: Add support for Pydantic v1 and above :tada: (#646)
authordmontagu <35119617+dmontagu@users.noreply.github.com>
Wed, 27 Nov 2019 19:32:02 +0000 (11:32 -0800)
committerSebastián Ramírez <tiangolo@gmail.com>
Wed, 27 Nov 2019 19:32:02 +0000 (20:32 +0100)
* Make compatible with pydantic v1

* Remove unused import

* Remove unused ignores

* Update pydantic version

* Fix minor formatting issue

* :rewind: Revert removing iterate_in_threadpool

* :sparkles: Add backwards compatibility with Pydantic 0.32.2

with deprecation warnings

* :white_check_mark: Update tests to not break when using Pydantic < 1.0.0

* :memo: Update docs for Pydantic version 1.0.0

* :pushpin: Update Pydantic range version to support from 0.32.2

* :art: Format test imports

* :sparkles: Add support for Pydantic < 1.2 for populate_validators

* :sparkles: Add backwards compatibility for Pydantic < 1.2.0 with required fields

* :pushpin: Relax requirement for Pydantic to < 2.0.0 :tada: :rocket:

* :green_heart: Update pragma coverage for older Pydantic versions

66 files changed:
Pipfile
docs/src/body_schema/tutorial001.py
docs/src/body_updates/tutorial002.py
docs/src/extra_models/tutorial001.py
docs/src/extra_models/tutorial002.py
docs/src/response_model/tutorial002.py
docs/src/response_model/tutorial003.py
docs/src/response_model/tutorial004.py
docs/tutorial/body-schema.md
docs/tutorial/body-updates.md
docs/tutorial/extra-models.md
docs/tutorial/response-model.md
fastapi/applications.py
fastapi/concurrency.py
fastapi/dependencies/models.py
fastapi/dependencies/utils.py
fastapi/encoders.py
fastapi/exceptions.py
fastapi/openapi/models.py
fastapi/openapi/utils.py
fastapi/params.py
fastapi/routing.py
fastapi/utils.py
pyproject.toml
tests/test_application.py
tests/test_extra_routes.py
tests/test_infer_param_optionality.py
tests/test_jsonable_encoder.py
tests/test_path.py
tests/test_put_no_body.py
tests/test_security_oauth2.py
tests/test_security_oauth2_optional.py
tests/test_skip_defaults.py
tests/test_starlette_exception.py
tests/test_tutorial/test_additional_responses/test_tutorial001.py
tests/test_tutorial/test_additional_responses/test_tutorial002.py
tests/test_tutorial/test_additional_responses/test_tutorial003.py
tests/test_tutorial/test_additional_responses/test_tutorial004.py
tests/test_tutorial/test_async_sql_databases/test_tutorial001.py
tests/test_tutorial/test_bigger_applications/test_main.py
tests/test_tutorial/test_body_multiple_params/test_tutorial003.py
tests/test_tutorial/test_body_schema/test_tutorial001.py
tests/test_tutorial/test_body_updates/test_tutorial001.py
tests/test_tutorial/test_cookie_params/test_tutorial001.py
tests/test_tutorial/test_events/test_tutorial001.py
tests/test_tutorial/test_extra_data_types/test_tutorial001.py
tests/test_tutorial/test_extra_models/test_tutorial003.py
tests/test_tutorial/test_extra_models/test_tutorial004.py
tests/test_tutorial/test_extra_models/test_tutorial005.py
tests/test_tutorial/test_handling_errors/test_tutorial001.py
tests/test_tutorial/test_handling_errors/test_tutorial002.py
tests/test_tutorial/test_handling_errors/test_tutorial004.py
tests/test_tutorial/test_handling_errors/test_tutorial005.py
tests/test_tutorial/test_path_params/test_tutorial004.py
tests/test_tutorial/test_path_params/test_tutorial005.py
tests/test_tutorial/test_query_params/test_tutorial005.py
tests/test_tutorial/test_query_params/test_tutorial006.py
tests/test_tutorial/test_query_params/test_tutorial007.py
tests/test_tutorial/test_response_model/test_tutorial003.py
tests/test_tutorial/test_response_model/test_tutorial004.py
tests/test_tutorial/test_response_model/test_tutorial005.py
tests/test_tutorial/test_response_model/test_tutorial006.py
tests/test_tutorial/test_security/test_tutorial003.py
tests/test_tutorial/test_security/test_tutorial005.py
tests/test_tutorial/test_sql_databases/test_sql_databases.py
tests/test_tutorial/test_sql_databases/test_sql_databases_middleware.py

diff --git a/Pipfile b/Pipfile
index 9b8d31bea80d5900373e543938e462532f40c3e6..4f1ba44d8b2a485a5f92d03284df5b2701eba2da 100644 (file)
--- a/Pipfile
+++ b/Pipfile
@@ -26,7 +26,7 @@ uvicorn = "*"
 
 [packages]
 starlette = "==0.12.9"
-pydantic = "==0.32.2"
+pydantic = "==1.0.0"
 databases = {extras = ["sqlite"],version = "*"}
 hypercorn = "*"
 orjson = "*"
index 6c8b101ba220a5fca81d045aacc3bdcdcce4411d..7b2c9454d82f4379b556c421bb7e6125e02a276b 100644 (file)
@@ -1,13 +1,13 @@
 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
 
 
index e10fbb9213cdd3b525e77378946a993d767d9895..56cb8c4dce663809c681f84e0a3abc5e114e4973 100644 (file)
@@ -31,7 +31,7 @@ async def read_item(item_id: str):
 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
index aa8e7dad456b1b7cf7ae944a955f647af6c551a7..08d3659b0148d7797035ed6167b2f530b7216679 100644 (file)
@@ -1,6 +1,5 @@
 from fastapi import FastAPI
-from pydantic import BaseModel
-from pydantic.types import EmailStr
+from pydantic import BaseModel, EmailStr
 
 app = FastAPI()
 
index 605baf91f05e95c5763730b6a5983edd036847cf..ab680eca0adc3d8a3ccdb29c99bc2ab3ef061158 100644 (file)
@@ -1,6 +1,5 @@
 from fastapi import FastAPI
-from pydantic import BaseModel
-from pydantic.types import EmailStr
+from pydantic import BaseModel, EmailStr
 
 app = FastAPI()
 
index 3fb475b9d9cc74862f2f14756156497aa6fd5662..b36b2a347cdbdff3717f13ea9b83ece537aa64cb 100644 (file)
@@ -1,6 +1,5 @@
 from fastapi import FastAPI
-from pydantic import BaseModel
-from pydantic.types import EmailStr
+from pydantic import BaseModel, EmailStr
 
 app = FastAPI()
 
index c8ea361d8d45bc197dbbce97acb6a9f32eb52f5d..a73372901416de5e0ad66669955c8a92f6a08374 100644 (file)
@@ -1,6 +1,5 @@
 from fastapi import FastAPI
-from pydantic import BaseModel
-from pydantic.types import EmailStr
+from pydantic import BaseModel, EmailStr
 
 app = FastAPI()
 
index bbfd855bbd7eaf1a491c33f3afca824d921ba5b5..7a2373e0cf61f11c42c5c6b37441243796b32fd6 100644 (file)
@@ -21,6 +21,6 @@ items = {
 }
 
 
-@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]
index 45ea5d93d4022d77e061652b1603fd06d63cfc0a..9f0a493701b0989dbfb3f2d16014d53292c6a44e 100644 (file)
@@ -1,6 +1,6 @@
-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:
 
@@ -9,32 +9,34 @@ 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.
 
@@ -55,6 +57,6 @@ And it would look in the `/docs` like this:
 
 ## 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.
index 0850d5d44089b9f031332d88f0dc729e1a15f37f..33880aad68af570f972e2e268db7112acf87bc80 100644 (file)
@@ -41,15 +41,15 @@ This means that you can send only the data that you want to update, leaving the
 
     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!}
@@ -72,7 +72,7 @@ In summary, to apply partial updates you would:
 * (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`).
index 5d301f64cf221a133281a3489dfab22b2049a79f..875ce8aec1e4cb8be4ada918fcd39f42947978c9 100644 (file)
@@ -15,7 +15,7 @@ This is especially the case for user models, because:
 
 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!}
 ```
 
@@ -148,7 +148,7 @@ All the data conversion, validation, documentation, etc. will still work as norm
 
 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!}
 ```
 
index 00c9b987c4a4ae89062e01ee0197b763a4ebf9f1..8057bd23b3fa85ed35c7f8f66602a169e327dbed 100644 (file)
@@ -33,13 +33,13 @@ But most importantly:
 
 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!}
 ```
 
@@ -56,19 +56,19 @@ But if we use the same model for another path operation, we could be sending our
 
 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!}
 ```
 
@@ -100,15 +100,15 @@ but you might want to omit them from the result if they were not actually stored
 
 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:
 
@@ -120,7 +120,7 @@ So, if you send a request to that *path operation* for the item with ID `foo`, t
 ```
 
 !!! 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
 
@@ -194,4 +194,4 @@ If you forget to use a `set` and use a `list` or `tuple` instead, FastAPI will s
 
 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.
index 000026b8384fc9fdfab4cdb73f3ec86c82bd0e82..ab1b77e6dcc4b6e56aba7043a79e24a715527357 100644 (file)
@@ -15,6 +15,7 @@ from fastapi.openapi.docs import (
 )
 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
@@ -159,11 +160,14 @@ class FastAPI(Starlette):
         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,
@@ -181,7 +185,9 @@ class FastAPI(Starlette):
             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,
@@ -205,11 +211,15 @@ class FastAPI(Starlette):
         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,
@@ -228,7 +238,9 @@ class FastAPI(Starlette):
                 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,
@@ -286,11 +298,14 @@ class FastAPI(Starlette):
         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,
@@ -306,7 +321,9 @@ class FastAPI(Starlette):
             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,
@@ -329,11 +346,14 @@ class FastAPI(Starlette):
         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,
@@ -349,7 +369,9 @@ class FastAPI(Starlette):
             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,
@@ -372,11 +394,14 @@ class FastAPI(Starlette):
         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,
@@ -392,7 +417,9 @@ class FastAPI(Starlette):
             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,
@@ -415,11 +442,14 @@ class FastAPI(Starlette):
         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,
@@ -435,7 +465,9 @@ class FastAPI(Starlette):
             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,
@@ -458,11 +490,14 @@ class FastAPI(Starlette):
         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,
@@ -478,7 +513,9 @@ class FastAPI(Starlette):
             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,
@@ -501,11 +538,14 @@ class FastAPI(Starlette):
         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,
@@ -521,7 +561,9 @@ class FastAPI(Starlette):
             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,
@@ -544,11 +586,14 @@ class FastAPI(Starlette):
         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,
@@ -564,7 +609,9 @@ class FastAPI(Starlette):
             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,
@@ -587,11 +634,14 @@ class FastAPI(Starlette):
         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,
@@ -607,7 +657,9 @@ class FastAPI(Starlette):
             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,
index 7006c1ad0c2fff324e4d5d1d6d2cc82b1d2d3a9e..03ed2d08d489ae0d51dc977032325281f1ddf1b0 100644 (file)
@@ -1,6 +1,7 @@
 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,
index 12f0679ed76cb954d3be93634658b646da924026..5fe99f3b46ce394cb2d728fbb4e23ecd811ef5ff 100644 (file)
@@ -1,7 +1,12 @@
 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)
 
@@ -16,11 +21,11 @@ class Dependant:
     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,
index 4745f173f0d6e628e4b9f1eb1db6da09de99fff5..54e27476249d94a9d15f9e783d206777fe004456 100644 (file)
@@ -27,13 +27,11 @@ from fastapi.dependencies.models import Dependant, SecurityRequirement
 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
@@ -41,20 +39,55 @@ from starlette.requests import Request
 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,
 }
 
 
@@ -150,12 +183,13 @@ def get_flat_dependant(
     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:
@@ -164,7 +198,7 @@ def is_scalar_field(field: Field) -> bool:
     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
     ):
@@ -239,7 +273,9 @@ def get_dependant(
             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
@@ -250,7 +286,8 @@ def get_dependant(
                 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,
             )
@@ -262,8 +299,9 @@ def get_dependant(
         ) 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
@@ -293,59 +331,82 @@ def add_non_field_param_to_dependency(
 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)
 
@@ -506,7 +567,7 @@ async def solve_dependencies(
 
 
 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 = {}
@@ -518,21 +579,32 @@ def request_params_to_args(
             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):
@@ -543,14 +615,15 @@ def request_params_to_args(
 
 
 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:
@@ -564,31 +637,38 @@ async def request_body_to_args(
                     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)
             ):
@@ -605,31 +685,45 @@ async def request_body_to_args(
     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
@@ -638,30 +732,45 @@ def get_body_field(*, dependant: Dependant, name: str) -> Optional[Field]:
         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
index 0f4fd8346aab4ff8d1c8d71a1dcdab03d2e07595..bececedae8f44c65163650adfb4f32744b4b18f7 100644 (file)
@@ -2,6 +2,7 @@ from enum import Enum
 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
 
@@ -14,24 +15,40 @@ def jsonable_encoder(
     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,
@@ -55,7 +72,7 @@ def jsonable_encoder(
                 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,
@@ -63,7 +80,7 @@ def jsonable_encoder(
                 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,
@@ -79,7 +96,7 @@ def jsonable_encoder(
                     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,
@@ -107,7 +124,7 @@ def jsonable_encoder(
     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,
index 310da940d8e8feb598c1333915d0859167865129..d4b1329d7d8909a54179f0a1a90ef550bad287c8 100644 (file)
@@ -1,6 +1,7 @@
 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
@@ -15,11 +16,21 @@ class HTTPException(StarletteHTTPException):
         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
index e5c50070e04b66d3e289a9719c243358dd3d0422..126b2dfeb8cd582bdd6b7b51a8040dc8ec17fb9f 100644 (file)
@@ -1,17 +1,25 @@
-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"
@@ -24,13 +32,13 @@ except ImportError:  # pragma: no cover
 
 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):
@@ -49,13 +57,13 @@ class ServerVariable(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):
@@ -73,32 +81,32 @@ class XML(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
@@ -119,17 +127,17 @@ class Schema(SchemaBase):
     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):
@@ -149,9 +157,7 @@ class Encoding(BaseModel):
 
 
 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
@@ -165,9 +171,7 @@ class ParameterBase(BaseModel):
     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
@@ -176,7 +180,7 @@ class ParameterBase(BaseModel):
 
 class Parameter(ParameterBase):
     name: str
-    in_: ParameterInType = PSchema(..., alias="in")  # type: ignore
+    in_: ParameterInType = Field(..., alias="in")
 
 
 class Header(ParameterBase):
@@ -227,7 +231,7 @@ class Operation(BaseModel):
 
 
 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
@@ -255,7 +259,7 @@ class SecuritySchemeType(Enum):
 
 
 class SecurityBase(BaseModel):
-    type_: SecuritySchemeType = PSchema(..., alias="type")  # type: ignore
+    type_: SecuritySchemeType = Field(..., alias="type")
     description: Optional[str] = None
 
 
@@ -266,13 +270,13 @@ class APIKeyIn(Enum):
 
 
 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
 
 
@@ -311,12 +315,12 @@ class OAuthFlows(BaseModel):
 
 
 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
 
 
index 311fb25a733f3018b7f6d9b9fa156187c7ce928a..d2dd620813e417c49644f45ab9ddbcfff15b2671 100644 (file)
@@ -14,16 +14,23 @@ from fastapi.openapi.models import OpenAPI
 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",
@@ -57,7 +64,7 @@ status_code_ranges: Dict[str, str] = {
 }
 
 
-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
@@ -83,37 +90,37 @@ def get_openapi_security_definitions(flat_dependant: Dependant) -> Tuple[Dict, L
 
 
 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:
index 0541a3695839c4e49a6fe08a1c0b045deff796e7..3aa333ac75615c282e067f4a6d6b8a1ad2d9078b 100644 (file)
@@ -1,7 +1,11 @@
 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):
@@ -11,7 +15,7 @@ class ParamTypes(Enum):
     cookie = "cookie"
 
 
-class Param(Schema):
+class Param(FieldInfo):
     in_: ParamTypes
 
     def __init__(
@@ -199,7 +203,7 @@ class Cookie(Param):
         )
 
 
-class Body(Schema):
+class Body(FieldInfo):
     def __init__(
         self,
         default: Any,
index b2a900b7e01025aedb0c7341390cedb0e753ffe7..547d2c4b140d19f1a1cd3ebd88a89380d5ac4bab 100644 (file)
@@ -14,10 +14,15 @@ from fastapi.dependencies.utils import (
 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
@@ -34,20 +39,30 @@ from starlette.status import WS_1008_POLICY_VIOLATION
 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_)
@@ -60,7 +75,7 @@ def serialize_response(
             include=include,
             exclude=exclude,
             by_alias=by_alias,
-            skip_defaults=skip_defaults,
+            exclude_unset=exclude_unset,
         )
     else:
         return jsonable_encoder(response)
@@ -68,19 +83,19 @@ def serialize_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:
@@ -122,7 +137,7 @@ def get_request_handler(
                 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,
@@ -199,7 +214,7 @@ class APIRoute(routing.Route):
         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,
@@ -220,15 +235,26 @@ class APIRoute(routing.Route):
                 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
@@ -236,9 +262,9 @@ class APIRoute(routing.Route):
             # 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
@@ -267,18 +293,29 @@ class APIRoute(routing.Route):
                     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
@@ -286,7 +323,7 @@ class APIRoute(routing.Route):
         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
 
@@ -313,7 +350,7 @@ class APIRoute(routing.Route):
             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,
         )
 
@@ -352,12 +389,15 @@ class APIRouter(routing.Router):
         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,
@@ -376,7 +416,9 @@ class APIRouter(routing.Router):
             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,
@@ -402,11 +444,15 @@ class APIRouter(routing.Router):
         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,
@@ -425,7 +471,9 @@ class APIRouter(routing.Router):
                 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,
@@ -493,7 +541,7 @@ class APIRouter(routing.Router):
                     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,
@@ -533,11 +581,14 @@ class APIRouter(routing.Router):
         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,
@@ -554,7 +605,9 @@ class APIRouter(routing.Router):
             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,
@@ -577,11 +630,14 @@ class APIRouter(routing.Router):
         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,
@@ -598,7 +654,9 @@ class APIRouter(routing.Router):
             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,
@@ -621,11 +679,14 @@ class APIRouter(routing.Router):
         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,
@@ -642,7 +703,9 @@ class APIRouter(routing.Router):
             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,
@@ -665,11 +728,14 @@ class APIRouter(routing.Router):
         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,
@@ -686,7 +752,9 @@ class APIRouter(routing.Router):
             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,
@@ -709,11 +777,14 @@ class APIRouter(routing.Router):
         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,
@@ -730,7 +801,9 @@ class APIRouter(routing.Router):
             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,
@@ -753,11 +826,14 @@ class APIRouter(routing.Router):
         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,
@@ -774,7 +850,9 @@ class APIRouter(routing.Router):
             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,
@@ -797,11 +875,14 @@ class APIRouter(routing.Router):
         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,
@@ -818,7 +899,9 @@ class APIRouter(routing.Router):
             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,
@@ -841,11 +924,14 @@ class APIRouter(routing.Router):
         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,
@@ -862,7 +948,9 @@ class APIRouter(routing.Router):
             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,
index 9e4210f86ef849461ad6f6c5d0de24112f0ec475..b817a10d0fe70956906795e8670a69cbf96cd3cf 100644 (file)
@@ -1,26 +1,60 @@
+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:
@@ -51,7 +85,7 @@ def get_path_param_names(path: str) -> Set[str]:
     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
@@ -64,22 +98,36 @@ def create_cloned_field(field: Field) -> Field:
         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:
@@ -89,11 +137,19 @@ def create_cloned_field(field: Field) -> Field:
     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
 
 
index 21cade15cc51cbe30034be14024ea7162a09362b..2c62c572b5229f1f0630e8b9109f5b7283dc1694 100644 (file)
@@ -20,7 +20,7 @@ classifiers = [
 ]
 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"
index 674c38a5f0c749c95225722026fdc93c118318f3..bdbff1cf6ff0d8078c471e4645c52662f10324f9 100644 (file)
@@ -68,7 +68,7 @@ openapi_schema = {
                 "parameters": [
                     {
                         "required": True,
-                        "schema": {"title": "Item_Id"},
+                        "schema": {"title": "Item Id"},
                         "name": "item_id",
                         "in": "path",
                     }
@@ -98,7 +98,7 @@ openapi_schema = {
                 "parameters": [
                     {
                         "required": True,
-                        "schema": {"title": "Item_Id", "type": "string"},
+                        "schema": {"title": "Item Id", "type": "string"},
                         "name": "item_id",
                         "in": "path",
                     }
@@ -128,7 +128,7 @@ openapi_schema = {
                 "parameters": [
                     {
                         "required": True,
-                        "schema": {"title": "Item_Id", "type": "integer"},
+                        "schema": {"title": "Item Id", "type": "integer"},
                         "name": "item_id",
                         "in": "path",
                     }
@@ -158,7 +158,7 @@ openapi_schema = {
                 "parameters": [
                     {
                         "required": True,
-                        "schema": {"title": "Item_Id", "type": "number"},
+                        "schema": {"title": "Item Id", "type": "number"},
                         "name": "item_id",
                         "in": "path",
                     }
@@ -188,7 +188,7 @@ openapi_schema = {
                 "parameters": [
                     {
                         "required": True,
-                        "schema": {"title": "Item_Id", "type": "boolean"},
+                        "schema": {"title": "Item Id", "type": "boolean"},
                         "name": "item_id",
                         "in": "path",
                     }
@@ -218,7 +218,7 @@ openapi_schema = {
                 "parameters": [
                     {
                         "required": True,
-                        "schema": {"title": "Item_Id", "type": "string"},
+                        "schema": {"title": "Item Id", "type": "string"},
                         "name": "item_id",
                         "in": "path",
                     }
@@ -248,7 +248,7 @@ openapi_schema = {
                 "parameters": [
                     {
                         "required": True,
-                        "schema": {"title": "Item_Id", "type": "string"},
+                        "schema": {"title": "Item Id", "type": "string"},
                         "name": "item_id",
                         "in": "path",
                     }
@@ -279,7 +279,7 @@ openapi_schema = {
                     {
                         "required": True,
                         "schema": {
-                            "title": "Item_Id",
+                            "title": "Item Id",
                             "minLength": 3,
                             "type": "string",
                         },
@@ -313,7 +313,7 @@ openapi_schema = {
                     {
                         "required": True,
                         "schema": {
-                            "title": "Item_Id",
+                            "title": "Item Id",
                             "maxLength": 3,
                             "type": "string",
                         },
@@ -347,7 +347,7 @@ openapi_schema = {
                     {
                         "required": True,
                         "schema": {
-                            "title": "Item_Id",
+                            "title": "Item Id",
                             "maxLength": 3,
                             "minLength": 2,
                             "type": "string",
@@ -382,7 +382,7 @@ openapi_schema = {
                     {
                         "required": True,
                         "schema": {
-                            "title": "Item_Id",
+                            "title": "Item Id",
                             "exclusiveMinimum": 3.0,
                             "type": "number",
                         },
@@ -416,7 +416,7 @@ openapi_schema = {
                     {
                         "required": True,
                         "schema": {
-                            "title": "Item_Id",
+                            "title": "Item Id",
                             "exclusiveMinimum": 0.0,
                             "type": "number",
                         },
@@ -450,7 +450,7 @@ openapi_schema = {
                     {
                         "required": True,
                         "schema": {
-                            "title": "Item_Id",
+                            "title": "Item Id",
                             "minimum": 3.0,
                             "type": "number",
                         },
@@ -484,7 +484,7 @@ openapi_schema = {
                     {
                         "required": True,
                         "schema": {
-                            "title": "Item_Id",
+                            "title": "Item Id",
                             "exclusiveMaximum": 3.0,
                             "type": "number",
                         },
@@ -518,7 +518,7 @@ openapi_schema = {
                     {
                         "required": True,
                         "schema": {
-                            "title": "Item_Id",
+                            "title": "Item Id",
                             "exclusiveMaximum": 0.0,
                             "type": "number",
                         },
@@ -552,7 +552,7 @@ openapi_schema = {
                     {
                         "required": True,
                         "schema": {
-                            "title": "Item_Id",
+                            "title": "Item Id",
                             "maximum": 3.0,
                             "type": "number",
                         },
@@ -586,7 +586,7 @@ openapi_schema = {
                     {
                         "required": True,
                         "schema": {
-                            "title": "Item_Id",
+                            "title": "Item Id",
                             "exclusiveMaximum": 3.0,
                             "exclusiveMinimum": 1.0,
                             "type": "number",
@@ -621,7 +621,7 @@ openapi_schema = {
                     {
                         "required": True,
                         "schema": {
-                            "title": "Item_Id",
+                            "title": "Item Id",
                             "maximum": 3.0,
                             "minimum": 1.0,
                             "type": "number",
@@ -656,7 +656,7 @@ openapi_schema = {
                     {
                         "required": True,
                         "schema": {
-                            "title": "Item_Id",
+                            "title": "Item Id",
                             "exclusiveMaximum": 3.0,
                             "type": "integer",
                         },
@@ -690,7 +690,7 @@ openapi_schema = {
                     {
                         "required": True,
                         "schema": {
-                            "title": "Item_Id",
+                            "title": "Item Id",
                             "exclusiveMinimum": 3.0,
                             "type": "integer",
                         },
@@ -724,7 +724,7 @@ openapi_schema = {
                     {
                         "required": True,
                         "schema": {
-                            "title": "Item_Id",
+                            "title": "Item Id",
                             "maximum": 3.0,
                             "type": "integer",
                         },
@@ -758,7 +758,7 @@ openapi_schema = {
                     {
                         "required": True,
                         "schema": {
-                            "title": "Item_Id",
+                            "title": "Item Id",
                             "minimum": 3.0,
                             "type": "integer",
                         },
@@ -792,7 +792,7 @@ openapi_schema = {
                     {
                         "required": True,
                         "schema": {
-                            "title": "Item_Id",
+                            "title": "Item Id",
                             "exclusiveMaximum": 3.0,
                             "exclusiveMinimum": 1.0,
                             "type": "integer",
@@ -827,7 +827,7 @@ openapi_schema = {
                     {
                         "required": True,
                         "schema": {
-                            "title": "Item_Id",
+                            "title": "Item Id",
                             "maximum": 3.0,
                             "minimum": 1.0,
                             "type": "integer",
index 812a48fea3f40389fd3086fbe1c6ad89c1dd85ee..f5bd7b3f20e71113b7fba0c622f2dcfb0f2b922d 100644 (file)
@@ -77,7 +77,7 @@ openapi_schema = {
                 "parameters": [
                     {
                         "required": True,
-                        "schema": {"title": "Item_Id", "type": "string"},
+                        "schema": {"title": "Item Id", "type": "string"},
                         "name": "item_id",
                         "in": "path",
                     }
@@ -105,7 +105,7 @@ openapi_schema = {
                 "parameters": [
                     {
                         "required": True,
-                        "schema": {"title": "Item_Id", "type": "string"},
+                        "schema": {"title": "Item Id", "type": "string"},
                         "name": "item_id",
                         "in": "path",
                     }
@@ -141,7 +141,7 @@ openapi_schema = {
                 "parameters": [
                     {
                         "required": True,
-                        "schema": {"title": "Item_Id", "type": "string"},
+                        "schema": {"title": "Item Id", "type": "string"},
                         "name": "item_id",
                         "in": "path",
                     }
@@ -169,7 +169,7 @@ openapi_schema = {
                 "parameters": [
                     {
                         "required": True,
-                        "schema": {"title": "Item_Id", "type": "string"},
+                        "schema": {"title": "Item Id", "type": "string"},
                         "name": "item_id",
                         "in": "path",
                     }
@@ -197,7 +197,7 @@ openapi_schema = {
                 "parameters": [
                     {
                         "required": True,
-                        "schema": {"title": "Item_Id", "type": "string"},
+                        "schema": {"title": "Item Id", "type": "string"},
                         "name": "item_id",
                         "in": "path",
                     }
@@ -233,7 +233,7 @@ openapi_schema = {
                 "parameters": [
                     {
                         "required": True,
-                        "schema": {"title": "Item_Id", "type": "string"},
+                        "schema": {"title": "Item Id", "type": "string"},
                         "name": "item_id",
                         "in": "path",
                     }
@@ -263,7 +263,7 @@ openapi_schema = {
                 "parameters": [
                     {
                         "required": True,
-                        "schema": {"title": "Item_Id", "type": "string"},
+                        "schema": {"title": "Item Id", "type": "string"},
                         "name": "item_id",
                         "in": "path",
                     }
index 79fa716fe7aa99f3794f1f00cff13be644812af9..3175f72d30b80d444f3dbfafe2a837996f61aeee 100644 (file)
@@ -110,7 +110,7 @@ def test_schema_1():
 
     d = {
         "required": True,
-        "schema": {"title": "User_Id", "type": "string"},
+        "schema": {"title": "User Id", "type": "string"},
         "name": "user_id",
         "in": "path",
     }
@@ -127,7 +127,7 @@ def test_schema_2():
 
     d = {
         "required": False,
-        "schema": {"title": "User_Id", "type": "string"},
+        "schema": {"title": "User Id", "type": "string"},
         "name": "user_id",
         "in": "query",
     }
index f079059edc3566cfb0ee4c4a608cdf53bd13ec66..0975430d78e31a35dcb4affd5fdc14371424fcb8 100644 (file)
@@ -3,7 +3,13 @@ from enum import Enum
 
 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:
@@ -60,7 +66,7 @@ class ModelWithConfig(BaseModel):
 
 
 class ModelWithAlias(BaseModel):
-    foo: str = Schema(..., alias="Foo")
+    foo: str = Field(..., alias="Foo")
 
 
 def test_encode_class():
index cde7a9d02b6df955c89330bfcf8683389fb45d6a..f1ef58038b0e426b99f7b69e3a87707bb07cc58b 100644 (file)
@@ -18,6 +18,16 @@ def test_nonexistent():
     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": [
         {
@@ -173,10 +183,10 @@ response_less_than_equal_3 = {
         ("/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),
index f70d6773e06829f0a7389306c1e7e457b931df0e..1f1a1c00a7102f9992c99fc99fe231279d1f3ed9 100644 (file)
@@ -39,7 +39,7 @@ openapi_schema = {
                 "parameters": [
                     {
                         "required": True,
-                        "schema": {"title": "Item_Id", "type": "string"},
+                        "schema": {"title": "Item Id", "type": "string"},
                         "name": "item_id",
                         "in": "path",
                     }
index 5cf2592f314dc570a2b739b6344205f2cabb58d8..233123337078b003d9e4af6d7a1cca18dc7e4425 100644 (file)
@@ -99,15 +99,15 @@ openapi_schema = {
                 "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": {
index f85db00d34519a850461fbba91a3cbecc15b5279..cc2c5f0fd20e9b523865f6f35e86a7acc9371717 100644 (file)
@@ -103,15 +103,15 @@ openapi_schema = {
                 "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": {
index 9997ba7b2c4b107a916e415b1008f7517c944b38..36b2de53768db7691ff4fde0b469f65a93e0f670 100644 (file)
@@ -20,7 +20,7 @@ class ModelSubclass(Model):
     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)
 
index 74db57142f38cee7497985b316a2b0ee0423abee..706957b9913d3f15f4f1c932bd19aa237d0dbecc 100644 (file)
@@ -54,7 +54,7 @@ openapi_schema = {
                 "parameters": [
                     {
                         "required": True,
-                        "schema": {"title": "Item_Id", "type": "string"},
+                        "schema": {"title": "Item Id", "type": "string"},
                         "name": "item_id",
                         "in": "path",
                     }
@@ -84,7 +84,7 @@ openapi_schema = {
                 "parameters": [
                     {
                         "required": True,
-                        "schema": {"title": "Item_Id", "type": "string"},
+                        "schema": {"title": "Item Id", "type": "string"},
                         "name": "item_id",
                         "in": "path",
                     }
index 5036afda2e8446cb2fc1282e60577283959c884b..839bb1d53e00f06f2237fb53cfb0367871ddf082 100644 (file)
@@ -43,7 +43,7 @@ openapi_schema = {
                 "parameters": [
                     {
                         "required": True,
-                        "schema": {"title": "Item_Id", "type": "string"},
+                        "schema": {"title": "Item Id", "type": "string"},
                         "name": "item_id",
                         "in": "path",
                     }
index ac63ff2edf222e1c78cc71532a5c27ed993c941b..722ef83db6f861cb296f36ade8290a493257ae67 100644 (file)
@@ -39,7 +39,7 @@ openapi_schema = {
                 "parameters": [
                     {
                         "required": True,
-                        "schema": {"title": "Item_Id", "type": "string"},
+                        "schema": {"title": "Item Id", "type": "string"},
                         "name": "item_id",
                         "in": "path",
                     },
index aa90fff1709a6e93bfd19effd08a2762c9a1f0d6..4f270efdc24030ab5da97b38af94a5eb5a180708 100644 (file)
@@ -44,7 +44,7 @@ openapi_schema = {
                 "parameters": [
                     {
                         "required": True,
-                        "schema": {"title": "Item_Id", "type": "string"},
+                        "schema": {"title": "Item Id", "type": "string"},
                         "name": "item_id",
                         "in": "path",
                     }
index d26183961fc8a76796759a0f38b557781761016b..4608799bf775e3b1fa4cd9d74ceea8acd9dcdfb9 100644 (file)
@@ -42,7 +42,7 @@ openapi_schema = {
                 "parameters": [
                     {
                         "required": True,
-                        "schema": {"title": "Item_Id", "type": "string"},
+                        "schema": {"title": "Item Id", "type": "string"},
                         "name": "item_id",
                         "in": "path",
                     },
index be5f56dbd5daa076f79130d9075c813991004463..91a8ba96797d4e76b4f3dbd0d4e76c8987b10101 100644 (file)
@@ -14,7 +14,7 @@ openapi_schema = {
                         "content": {
                             "application/json": {
                                 "schema": {
-                                    "title": "Response_Read_Notes_Notes__Get",
+                                    "title": "Response Read Notes Notes  Get",
                                     "type": "array",
                                     "items": {"$ref": "#/components/schemas/Note"},
                                 }
index d0eff5b65150468eff5fc6c253d95e58969289c1..794e48e1d5f2aefda26e6c495b302a1abb79ab68 100644 (file)
@@ -123,7 +123,7 @@ openapi_schema = {
                 "parameters": [
                     {
                         "required": True,
-                        "schema": {"title": "Item_Id", "type": "string"},
+                        "schema": {"title": "Item Id", "type": "string"},
                         "name": "item_id",
                         "in": "path",
                     },
@@ -160,7 +160,7 @@ openapi_schema = {
                 "parameters": [
                     {
                         "required": True,
-                        "schema": {"title": "Item_Id", "type": "string"},
+                        "schema": {"title": "Item Id", "type": "string"},
                         "name": "item_id",
                         "in": "path",
                     },
index dc7e518e249a15d178df05858d4a3d69d498b123..9141e7f48d72e4742e91e5180c0665cd9bbf97f4 100644 (file)
@@ -32,7 +32,7 @@ openapi_schema = {
                 "parameters": [
                     {
                         "required": True,
-                        "schema": {"title": "Item_Id", "type": "integer"},
+                        "schema": {"title": "Item Id", "type": "integer"},
                         "name": "item_id",
                         "in": "path",
                     }
@@ -69,7 +69,7 @@ openapi_schema = {
                 "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": {
index eb938839684f44d638eff498ba283e60aa0c9c61..2d822cbfe8ac3b768f9cfb167394e19aea2d5ad9 100644 (file)
@@ -3,6 +3,15 @@ from starlette.testclient import TestClient
 
 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)
 
 
@@ -33,7 +42,7 @@ openapi_schema = {
                 "parameters": [
                     {
                         "required": True,
-                        "schema": {"title": "Item_Id", "type": "integer"},
+                        "schema": {"title": "Item Id", "type": "integer"},
                         "name": "item_id",
                         "in": "path",
                     }
index 76a368c663dba551c1f1a801d263a96c0d76d1cc..bfd62e927c3beb2273e57152d59ff4fcb6fab272 100644 (file)
@@ -35,7 +35,7 @@ openapi_schema = {
                 "parameters": [
                     {
                         "required": True,
-                        "schema": {"title": "Item_Id", "type": "string"},
+                        "schema": {"title": "Item Id", "type": "string"},
                         "name": "item_id",
                         "in": "path",
                     }
@@ -67,7 +67,7 @@ openapi_schema = {
                 "parameters": [
                     {
                         "required": True,
-                        "schema": {"title": "Item_Id", "type": "string"},
+                        "schema": {"title": "Item Id", "type": "string"},
                         "name": "item_id",
                         "in": "path",
                     }
index 1cf1b492b6f756f6f4afeaf19a4f8a8213fe0490..d6aed3ca77148384ee8a446dbbb06732584da723 100644 (file)
@@ -32,7 +32,7 @@ openapi_schema = {
                 "parameters": [
                     {
                         "required": False,
-                        "schema": {"title": "Ads_Id", "type": "string"},
+                        "schema": {"title": "Ads Id", "type": "string"},
                         "name": "ads_id",
                         "in": "cookie",
                     }
index 32f0b56ce087e7bf5b3f840427e6b8669cc18e73..bf7cd532b8bbcddcf36de14118c98cac47739cac 100644 (file)
@@ -29,7 +29,7 @@ openapi_schema = {
                 "parameters": [
                     {
                         "required": True,
-                        "schema": {"title": "Item_Id", "type": "string"},
+                        "schema": {"title": "Item Id", "type": "string"},
                         "name": "item_id",
                         "in": "path",
                     }
index 6c53b7adcf3df2bfac9a09e71298bcbe319b075f..846d3c19119b2ca5ce54fab35396d659e7e4f099 100644 (file)
@@ -33,7 +33,7 @@ openapi_schema = {
                     {
                         "required": True,
                         "schema": {
-                            "title": "Item_Id",
+                            "title": "Item Id",
                             "type": "string",
                             "format": "uuid",
                         },
@@ -60,22 +60,22 @@ openapi_schema = {
                 "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",
                     },
index 95aef55cb7fbebe1634c94401174bc0fb8d384ac..7b4776d155e2aa52115448ddfa77f567122dc6f5 100644 (file)
@@ -16,7 +16,7 @@ openapi_schema = {
                         "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"},
@@ -41,7 +41,7 @@ openapi_schema = {
                 "parameters": [
                     {
                         "required": True,
-                        "schema": {"title": "Item_Id", "type": "string"},
+                        "schema": {"title": "Item Id", "type": "string"},
                         "name": "item_id",
                         "in": "path",
                     }
index 17ea0e1be3ac8e41241c8989fb1930da380914c7..1ee2980c0ebab25d23144dd80d6cd0446d698672 100644 (file)
@@ -16,7 +16,7 @@ openapi_schema = {
                         "content": {
                             "application/json": {
                                 "schema": {
-                                    "title": "Response_Read_Items_Items__Get",
+                                    "title": "Response Read Items Items  Get",
                                     "type": "array",
                                     "items": {"$ref": "#/components/schemas/Item"},
                                 }
index d8259f1cdcdd0da531f71a4943e1848a698c094b..7f815e08a74631b9d26c39954d2716f7d85c052e 100644 (file)
@@ -16,7 +16,7 @@ openapi_schema = {
                         "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"},
                                 }
index 93cd6c12e81179ead909fad986ffd6afc37c4278..3869cb485f85d7c0d56a0deaeb13f46f6c9d73cc 100644 (file)
@@ -31,7 +31,7 @@ openapi_schema = {
                 "parameters": [
                     {
                         "required": True,
-                        "schema": {"title": "Item_Id", "type": "string"},
+                        "schema": {"title": "Item Id", "type": "string"},
                         "name": "item_id",
                         "in": "path",
                     }
index 7d9039e8852890e33cb39af07964ede89c5a4011..3381f9d72042e00b56dc3a56934cd8b77c21ecbd 100644 (file)
@@ -31,7 +31,7 @@ openapi_schema = {
                 "parameters": [
                     {
                         "required": True,
-                        "schema": {"title": "Item_Id", "type": "string"},
+                        "schema": {"title": "Item Id", "type": "string"},
                         "name": "item_id",
                         "in": "path",
                     }
index 38a9c20b24527308bcf3520633d816278033a342..83eb3675ceb17ca857f0fbe56dc9b28b3bcd8c28 100644 (file)
@@ -31,7 +31,7 @@ openapi_schema = {
                 "parameters": [
                     {
                         "required": True,
-                        "schema": {"title": "Item_Id", "type": "integer"},
+                        "schema": {"title": "Item Id", "type": "integer"},
                         "name": "item_id",
                         "in": "path",
                     }
index a59399a785b11974551407eb310f98e6cb45fd38..4813201c83bfb46f8dc7a3a3ec4a0b775c5099a3 100644 (file)
@@ -31,7 +31,7 @@ openapi_schema = {
                 "parameters": [
                     {
                         "required": True,
-                        "schema": {"title": "Item_Id", "type": "integer"},
+                        "schema": {"title": "Item Id", "type": "integer"},
                         "name": "item_id",
                         "in": "path",
                     }
index e4590db7b894aa5e222bc52e98b2ab5a880f9a2c..a131b81cab9ca31b4fa85f2fa3247128de0b6d6e 100644 (file)
@@ -31,7 +31,7 @@ openapi_schema = {
                 "parameters": [
                     {
                         "required": True,
-                        "schema": {"title": "File_Path", "type": "string"},
+                        "schema": {"title": "File Path", "type": "string"},
                         "name": "file_path",
                         "in": "path",
                     }
index 4ff7e5e29af9eeb4a277b7fd3eaf5eb4f26c4048..91cc6db64e0b10ab4dfb5ed5f71ff7d1dc23dcfe 100644 (file)
@@ -33,7 +33,7 @@ openapi_schema = {
                     {
                         "required": True,
                         "schema": {
-                            "title": "Model_Name",
+                            "title": "Model Name",
                             "enum": ["alexnet", "resnet", "lenet"],
                             "type": "string",
                         },
index 16d04afa29f9c6bddb67d78c2e149200aa647845..e6db1b0f0d7797153bdc88e99f9d99e147577497 100644 (file)
@@ -32,7 +32,7 @@ openapi_schema = {
                 "parameters": [
                     {
                         "required": True,
-                        "schema": {"title": "Item_Id", "type": "string"},
+                        "schema": {"title": "Item Id", "type": "string"},
                         "name": "item_id",
                         "in": "path",
                     },
index bbe5afc55073c17f1526d22eddc0dd7a7e32824f..edcf50697e03a04af1f24524a2d523bc4ae6982a 100644 (file)
@@ -32,7 +32,7 @@ openapi_schema = {
                 "parameters": [
                     {
                         "required": True,
-                        "schema": {"title": "Item_Id", "type": "string"},
+                        "schema": {"title": "Item Id", "type": "string"},
                         "name": "item_id",
                         "in": "path",
                     },
index a0fb2385018501b60e6471b4b2eee2305dae16e6..f66791a509b6d3fc250b45e44db5a41de14787ba 100644 (file)
@@ -31,7 +31,7 @@ openapi_schema = {
                 "parameters": [
                     {
                         "required": True,
-                        "schema": {"title": "Item_Id", "type": "string"},
+                        "schema": {"title": "Item Id", "type": "string"},
                         "name": "item_id",
                         "in": "path",
                     },
index 569e96e141155d0ad5f52d1c127da465ffdbb834..9cf3158252e8624421ccc2744a8c271257fb9152 100644 (file)
@@ -52,7 +52,7 @@ openapi_schema = {
                 "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": {
@@ -63,7 +63,7 @@ openapi_schema = {
                     "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": {
index c609d690d5ebbc5408c46bf32ba778450d8f1f70..3b71420b60002bfb432678de0cb82f64585f8373 100644 (file)
@@ -36,7 +36,7 @@ openapi_schema = {
                 "parameters": [
                     {
                         "required": True,
-                        "schema": {"title": "Item_Id", "type": "string"},
+                        "schema": {"title": "Item Id", "type": "string"},
                         "name": "item_id",
                         "in": "path",
                     }
index b087395ff4ae6252694f6dbcfe36834c0073dcc3..d0efe22b94725ee80689e8c86b4d3269207e10ae 100644 (file)
@@ -35,7 +35,7 @@ openapi_schema = {
                 "parameters": [
                     {
                         "required": True,
-                        "schema": {"title": "Item_Id", "type": "string"},
+                        "schema": {"title": "Item Id", "type": "string"},
                         "name": "item_id",
                         "in": "path",
                     }
@@ -69,7 +69,7 @@ openapi_schema = {
                 "parameters": [
                     {
                         "required": True,
-                        "schema": {"title": "Item_Id", "type": "string"},
+                        "schema": {"title": "Item Id", "type": "string"},
                         "name": "item_id",
                         "in": "path",
                     }
index d7d2bce82901e397e60d829470694da9b4c295d3..232ce0f794b14aa2f8244fd2d8fa9c3d72acac23 100644 (file)
@@ -35,7 +35,7 @@ openapi_schema = {
                 "parameters": [
                     {
                         "required": True,
-                        "schema": {"title": "Item_Id", "type": "string"},
+                        "schema": {"title": "Item Id", "type": "string"},
                         "name": "item_id",
                         "in": "path",
                     }
@@ -69,7 +69,7 @@ openapi_schema = {
                 "parameters": [
                     {
                         "required": True,
-                        "schema": {"title": "Item_Id", "type": "string"},
+                        "schema": {"title": "Item Id", "type": "string"},
                         "name": "item_id",
                         "in": "path",
                     }
index ebd8731aba5750efdc777020a0bbb80832099235..82b2d959dca121e61b1046e752074a9bbbd0b890 100644 (file)
@@ -62,15 +62,15 @@ openapi_schema = {
                 "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": {
index 786fbc3ac577df11f16794c62eb88b905e7e69e1..a3f8a9ffd5ed86038fe0d63556020645aaa1242a 100644 (file)
@@ -103,7 +103,7 @@ openapi_schema = {
                 "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"},
                 },
             },
@@ -112,8 +112,8 @@ openapi_schema = {
                 "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": {
@@ -122,15 +122,15 @@ openapi_schema = {
                 "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": {
index a85a7f91427d31a0fd9a79a2d220e693d0094750..e4adc1f7319a373abed7466a8f2472a9889cd41d 100644 (file)
@@ -15,7 +15,7 @@ openapi_schema = {
                         "content": {
                             "application/json": {
                                 "schema": {
-                                    "title": "Response_Read_Users_Users__Get",
+                                    "title": "Response Read Users Users  Get",
                                     "type": "array",
                                     "items": {"$ref": "#/components/schemas/User"},
                                 }
@@ -110,7 +110,7 @@ openapi_schema = {
                 "parameters": [
                     {
                         "required": True,
-                        "schema": {"title": "User_Id", "type": "integer"},
+                        "schema": {"title": "User Id", "type": "integer"},
                         "name": "user_id",
                         "in": "path",
                     }
@@ -144,7 +144,7 @@ openapi_schema = {
                 "parameters": [
                     {
                         "required": True,
-                        "schema": {"title": "User_Id", "type": "integer"},
+                        "schema": {"title": "User Id", "type": "integer"},
                         "name": "user_id",
                         "in": "path",
                     }
@@ -167,7 +167,7 @@ openapi_schema = {
                         "content": {
                             "application/json": {
                                 "schema": {
-                                    "title": "Response_Read_Items_Items__Get",
+                                    "title": "Response Read Items Items  Get",
                                     "type": "array",
                                     "items": {"$ref": "#/components/schemas/Item"},
                                 }
@@ -223,7 +223,7 @@ openapi_schema = {
                     "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": {
@@ -233,7 +233,7 @@ openapi_schema = {
                 "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",
index d5644d87651d191931b18ecce6b7e1ab3a64d345..c056e1800025090444484482c8ccaf8b7d0f348b 100644 (file)
@@ -15,7 +15,7 @@ openapi_schema = {
                         "content": {
                             "application/json": {
                                 "schema": {
-                                    "title": "Response_Read_Users_Users__Get",
+                                    "title": "Response Read Users Users  Get",
                                     "type": "array",
                                     "items": {"$ref": "#/components/schemas/User"},
                                 }
@@ -110,7 +110,7 @@ openapi_schema = {
                 "parameters": [
                     {
                         "required": True,
-                        "schema": {"title": "User_Id", "type": "integer"},
+                        "schema": {"title": "User Id", "type": "integer"},
                         "name": "user_id",
                         "in": "path",
                     }
@@ -144,7 +144,7 @@ openapi_schema = {
                 "parameters": [
                     {
                         "required": True,
-                        "schema": {"title": "User_Id", "type": "integer"},
+                        "schema": {"title": "User Id", "type": "integer"},
                         "name": "user_id",
                         "in": "path",
                     }
@@ -167,7 +167,7 @@ openapi_schema = {
                         "content": {
                             "application/json": {
                                 "schema": {
-                                    "title": "Response_Read_Items_Items__Get",
+                                    "title": "Response Read Items Items  Get",
                                     "type": "array",
                                     "items": {"$ref": "#/components/schemas/Item"},
                                 }
@@ -223,7 +223,7 @@ openapi_schema = {
                     "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": {
@@ -233,7 +233,7 @@ openapi_schema = {
                 "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",