]> git.ipfire.org Git - thirdparty/fastapi/fastapi.git/commitdiff
:recycle: Re-format tutorials, files names and tests
authorSebastián Ramírez <tiangolo@gmail.com>
Tue, 18 Dec 2018 17:59:06 +0000 (21:59 +0400)
committerSebastián Ramírez <tiangolo@gmail.com>
Tue, 18 Dec 2018 17:59:06 +0000 (21:59 +0400)
for tutorial files

146 files changed:
docs/tutorial/bigger-applications.md
docs/tutorial/body-multiple-params.md
docs/tutorial/body-nested-models.md
docs/tutorial/body-schema.md
docs/tutorial/body.md
docs/tutorial/cookie-params.md
docs/tutorial/custom-response.md
docs/tutorial/dependencies/second-steps.md
docs/tutorial/extra-models.md
docs/tutorial/first-steps.md
docs/tutorial/header-params.md
docs/tutorial/nosql-databases.md
docs/tutorial/path-operation-advanced-configuration.md
docs/tutorial/path-operation-configuration.md
docs/tutorial/path-params-numeric-validations.md
docs/tutorial/path-params.md
docs/tutorial/python-types.md
docs/tutorial/query-params-str-validations.md
docs/tutorial/query-params.md
docs/tutorial/request-files.md
docs/tutorial/request-forms-and-files.md
docs/tutorial/request-forms.md
docs/tutorial/response-model.md
docs/tutorial/sql-databases.md
docs/tutorial/src/bigger_applications/__init__.py [new file with mode: 0644]
docs/tutorial/src/bigger_applications/app/__init__.py [new file with mode: 0644]
docs/tutorial/src/bigger_applications/app/routers/__init__.py [new file with mode: 0644]
docs/tutorial/src/bigger_applications/app/routers/tutorial001.py [moved from docs/tutorial/src/bigger-applications/tutorial001.py with 100% similarity]
docs/tutorial/src/bigger_applications/app/routers/tutorial002.py [moved from docs/tutorial/src/bigger-applications/tutorial002.py with 100% similarity]
docs/tutorial/src/bigger_applications/app/tutorial003.py [moved from docs/tutorial/src/bigger-applications/tutorial003.py with 53% similarity]
docs/tutorial/src/body/tutorial001.py
docs/tutorial/src/body/tutorial002.py
docs/tutorial/src/body/tutorial003.py
docs/tutorial/src/body/tutorial004.py
docs/tutorial/src/body_multiple_params/tutorial001.py [moved from docs/tutorial/src/body-multiple-params/tutorial001.py with 96% similarity]
docs/tutorial/src/body_multiple_params/tutorial002.py [moved from docs/tutorial/src/body-multiple-params/tutorial002.py with 99% similarity]
docs/tutorial/src/body_multiple_params/tutorial003.py [moved from docs/tutorial/src/body-multiple-params/tutorial003.py with 99% similarity]
docs/tutorial/src/body_multiple_params/tutorial004.py [moved from docs/tutorial/src/body-multiple-params/tutorial004.py with 99% similarity]
docs/tutorial/src/body_multiple_params/tutorial005.py [moved from docs/tutorial/src/body-multiple-params/tutorial005.py with 99% similarity]
docs/tutorial/src/body_nested_models/tutorial001.py [moved from docs/tutorial/src/body-nested-models/tutorial001.py with 99% similarity]
docs/tutorial/src/body_nested_models/tutorial002.py [moved from docs/tutorial/src/body-nested-models/tutorial002.py with 99% similarity]
docs/tutorial/src/body_nested_models/tutorial003.py [moved from docs/tutorial/src/body-nested-models/tutorial003.py with 99% similarity]
docs/tutorial/src/body_nested_models/tutorial004.py [moved from docs/tutorial/src/body-nested-models/tutorial004.py with 99% similarity]
docs/tutorial/src/body_nested_models/tutorial005.py [moved from docs/tutorial/src/body-nested-models/tutorial005.py with 99% similarity]
docs/tutorial/src/body_nested_models/tutorial006.py [moved from docs/tutorial/src/body-nested-models/tutorial006.py with 99% similarity]
docs/tutorial/src/body_nested_models/tutorial007.py [moved from docs/tutorial/src/body-nested-models/tutorial007.py with 99% similarity]
docs/tutorial/src/body_nested_models/tutorial008.py [moved from docs/tutorial/src/body-nested-models/tutorial008.py with 99% similarity]
docs/tutorial/src/body_schema/tutorial001.py [moved from docs/tutorial/src/body-schema/tutorial001.py with 99% similarity]
docs/tutorial/src/body_schema/tutorial002.py [moved from docs/tutorial/src/body-schema/tutorial002.py with 99% similarity]
docs/tutorial/src/cookie_params/tutorial001.py [moved from docs/tutorial/src/cookie-params/tutorial001.py with 100% similarity]
docs/tutorial/src/custom_response/tutorial001.py [moved from docs/tutorial/src/custom-response/tutorial001.py with 99% similarity]
docs/tutorial/src/custom_response/tutorial002.py [moved from docs/tutorial/src/custom-response/tutorial002.py with 99% similarity]
docs/tutorial/src/custom_response/tutorial003.py [moved from docs/tutorial/src/custom-response/tutorial003.py with 99% similarity]
docs/tutorial/src/custom_response/tutorial004.py [moved from docs/tutorial/src/custom-response/tutorial004.py with 99% similarity]
docs/tutorial/src/dependencies/tutorial002.py
docs/tutorial/src/dependencies/tutorial003.py
docs/tutorial/src/dependencies/tutorial004.py
docs/tutorial/src/extra_models/tutorial001.py [moved from docs/tutorial/src/extra-models/tutorial001.py with 99% similarity]
docs/tutorial/src/extra_models/tutorial002.py [moved from docs/tutorial/src/extra-models/tutorial002.py with 99% similarity]
docs/tutorial/src/first_steps/tutorial001.py [moved from docs/tutorial/src/first-steps/tutorial001.py with 100% similarity]
docs/tutorial/src/first_steps/tutorial002.py [moved from docs/tutorial/src/first-steps/tutorial002.py with 100% similarity]
docs/tutorial/src/first_steps/tutorial003.py [moved from docs/tutorial/src/first-steps/tutorial003.py with 100% similarity]
docs/tutorial/src/header_params/tutorial001.py [moved from docs/tutorial/src/header-params/tutorial001.py with 100% similarity]
docs/tutorial/src/header_params/tutorial002.py [moved from docs/tutorial/src/header-params/tutorial002.py with 100% similarity]
docs/tutorial/src/nosql_databases/tutorial001.py [moved from docs/tutorial/src/nosql-databases/tutorial001.py with 90% similarity]
docs/tutorial/src/path_operation_advanced_configuration/tutorial001.py [moved from docs/tutorial/src/path-operation-advanced-configuration/tutorial001.py with 100% similarity]
docs/tutorial/src/path_operation_advanced_configuration/tutorial002.py [moved from docs/tutorial/src/path-operation-advanced-configuration/tutorial002.py with 100% similarity]
docs/tutorial/src/path_operation_configuration/tutorial001.py [moved from docs/tutorial/src/path-operation-configuration/tutorial001.py with 99% similarity]
docs/tutorial/src/path_operation_configuration/tutorial002.py [moved from docs/tutorial/src/path-operation-configuration/tutorial002.py with 99% similarity]
docs/tutorial/src/path_operation_configuration/tutorial003.py [moved from docs/tutorial/src/path-operation-configuration/tutorial003.py with 99% similarity]
docs/tutorial/src/path_operation_configuration/tutorial004.py [moved from docs/tutorial/src/path-operation-configuration/tutorial004.py with 99% similarity]
docs/tutorial/src/path_operation_configuration/tutorial005.py [moved from docs/tutorial/src/path-operation-configuration/tutorial005.py with 99% similarity]
docs/tutorial/src/path_operation_configuration/tutorial006.py [moved from docs/tutorial/src/path-operation-configuration/tutorial006.py with 100% similarity]
docs/tutorial/src/path_params/tutorial001.py [moved from docs/tutorial/src/path-params/tutorial001.py with 100% similarity]
docs/tutorial/src/path_params/tutorial002.py [moved from docs/tutorial/src/path-params/tutorial002.py with 100% similarity]
docs/tutorial/src/path_params/tutorial003.py [moved from docs/tutorial/src/path-params/tutorial003.py with 100% similarity]
docs/tutorial/src/path_params_numeric_validations/tutorial001.py [moved from docs/tutorial/src/path-params-numeric-validations/tutorial001.py with 100% similarity]
docs/tutorial/src/path_params_numeric_validations/tutorial002.py [moved from docs/tutorial/src/path-params-numeric-validations/tutorial002.py with 100% similarity]
docs/tutorial/src/path_params_numeric_validations/tutorial003.py [moved from docs/tutorial/src/path-params-numeric-validations/tutorial003.py with 100% similarity]
docs/tutorial/src/path_params_numeric_validations/tutorial004.py [moved from docs/tutorial/src/path-params-numeric-validations/tutorial004.py with 100% similarity]
docs/tutorial/src/path_params_numeric_validations/tutorial005.py [moved from docs/tutorial/src/path-params-numeric-validations/tutorial005.py with 100% similarity]
docs/tutorial/src/path_params_numeric_validations/tutorial006.py [moved from docs/tutorial/src/path-params-numeric-validations/tutorial006.py with 100% similarity]
docs/tutorial/src/python_types/tutorial001.py [moved from docs/tutorial/src/python-types/tutorial001.py with 100% similarity]
docs/tutorial/src/python_types/tutorial002.py [moved from docs/tutorial/src/python-types/tutorial002.py with 100% similarity]
docs/tutorial/src/python_types/tutorial003.py [moved from docs/tutorial/src/python-types/tutorial003.py with 100% similarity]
docs/tutorial/src/python_types/tutorial004.py [moved from docs/tutorial/src/python-types/tutorial004.py with 100% similarity]
docs/tutorial/src/python_types/tutorial005.py [moved from docs/tutorial/src/python-types/tutorial005.py with 100% similarity]
docs/tutorial/src/python_types/tutorial006.py [moved from docs/tutorial/src/python-types/tutorial006.py with 100% similarity]
docs/tutorial/src/python_types/tutorial007.py [moved from docs/tutorial/src/python-types/tutorial007.py with 100% similarity]
docs/tutorial/src/python_types/tutorial008.py [moved from docs/tutorial/src/python-types/tutorial008.py with 100% similarity]
docs/tutorial/src/python_types/tutorial009.py [moved from docs/tutorial/src/python-types/tutorial009.py with 100% similarity]
docs/tutorial/src/python_types/tutorial010.py [moved from docs/tutorial/src/python-types/tutorial010.py with 100% similarity]
docs/tutorial/src/query_params/tutorial001.py [moved from docs/tutorial/src/query-params/tutorial001.py with 100% similarity]
docs/tutorial/src/query_params/tutorial002.py [moved from docs/tutorial/src/query-params/tutorial002.py with 100% similarity]
docs/tutorial/src/query_params/tutorial003.py [moved from docs/tutorial/src/query-params/tutorial003.py with 100% similarity]
docs/tutorial/src/query_params/tutorial004.py [moved from docs/tutorial/src/query-params/tutorial004.py with 100% similarity]
docs/tutorial/src/query_params/tutorial005.py [moved from docs/tutorial/src/query-params/tutorial005.py with 100% similarity]
docs/tutorial/src/query_params_str_validations/tutorial001.py [moved from docs/tutorial/src/query-params-str-validations/tutorial001.py with 100% similarity]
docs/tutorial/src/query_params_str_validations/tutorial002.py [moved from docs/tutorial/src/query-params-str-validations/tutorial002.py with 100% similarity]
docs/tutorial/src/query_params_str_validations/tutorial003.py [moved from docs/tutorial/src/query-params-str-validations/tutorial003.py with 100% similarity]
docs/tutorial/src/query_params_str_validations/tutorial004.py [moved from docs/tutorial/src/query-params-str-validations/tutorial004.py with 100% similarity]
docs/tutorial/src/query_params_str_validations/tutorial005.py [moved from docs/tutorial/src/query-params-str-validations/tutorial005.py with 100% similarity]
docs/tutorial/src/query_params_str_validations/tutorial006.py [moved from docs/tutorial/src/query-params-str-validations/tutorial006.py with 100% similarity]
docs/tutorial/src/query_params_str_validations/tutorial007.py [moved from docs/tutorial/src/query-params-str-validations/tutorial007.py with 100% similarity]
docs/tutorial/src/query_params_str_validations/tutorial008.py [moved from docs/tutorial/src/query-params-str-validations/tutorial008.py with 100% similarity]
docs/tutorial/src/query_params_str_validations/tutorial009.py [moved from docs/tutorial/src/query-params-str-validations/tutorial009.py with 100% similarity]
docs/tutorial/src/query_params_str_validations/tutorial010.py [moved from docs/tutorial/src/query-params-str-validations/tutorial010.py with 100% similarity]
docs/tutorial/src/request_files/tutorial001.py [moved from docs/tutorial/src/request-files/tutorial001.py with 100% similarity]
docs/tutorial/src/request_forms/tutorial001.py [moved from docs/tutorial/src/request-forms/tutorial001.py with 100% similarity]
docs/tutorial/src/request_forms_and_files/tutorial001.py [moved from docs/tutorial/src/request-forms-and-files/tutorial001.py with 100% similarity]
docs/tutorial/src/response_model/tutorial001.py [moved from docs/tutorial/src/response-model/tutorial001.py with 99% similarity]
docs/tutorial/src/response_model/tutorial002.py [moved from docs/tutorial/src/response-model/tutorial002.py with 99% similarity]
docs/tutorial/src/response_model/tutorial003.py [moved from docs/tutorial/src/response-model/tutorial003.py with 99% similarity]
docs/tutorial/src/security/tutorial002.py
docs/tutorial/src/security/tutorial003.py
docs/tutorial/src/security/tutorial004.py
docs/tutorial/src/sql_databases/tutorial001.py [moved from docs/tutorial/src/sql-databases/tutorial001.py with 100% similarity]
fastapi/applications.py
fastapi/dependencies/models.py
fastapi/openapi/models.py
fastapi/openapi/utils.py
fastapi/security/api_key.py
fastapi/security/http.py
fastapi/security/oauth2.py
fastapi/security/open_id_connect_url.py
fastapi/utils.py
scripts/test.sh
tests/main.py
tests/main_old.py [new file with mode: 0644]
tests/test_application.py [new file with mode: 0644]
tests/test_tutorial/__init__.py [new file with mode: 0644]
tests/test_tutorial/test_bigger_applications/__init__.py [new file with mode: 0644]
tests/test_tutorial/test_bigger_applications/test_tutorial003.py [new file with mode: 0644]
tests/test_tutorial/test_body/__init__.py [new file with mode: 0644]
tests/test_tutorial/test_body/test_tutorial001.py [new file with mode: 0644]
tests/test_tutorial/test_body_multiple_params/__init__.py [new file with mode: 0644]
tests/test_tutorial/test_body_multiple_params/test_tutorial001.py [new file with mode: 0644]
tests/test_tutorial/test_dependencies/__init__.py [new file with mode: 0644]
tests/test_tutorial/test_dependencies/test_tutorial001.py [new file with mode: 0644]
tests/test_tutorial/test_first_steps/__init__.py [new file with mode: 0644]
tests/test_tutorial/test_first_steps/test_tutorial001.py [new file with mode: 0644]
tests/test_tutorial/test_security/__init__.py [new file with mode: 0644]
tests/test_tutorial/test_security/test_tutorial001.py [new file with mode: 0644]
tests/xtest_path.py [moved from tests/test_path.py with 100% similarity]
tests/xtest_query.py [moved from tests/test_query.py with 100% similarity]
tests/xtest_security.py [moved from tests/test_security.py with 100% similarity]

index 499acf68534aad06dd3ddf9f9be9fd06a06eef1e..b82dd6125cc71e86451971947f87f98543c7db48 100644 (file)
@@ -1,13 +1,13 @@
 Coming soon...
 
 ```Python
-{!./tutorial/src/bigger-applications/tutorial001.py!}
+{!./tutorial/src/bigger_applications/app/routers/tutorial001.py!}
 ```
 
 ```Python
-{!./tutorial/src/bigger-applications/tutorial002.py!}
+{!./tutorial/src/bigger_applications/app/routers/tutorial002.py!}
 ```
 
 ```Python
-{!./tutorial/src/bigger-applications/tutorial003.py!}
+{!./tutorial/src/bigger_applications/app/tutorial003.py!}
 ```
index ad58168b6121e2b415b78dff87159626e6d4fbfa..eeb882909771e7ee34ae7fc1411240907a7eab32 100644 (file)
@@ -4,8 +4,10 @@ Now that we have seen how to use `Path` and `Query`, let's see more advanced use
 
 First, of course, you can mix `Path`, `Query` and request body parameter declarations freely and **FastAPI** will know what to do.
 
-```Python hl_lines="17 18 19"
-{!./tutorial/src/body-multiple-params/tutorial001.py!}
+And you can also declare body parameters as optional, by setting the default to `None`:
+
+```Python hl_lines="18 19 20"
+{!./tutorial/src/body_multiple_params/tutorial001.py!}
 ```
 
 !!! note
@@ -27,8 +29,8 @@ In the previous example, the path operations would expect a JSON body with the a
 
 But you can also declare multiple body parameters, e.g. `item` and `user`:
 
-```Python hl_lines="20"
-{!./tutorial/src/body-multiple-params/tutorial002.py!}
+```Python hl_lines="21"
+{!./tutorial/src/body_multiple_params/tutorial002.py!}
 ```
 
 In this case, **FastAPI** will notice that there are more than one body parameter in the function (two parameters that are Pydantic models).
@@ -69,8 +71,8 @@ If you declare it as is, because it is a singular value, **FastAPI** will assume
 But you can instruct **FastAPI** to treat it as another body key using `Body`:
 
 
-```Python hl_lines="21"
-{!./tutorial/src/body-multiple-params/tutorial003.py!}
+```Python hl_lines="22"
+{!./tutorial/src/body_multiple_params/tutorial003.py!}
 ```
 
 In this case, **FastAPI** will expect a body like:
@@ -106,8 +108,8 @@ q: str = None
 
 as in:
 
-```Python hl_lines="25"
-{!./tutorial/src/body-multiple-params/tutorial004.py!}
+```Python hl_lines="27"
+{!./tutorial/src/body_multiple_params/tutorial004.py!}
 ```
 
 !!! info
@@ -128,8 +130,8 @@ item: Item = Body(..., embed=True)
 
 as in:
 
-```Python hl_lines="15"
-{!./tutorial/src/body-multiple-params/tutorial005.py!}
+```Python hl_lines="16"
+{!./tutorial/src/body_multiple_params/tutorial005.py!}
 ```
 
 In this case **FastAPI** will expect a body like:
index 2d57dd78ce603f8b4b6a1fa293fe915dc36e1ffa..3ae38bbef4895fc3c01015d016775585d0d3ae41 100644 (file)
@@ -4,8 +4,8 @@ With **FastAPI**, you can define, validate, document, and use arbitrarily deeply
 
 You can define an attribute to be a subtype. For example, a Python `list`:
 
-```Python hl_lines="12"
-{!./tutorial/src/body-nested-models/tutorial001.py!}
+```Python hl_lines="13"
+{!./tutorial/src/body_nested_models/tutorial001.py!}
 ```
 
 This will make `tags` be a list of items. Although it doesn't declare the type of each of the items.
@@ -19,7 +19,7 @@ But Python has a specific way to declare lists with subtypes:
 First, import `List` from standard Python's `typing` module:
 
 ```Python hl_lines="1"
-{!./tutorial/src/body-nested-models/tutorial002.py!}
+{!./tutorial/src/body_nested_models/tutorial002.py!}
 ```
 
 ### Declare a `List` with a subtype
@@ -41,8 +41,8 @@ Use that same standard syntax for model attributes with subtypes.
 
 So, in our example, we can make `tags` be specifically a "list of strings":
 
-```Python hl_lines="14"
-{!./tutorial/src/body-nested-models/tutorial002.py!}
+```Python hl_lines="15"
+{!./tutorial/src/body_nested_models/tutorial002.py!}
 ```
 
 ## Set types
@@ -53,8 +53,8 @@ And Python has a special data type for sets of unique items, the `set`.
 
 Then we can import `Set` and declare `tags` as a `set` of `str`:
 
-```Python hl_lines="1 14"
-{!./tutorial/src/body-nested-models/tutorial003.py!}
+```Python hl_lines="1 15"
+{!./tutorial/src/body_nested_models/tutorial003.py!}
 ```
 
 With this, even if you receive a request with duplicate data, it will be converted to a set of unique items.
@@ -77,16 +77,16 @@ All that, arbitrarily nested.
 
 For example, we can define an `Image` model:
 
-```Python hl_lines="9 10 11"
-{!./tutorial/src/body-nested-models/tutorial004.py!}
+```Python hl_lines="10 11 12"
+{!./tutorial/src/body_nested_models/tutorial004.py!}
 ```
 
 ### Use the submodel as a type
 
 And then we can use it as the type of an attribute:
 
-```Python hl_lines="20"
-{!./tutorial/src/body-nested-models/tutorial004.py!}
+```Python hl_lines="21"
+{!./tutorial/src/body_nested_models/tutorial004.py!}
 ```
 
 This would mean that **FastAPI** would expect a body similar to:
@@ -120,8 +120,8 @@ To see all the options you have, checkout the docs for <a href="https://pydantic
 
 For example, as in the `Image` model we have a `url` field, we can declare it to be instead of a `str`, a Pydantic's `UrlStr`:
 
-```Python hl_lines="5 11"
-{!./tutorial/src/body-nested-models/tutorial005.py!}
+```Python hl_lines="4 12"
+{!./tutorial/src/body_nested_models/tutorial005.py!}
 ```
 
 The string will be checked to be a valid URL, and documented in JSON Schema / OpenAPI as such.
@@ -130,8 +130,8 @@ The string will be checked to be a valid URL, and documented in JSON Schema / Op
 
 You can also use Pydantic models as subtypes of `list`, `set`, etc:
 
-```Python hl_lines="21"
-{!./tutorial/src/body-nested-models/tutorial006.py!}
+```Python hl_lines="22"
+{!./tutorial/src/body_nested_models/tutorial006.py!}
 ```
 
 This will expect (convert, validate, document, etc) a JSON body like:
@@ -167,8 +167,8 @@ This will expect (convert, validate, document, etc) a JSON body like:
 
 You can define arbitrarily deeply nested models:
 
-```Python hl_lines="10 15 21 24 28"
-{!./tutorial/src/body-nested-models/tutorial007.py!}
+```Python hl_lines="11 16 22 25 29"
+{!./tutorial/src/body_nested_models/tutorial007.py!}
 ```
 
 !!! info
@@ -184,8 +184,8 @@ images: List[Image]
 
 as in:
 
-```Python hl_lines="16"
-{!./tutorial/src/body-nested-models/tutorial008.py!}
+```Python hl_lines="17"
+{!./tutorial/src/body_nested_models/tutorial008.py!}
 ```
 
 ## Editor support everywhere
index 6a6895efd3c5b6980d3e6b44eaa898c691285e24..8c1412c9dcd8ca78ed6f4113edeb44782bdd8d40 100644 (file)
@@ -4,8 +4,8 @@ The same way you can declare additional validation and metadata in path operatio
 
 First, you have to import it:
 
-```Python hl_lines="2"
-{!./tutorial/src/body-schema/tutorial001.py!}
+```Python hl_lines="3"
+{!./tutorial/src/body_schema/tutorial001.py!}
 ```
 
 !!! warning
@@ -16,8 +16,8 @@ First, you have to import it:
 
 You can then use `Schema` with model attributes:
 
-```Python hl_lines="9 10"
-{!./tutorial/src/body-schema/tutorial001.py!}
+```Python hl_lines="10 11"
+{!./tutorial/src/body_schema/tutorial001.py!}
 ```
 
 `Schema` works the same way as `Query`, `Path` and `Body`, it has all the same parameters, etc.
@@ -44,8 +44,8 @@ If you know JSON Schema and want to add extra information appart from what we ha
 
 For example, you can use that functionality to pass a <a href="http://json-schema.org/latest/json-schema-validation.html#rfc.section.8.5" target="_blank">JSON Schema example</a> field to a body request JSON Schema:
 
-```Python hl_lines="20 21 22 23 24 25"
-{!./tutorial/src/body-schema/tutorial002.py!}
+```Python hl_lines="21 22 23 24 25 26"
+{!./tutorial/src/body_schema/tutorial002.py!}
 ```
 
 ## Recap
index ee7a540ace41cb7c8482d78dca75c73f0f80789c..236232c1c6d05e33fea3ed18c3d2b13b166fbc0f 100644 (file)
@@ -4,7 +4,7 @@ To declare a request body, you use <a href="https://pydantic-docs.helpmanual.io/
 
 First, you need to import `BaseModel` from `pydantic`:
 
-```Python hl_lines="2"
+```Python hl_lines="1"
 {!./tutorial/src/body/tutorial001.py!}
 ```
 
@@ -14,7 +14,7 @@ Then you declare your data model as a class that inherits from `BaseModel`.
 
 Use standard Python types for all the attributes:
 
-```Python hl_lines="5 6 7 8 9"
+```Python hl_lines="6 7 8 9 10"
 {!./tutorial/src/body/tutorial001.py!}
 ```
 
@@ -44,7 +44,7 @@ For example, this model above declares a JSON "`object`" (or Python `dict`) like
 
 To add it to your path operation, declare it the same way you declared path and query parameters:
 
-```Python hl_lines="16"
+```Python hl_lines="17"
 {!./tutorial/src/body/tutorial001.py!}
 ```
 
@@ -100,7 +100,7 @@ But you would get the same editor support with <a href="https://www.jetbrains.co
 
 Inside of the function, you can access all the attributes of the model object directly:
 
-```Python hl_lines="19"
+```Python hl_lines="20"
 {!./tutorial/src/body/tutorial002.py!}
 ```
 
@@ -110,7 +110,7 @@ You can declare path parameters and body requests at the same time.
 
 **FastAPI** will recognize that the function parameters that match path parameters should be **taken from the path**, and that function parameters that are declared to be Pydantic models should be **taken from the request body**.
 
-```Python hl_lines="15 16"
+```Python hl_lines="16 17"
 {!./tutorial/src/body/tutorial003.py!}
 ```
 
@@ -120,7 +120,7 @@ You can also declare **body**, **path** and **query** parameters, all at the sam
 
 **FastAPI** will recognize each of them and take the data from the correct place.
 
-```Python hl_lines="16"
+```Python hl_lines="17"
 {!./tutorial/src/body/tutorial004.py!}
 ```
 
index ba1756b583c4cfca8c20247a161babdd4a4c061d..ae35e3149ca0b97e51dd33d6e8749ed4b606e63f 100644 (file)
@@ -5,7 +5,7 @@ You can define Cookie parameters the same way you define `Query` and `Path` para
 First import `Cookie`:
 
 ```Python hl_lines="1"
-{!./tutorial/src/cookie-params/tutorial001.py!}
+{!./tutorial/src/cookie_params/tutorial001.py!}
 ```
 
 ## Declare `Cookie` parameteres
@@ -15,7 +15,7 @@ Then declare the cookie parameters using the same structure as with `Path` and `
 The first value is the default value, you can pass all the extra validation or annotation parameteres:
 
 ```Python hl_lines="7"
-{!./tutorial/src/cookie-params/tutorial001.py!}
+{!./tutorial/src/cookie_params/tutorial001.py!}
 ```
 
 !!! info
index a0da6ccc141cd31bbb9b55d739d27189945edbf5..28bf13a0f6cd3e9f0482c6db8f7a24c06867f3b5 100644 (file)
@@ -13,8 +13,8 @@ For example, if you are squeezing performance, you can use `ujson` and set the r
 
 ### Import `UJSONResponse`
 
-```Python hl_lines="2"
-{!./tutorial/src/custom-response/tutorial001.py!}
+```Python hl_lines="1"
+{!./tutorial/src/custom_response/tutorial001.py!}
 ```
 
 !!! note
@@ -24,8 +24,8 @@ For example, if you are squeezing performance, you can use `ujson` and set the r
 
 Make your path operation use `UJSONResponse` as the response class using the parameter `content_type`:
 
-```Python hl_lines="7"
-{!./tutorial/src/custom-response/tutorial001.py!}
+```Python hl_lines="8"
+{!./tutorial/src/custom_response/tutorial001.py!}
 ```
 
 !!! info
@@ -39,8 +39,8 @@ To return a response with HTML directly from **FastAPI**, use `HTMLResponse`.
 
 ### Import `HTMLResponse`
 
-```Python hl_lines="2"
-{!./tutorial/src/custom-response/tutorial002.py!}
+```Python hl_lines="1"
+{!./tutorial/src/custom_response/tutorial002.py!}
 ```
 
 !!! note
@@ -51,8 +51,8 @@ To return a response with HTML directly from **FastAPI**, use `HTMLResponse`.
 
 Pass `HTMLResponse` as the parameter `content_type` of your path operation:
 
-```Python hl_lines="7"
-{!./tutorial/src/custom-response/tutorial002.py!}
+```Python hl_lines="8"
+{!./tutorial/src/custom_response/tutorial002.py!}
 ```
 
 !!! info
@@ -71,8 +71,8 @@ If you return an object that is an instance of Starlette's `Response`, it will b
 
 The same example from above, returning an `HTMLResponse`, could look like:
 
-```Python hl_lines="7"
-{!./tutorial/src/custom-response/tutorial003.py!}
+```Python hl_lines="8 20"
+{!./tutorial/src/custom_response/tutorial003.py!}
 ```
 
 !!! info
@@ -92,8 +92,8 @@ The `content_type` class will then be used only to document the OpenAPI path ope
 
 For example, it could be something like:
 
-```Python hl_lines="7 23"
-{!./tutorial/src/custom-response/tutorial004.py!}
+```Python hl_lines="8 19 22"
+{!./tutorial/src/custom_response/tutorial004.py!}
 ```
 
 In this example, the function `generate_html_response()` already generates a Starlette `Response` instead of the HTML in a `str`.
@@ -104,8 +104,8 @@ By returning the result of calling `generate_html_response()`, you are already r
 
 But by declaring it also in the path operation decorator:
 
-```Python hl_lines="21"
-{!./tutorial/src/custom-response/tutorial004.py!}
+```Python hl_lines="22"
+{!./tutorial/src/custom_response/tutorial004.py!}
 ```
 
 #### OpenAPI knows how to document it
index ef673eba246c26c8ac00802f0b0d24e5f743ba67..2b6412890b5d9b6ed82e16ee77ec7646c1d5fc6c 100644 (file)
@@ -20,7 +20,7 @@ Let's use them here too.
 
 Create a model for the common parameters (and don't pay attention to the rest, for now):
 
-```Python hl_lines="10 11 12 13"
+```Python hl_lines="11 12 13 14"
 {!./tutorial/src/dependencies/tutorial002.py!}
 ```
 
@@ -28,7 +28,7 @@ Create a model for the common parameters (and don't pay attention to the rest, f
 
 Now we can return a Pydantic model from the dependency ("dependable") with the same data as the dict before:
 
-```Python hl_lines="17"
+```Python hl_lines="18"
 {!./tutorial/src/dependencies/tutorial002.py!}
 ```
 
@@ -42,7 +42,7 @@ commons: CommonQueryParams = Depends(common_parameters)
 
 It won't be interpreted as a JSON request `Body` because we are using `Depends`:
 
-```Python hl_lines="21"
+```Python hl_lines="22"
 {!./tutorial/src/dependencies/tutorial002.py!}
 ```
 
@@ -55,7 +55,7 @@ It won't be interpreted as a JSON request `Body` because we are using `Depends`:
 
 And now we can use that model in our code, with all the lovable editor support:
 
-```Python hl_lines="23 24 25"
+```Python hl_lines="24 25 26"
 {!./tutorial/src/dependencies/tutorial002.py!}
 ```
 
index 1e24c747864e592b79d7a6bc0dafb141a3537a42..8ad7333716e3fe067ee8da34970ea2c4f276cc87 100644 (file)
@@ -2,9 +2,9 @@ Continuing with the previous example, it will be common to have more than one re
 
 This is especially the case for user models, because:
 
-* The **input model** needs to be able to have a password
-* The **output model** should do not have a password
-* The **database model** would probably need to have a hashed password
+* The **input model** needs to be able to have a password.
+* The **output model** should do not have a password.
+* The **database model** would probably need to have a hashed password.
 
 !!! danger
     Never store user's plaintext passwords. Always store a secure hash that you can then verify.
@@ -13,8 +13,8 @@ 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"
-{!./tutorial/src/extra-models/tutorial001.py!}
+```Python hl_lines="9 11 16 22 24 33 35 40 41"
+{!./tutorial/src/extra_models/tutorial001.py!}
 ```
 
 !!! warning
@@ -36,8 +36,8 @@ 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"
-{!./tutorial/src/extra-models/tutorial002.py!}
+```Python hl_lines="9 15 16 19 20 23 24"
+{!./tutorial/src/extra_models/tutorial002.py!}
 ```
 
 ## Recap
index 2b29e089ebd36d5737ec658ea6e8e3344f4e71a8..7ae275ea885abac965e6661f0a73080a44b3780c 100644 (file)
@@ -1,7 +1,7 @@
 The simplest FastAPI file could look like this:
 
 ```Python
-{!tutorial/src/first-steps/tutorial001.py!}
+{!tutorial/src/first_steps/tutorial001.py!}
 ```
 
 Copy that to a file `main.py`.
@@ -89,7 +89,7 @@ It will show a JSON starting with something like:
 ### Step 1: import `FastAPI`
 
 ```Python hl_lines="1"
-{!tutorial/src/first-steps/tutorial001.py!}
+{!tutorial/src/first_steps/tutorial001.py!}
 ```
 
 `FastAPI` is a Python class that provides all the functionality for your API.
@@ -102,7 +102,7 @@ It will show a JSON starting with something like:
 ### Step 2: create a `FastAPI` "instance"
 
 ```Python hl_lines="3"
-{!tutorial/src/first-steps/tutorial001.py!}
+{!tutorial/src/first_steps/tutorial001.py!}
 ```
 
 Here the `app` variable will be an "instance" of the class `FastAPI`.
@@ -118,7 +118,7 @@ uvicorn main:app --debug
 If you create your app like:
 
 ```Python hl_lines="3"
-{!tutorial/src/first-steps/tutorial002.py!}
+{!tutorial/src/first_steps/tutorial002.py!}
 ```
 
 And put it in a file `main.py`, then you would call `uvicorn` like:
@@ -188,7 +188,7 @@ We are going to call them "operations" too.
 #### Define a path operation function
 
 ```Python hl_lines="6"
-{!tutorial/src/first-steps/tutorial001.py!}
+{!tutorial/src/first_steps/tutorial001.py!}
 ```
 
 The `@app.get("/")` tells **FastAPI** that the function right below is in charge of handling requests that go to:
@@ -221,7 +221,7 @@ And the more exotic ones:
 ### Step 4: define the path operation function
 
 ```Python hl_lines="7"
-{!tutorial/src/first-steps/tutorial001.py!}
+{!tutorial/src/first_steps/tutorial001.py!}
 ```
 
 This is a Python function. 
@@ -235,7 +235,7 @@ In this case, it is an `async` function.
 You could also define it as a normal function instead of `async def`:
 
 ```Python hl_lines="7"
-{!tutorial/src/first-steps/tutorial003.py!}
+{!tutorial/src/first_steps/tutorial003.py!}
 ```
 
 To know the difference, read the section about [Concurrency and `async` / `await`](/async/).
@@ -243,7 +243,7 @@ To know the difference, read the section about [Concurrency and `async` / `await
 ### Step 5: return the content
 
 ```Python hl_lines="8"
-{!tutorial/src/first-steps/tutorial001.py!}
+{!tutorial/src/first_steps/tutorial001.py!}
 ```
 
 You can return a `dict`, `list`, singular values as `str`, `int`, etc.
index f18701f9d79dd350e103d5f52939e517b9744a68..67c2026b9a0b31e646a7cf569178c9a9bcab8309 100644 (file)
@@ -5,7 +5,7 @@ You can define Header parameters the same way you define `Query`, `Path` and `Co
 First import `Header`:
 
 ```Python hl_lines="1"
-{!./tutorial/src/header-params/tutorial001.py!}
+{!./tutorial/src/header_params/tutorial001.py!}
 ```
 
 ## Declare `Header` parameteres
@@ -15,7 +15,7 @@ Then declare the header parameters using the same structure as with `Path`, `Que
 The first value is the default value, you can pass all the extra validation or annotation parameteres:
 
 ```Python hl_lines="7"
-{!./tutorial/src/header-params/tutorial001.py!}
+{!./tutorial/src/header_params/tutorial001.py!}
 ```
 
 !!! info
@@ -41,7 +41,7 @@ So, you can use `user_agent` as you normally would in Python code, instead of ne
 If for some reason you need to disable automatic conversion of underscores to hyphens, set the parameter `convert_underscores` of `Header` to `False`:
 
 ```Python hl_lines="7"
-{!./tutorial/src/header-params/tutorial002.py!}
+{!./tutorial/src/header_params/tutorial002.py!}
 ```
 
 !!! warning
index 96d7f73f3f033be5b8fed3e267c34de796411ca3..bbfac3b9a2dba58f6d7981036f7f72e1add2d56e 100644 (file)
@@ -14,8 +14,8 @@ You can adapt it to any other NoSQL database like:
 
 For now, don't pay attention to the rest, only the imports:
 
-```Python hl_lines="6 7 8"
-{!./tutorial/src/nosql-databases/tutorial001.py!}
+```Python hl_lines="5 6 7"
+{!./tutorial/src/nosql_databases/tutorial001.py!}
 ```
 
 ## Define a constant to use as a "document type"
@@ -25,7 +25,7 @@ We will use it later as a fixed field `type` in our documents.
 This is not required by Couchbase, but is a good practice that will help you afterwards.
 
 ```Python hl_lines="10"
-{!./tutorial/src/nosql-databases/tutorial001.py!}
+{!./tutorial/src/nosql_databases/tutorial001.py!}
 ```
 
 ## Add a function to get a `Bucket`
@@ -49,8 +49,8 @@ This utility function will:
     * Set defaults for timeouts.
 * Return it.
 
-```Python hl_lines="13 14 15 16 17 18 19 20"
-{!./tutorial/src/nosql-databases/tutorial001.py!}
+```Python hl_lines="13 14 15 16 17 18 19 20 21 22"
+{!./tutorial/src/nosql_databases/tutorial001.py!}
 ```
 
 ## Create Pydantic models
@@ -61,8 +61,8 @@ As **Couchbase** "documents" are actually just "JSON objects", we can model them
 
 First, let's create a `User` model:
 
-```Python hl_lines="23 24 25 26 27"
-{!./tutorial/src/nosql-databases/tutorial001.py!}
+```Python hl_lines="25 26 27 28 29"
+{!./tutorial/src/nosql_databases/tutorial001.py!}
 ```
 
 We will use this model in our path operation function, so, we don't include in it the `hashed_password`.
@@ -75,8 +75,8 @@ This will have the data that is actually stored in the database.
 
 We don't create it as a subclass of Pydantic's `BaseModel` but as a subclass of our own `User`, because it will have all the attributes in `User` plus a couple more:
 
-```Python hl_lines="30 31 32"
-{!./tutorial/src/nosql-databases/tutorial001.py!}
+```Python hl_lines="32 33 34"
+{!./tutorial/src/nosql_databases/tutorial001.py!}
 ```
 
 !!! note
@@ -96,8 +96,8 @@ Now create a function that will:
 
 By creating a function that is only dedicated to getting your user from a `username` (or any other parameter) independent of your path operation function, you can more easily re-use it in multiple parts and also add <abbr title="Automated test, written in code, that checks if another piece of code is working correctly.">unit tests</abbr> for it:
 
-```Python hl_lines="35 36 37 38 39 40 41"
-{!./tutorial/src/nosql-databases/tutorial001.py!}
+```Python hl_lines="37 38 39 40 41 42 43"
+{!./tutorial/src/nosql_databases/tutorial001.py!}
 ```
 
 ### f-strings
@@ -131,8 +131,8 @@ UserInDB(username="johndoe", hashed_password="some_hash")
 
 ### Create the `FastAPI` app
 
-```Python hl_lines="45"
-{!./tutorial/src/nosql-databases/tutorial001.py!}
+```Python hl_lines="47"
+{!./tutorial/src/nosql_databases/tutorial001.py!}
 ```
 
 ### Create the path operation function
@@ -141,8 +141,8 @@ As our code is calling Couchbase and we are not using the <a href="https://docs.
 
 Also, Couchbase recommends not using a single `Bucket` object in multiple "<abbr title="A sequence of code being executed by the program, while at the same time, or at intervals, there can be others being executed too.">thread</abbr>s", so, we can get just get the bucket directly and pass it to our utility functions:
 
-```Python hl_lines="48 49 50 51 52"
-{!./tutorial/src/nosql-databases/tutorial001.py!}
+```Python hl_lines="50 51 52 53 54"
+{!./tutorial/src/nosql_databases/tutorial001.py!}
 ```
 
 ## Recap
index f88a640ac242224dab68860126485173b02762f9..635533b05ef9986bbe01c8de12e237cdb56ab0d7 100644 (file)
@@ -8,7 +8,7 @@ You can set the OpenAPI `operationId` to be used in your path operation with the
 You would have to make sure that it is unique for each operation.
 
 ```Python hl_lines="6"
-{!./tutorial/src/path-operation-advanced-configuration/tutorial001.py!}
+{!./tutorial/src/path_operation_advanced_configuration/tutorial001.py!}
 ```
 
 ## Exclude from OpenAPI
@@ -16,5 +16,5 @@ You would have to make sure that it is unique for each operation.
 To exclude a path operation from the generated OpenAPI schema (and thus, from the automatic documentation systems), use the parameter `include_in_schema` and set it to `False`;
 
 ```Python hl_lines="6"
-{!./tutorial/src/path-operation-advanced-configuration/tutorial002.py!}
+{!./tutorial/src/path_operation_advanced_configuration/tutorial002.py!}
 ```
index 53b33359e8f50543dd8394a0559db05c9993f125..391d1e6bcb8e91e953bc1914a9975ef11b53a1b4 100644 (file)
@@ -11,8 +11,8 @@ You can pass directly the `int` code, like `404`.
 
 But if you don't remember what each number code is for, you can use the shortcut constants from `starlette`:
 
-```Python hl_lines="5 18"
-{!./tutorial/src/path-operation-configuration/tutorial001.py!}
+```Python hl_lines="4 19"
+{!./tutorial/src/path_operation_configuration/tutorial001.py!}
 ```
 
 That status code will be used in the response and will be added to the OpenAPI schema.
@@ -22,8 +22,8 @@ That status code will be used in the response and will be added to the OpenAPI s
 
 You can add tags to your path operation, pass the parameter `tags` with a `list` of `str` (commonly just one `str`):
 
-```Python hl_lines="17 22 27"
-{!./tutorial/src/path-operation-configuration/tutorial002.py!}
+```Python hl_lines="18 23 28"
+{!./tutorial/src/path_operation_configuration/tutorial002.py!}
 ```
 
 They will be added to the OpenAPI schema and used by the automatic documentation interfaces:
@@ -34,16 +34,16 @@ They will be added to the OpenAPI schema and used by the automatic documentation
 
 You can add a `summary` and `description`:
 
-```Python hl_lines="20 21"
-{!./tutorial/src/path-operation-configuration/tutorial003.py!}
+```Python hl_lines="21 22"
+{!./tutorial/src/path_operation_configuration/tutorial003.py!}
 ```
 
 ## Description from docstring
 
 As descriptions tend to be long and cover multiple lines, you can declare the path operation description in the function <abbr title="a multi-line string as the first expression inside a function (not assigned to any variable) used for documentation">docstring</abbr> and **FastAPI** will read it from there.
 
-```Python hl_lines="19 20 21 22 23 24 25 26 27"
-{!./tutorial/src/path-operation-configuration/tutorial004.py!}
+```Python hl_lines="20 21 22 23 24 25 26 27 28"
+{!./tutorial/src/path_operation_configuration/tutorial004.py!}
 ```
 
 It will be used in the interactive docs:
@@ -57,8 +57,8 @@ It will be used in the interactive docs:
 
 You can specify the response description with the parameter `response_description`:
 
-```Python hl_lines="21"
-{!./tutorial/src/path-operation-configuration/tutorial005.py!}
+```Python hl_lines="22"
+{!./tutorial/src/path_operation_configuration/tutorial005.py!}
 ```
 
 !!! info
@@ -77,7 +77,7 @@ If you need to mark a path operation as <abbr title="obsolete, recommended not t
 
 
 ```Python hl_lines="16"
-{!./tutorial/src/path-operation-configuration/tutorial006.py!}
+{!./tutorial/src/path_operation_configuration/tutorial006.py!}
 ```
 
 It will be clearly marked as deprecated in the interactive docs:
index eade3da020f835357fb361f89d481593486abd5c..641129593fe47dbbcfe19dca21584c01f7e72d7e 100644 (file)
@@ -5,7 +5,7 @@ The same way you can declare more validations and metadata for query parameters
 First, import `Path` from `fastapi`:
 
 ```Python hl_lines="1"
-{!./tutorial/src/path-params-numeric-validations/tutorial001.py!}
+{!./tutorial/src/path_params_numeric_validations/tutorial001.py!}
 ```
 
 ## Declare metadata
@@ -15,7 +15,7 @@ You can declare all the same parameters as for `Query`.
 For example, to declare a `title` metadata value for the path parameter `item_id` you can type:
 
 ```Python hl_lines="8"
-{!./tutorial/src/path-params-numeric-validations/tutorial001.py!}
+{!./tutorial/src/path_params_numeric_validations/tutorial001.py!}
 ```
 
 !!! note
@@ -42,7 +42,7 @@ It doesn't matter for **FastAPI**. It will detect the parameters by their names,
 So, you can declare your function as:
 
 ```Python hl_lines="8"
-{!./tutorial/src/path-params-numeric-validations/tutorial002.py!}
+{!./tutorial/src/path_params_numeric_validations/tutorial002.py!}
 ```
 
 ## Order the parameters as you need, tricks
@@ -54,7 +54,7 @@ Pass `*`, as the first parameter of the function.
 Python won't do anything with that `*`, but it will know that all the following parameters should be called as keyword arguments (key-value pairs), also known as <abbr title="From: K-ey W-ord Arg-uments"><code>kwargs</code></abbr>. Even if they don't have a default value.
 
 ```Python hl_lines="8"
-{!./tutorial/src/path-params-numeric-validations/tutorial003.py!}
+{!./tutorial/src/path_params_numeric_validations/tutorial003.py!}
 ```
 
 ## Number validations: greater than or equal
@@ -64,7 +64,7 @@ With `Query` and `Path` (and other's you'll see later) you can declare string co
 Here, with `ge=1`, `item_id` will need to be an integer number "`g`reater than or `e`qual" to `1`.
 
 ```Python hl_lines="8"
-{!./tutorial/src/path-params-numeric-validations/tutorial004.py!}
+{!./tutorial/src/path_params_numeric_validations/tutorial004.py!}
 ```
 ## Number validations: greater than and less than or equal
 
@@ -74,7 +74,7 @@ The same applies for:
 * `le`: `l`ess than or `e`qual
 
 ```Python hl_lines="9"
-{!./tutorial/src/path-params-numeric-validations/tutorial005.py!}
+{!./tutorial/src/path_params_numeric_validations/tutorial005.py!}
 ```
 
 ## Number validations: floats, greater than and less than
@@ -88,7 +88,7 @@ So, `0.5` would be a valid value. But `0.0` or `0` would not.
 And the same for <abbr title="less than"><code>lt</code></abbr>.
 
 ```Python hl_lines="11"
-{!./tutorial/src/path-params-numeric-validations/tutorial006.py!}
+{!./tutorial/src/path_params_numeric_validations/tutorial006.py!}
 ```
 
 ## Recap
index 63898c31560e121546644ae8889c9a753b17aafa..68d9973f95752b426414559821978d5f78f6dad4 100644 (file)
@@ -1,7 +1,7 @@
 You can declare path "parameters" or "variables" with the same syntax used by Python format strings:
 
 ```Python hl_lines="6 7"
-{!./tutorial/src/path-params/tutorial001.py!}
+{!./tutorial/src/path_params/tutorial001.py!}
 ```
 
 The value of the path parameter `item_id` will be passed to your function as the argument `item_id`.
@@ -17,7 +17,7 @@ So, if you run this example and go to <a href="http://127.0.0.1:8000/items/foo"
 You can declare the type of a path parameter in the function, using standard Python type annotations:
 
 ```Python hl_lines="7"
-{!./tutorial/src/path-params/tutorial002.py!}
+{!./tutorial/src/path_params/tutorial002.py!}
 ```
 
 In this case, `item_id` is declared to be an `int`.
index 8938e81e7c726f4a21e3821a0722c92ab400264b..2b991ffae1ddc5f0cfbaf7ceb4dafc98e3064ae2 100644 (file)
@@ -18,7 +18,7 @@ But even if you never use **FastAPI**, you would benefit from learning a bit abo
 Let's start with a simple example:
 
 ```Python
-{!./tutorial/src/python-types/tutorial001.py!}
+{!./tutorial/src/python_types/tutorial001.py!}
 ```
 
 Calling this program outputs:
@@ -34,7 +34,7 @@ The function does the following:
 * <abbr title="Puts them together, as one. With the contents of one after the other.">Concatenates</abbr> them with a space in the middle.
 
 ```Python hl_lines="2"
-{!./tutorial/src/python-types/tutorial001.py!}
+{!./tutorial/src/python_types/tutorial001.py!}
 ```
 
 ### Edit it
@@ -78,7 +78,7 @@ That's it.
 Those are the "type hints":
 
 ```Python hl_lines="1"
-{!./tutorial/src/python-types/tutorial002.py!}
+{!./tutorial/src/python_types/tutorial002.py!}
 ```
 
 That is not the same as declaring default values like would be with:
@@ -108,7 +108,7 @@ With that, you can scroll, seeing the options, until you find the one that "ring
 Check this function, it already has type hints:
 
 ```Python hl_lines="1"
-{!./tutorial/src/python-types/tutorial003.py!}
+{!./tutorial/src/python_types/tutorial003.py!}
 ```
 
 Because the editor knows the types of the variables, you don't only get completion, you also get error checks:
@@ -118,7 +118,7 @@ Because the editor knows the types of the variables, you don't only get completi
 Now you know that you have to fix it, convert `age` to a string with `str(age)`:
 
 ```Python hl_lines="2"
-{!./tutorial/src/python-types/tutorial004.py!}
+{!./tutorial/src/python_types/tutorial004.py!}
 ```
 
 
@@ -140,7 +140,7 @@ You can use, for example:
 * `bytes`
 
 ```Python hl_lines="1"
-{!./tutorial/src/python-types/tutorial005.py!}
+{!./tutorial/src/python_types/tutorial005.py!}
 ```
 
 ### Types with subtypes
@@ -158,7 +158,7 @@ For example, let's define a variable to be a `list` of `str`.
 From `typing`, import `List` (with a capital `L`):
 
 ```Python hl_lines="1"
-{!./tutorial/src/python-types/tutorial006.py!}
+{!./tutorial/src/python_types/tutorial006.py!}
 ```
 
 Declare the variable, with the same colon (`:`) syntax.
@@ -168,7 +168,7 @@ As the type, put the `List`.
 As the list is a type that takes a "subtype", you put the subtype in square brackets:
 
 ```Python hl_lines="4"
-{!./tutorial/src/python-types/tutorial006.py!}
+{!./tutorial/src/python_types/tutorial006.py!}
 ```
 
 That means: "the variable `items` is a `list`, and each of the items in this list is a `str`".
@@ -188,7 +188,7 @@ And still, the editor knows it is a `str`, and provides support for that.
 You would do the same to declare `tuple`s and `set`s:
 
 ```Python hl_lines="1 4"
-{!./tutorial/src/python-types/tutorial007.py!}
+{!./tutorial/src/python_types/tutorial007.py!}
 ```
 
 This means:
@@ -205,7 +205,7 @@ The first subtype is for the keys of the `dict`.
 The second subtype is for the values of the `dict`:
 
 ```Python hl_lines="1 4"
-{!./tutorial/src/python-types/tutorial008.py!}
+{!./tutorial/src/python_types/tutorial008.py!}
 ```
 
 This means:
@@ -222,13 +222,13 @@ You can also declare a class as the type of a variable.
 Let's say you have a class `Person`, with a name:
 
 ```Python hl_lines="1 2 3"
-{!./tutorial/src/python-types/tutorial009.py!}
+{!./tutorial/src/python_types/tutorial009.py!}
 ```
 
 Then you can declare a variable to be of type `Person`:
 
 ```Python hl_lines="6"
-{!./tutorial/src/python-types/tutorial009.py!}
+{!./tutorial/src/python_types/tutorial009.py!}
 ```
 
 And then, again, you get all the editor support:
@@ -251,7 +251,7 @@ And you get all the editor support with that resulting object.
 Taken from the official Pydantic docs:
 
 ```Python
-{!./tutorial/src/python-types/tutorial010.py!}
+{!./tutorial/src/python_types/tutorial010.py!}
 ```
 
 !!! info
index 357c7bf80812bfbc148694393bb099943bf484c1..afed65be88870b8dee5df6fc173d42275d89498f 100644 (file)
@@ -3,7 +3,7 @@
 Let's take this application as example:
 
 ```Python hl_lines="7"
-{!./tutorial/src/query-params-str-validations/tutorial001.py!}
+{!./tutorial/src/query_params_str_validations/tutorial001.py!}
 ```
 
 The query parameter `q` is of type `str`, and by default is `None`, so it is optional.
@@ -18,7 +18,7 @@ We are going to enforce that even though `q` is optional, whenever it is provide
 To achieve that, first import `Query` from `fastapi`:
 
 ```Python hl_lines="1"
-{!./tutorial/src/query-params-str-validations/tutorial002.py!}
+{!./tutorial/src/query_params_str_validations/tutorial002.py!}
 ```
 
 ## Use `Query` as the default value
@@ -26,7 +26,7 @@ To achieve that, first import `Query` from `fastapi`:
 And now use it as the default value of your parameter, setting the parameter `max_length` to 50:
 
 ```Python hl_lines="7"
-{!./tutorial/src/query-params-str-validations/tutorial002.py!}
+{!./tutorial/src/query_params_str_validations/tutorial002.py!}
 ```
 
 As we have to replace the default value `None` with `Query(None)`, the first parameter to `Query` serves the same purpose of defining that default value. 
@@ -59,7 +59,7 @@ This will validate the data, show a clear error when the data is not valid, and
 You can also add a parameter `min_length`:
 
 ```Python hl_lines="7"
-{!./tutorial/src/query-params-str-validations/tutorial003.py!}
+{!./tutorial/src/query_params_str_validations/tutorial003.py!}
 ```
 
 ## Add regular expressions
@@ -67,7 +67,7 @@ You can also add a parameter `min_length`:
 You can define a <abbr title="A regular expression, regex or regexp is a sequence of characters that define a search pattern for strings.">regular expression</abbr> that the parameter should match:
 
 ```Python hl_lines="8"
-{!./tutorial/src/query-params-str-validations/tutorial004.py!}
+{!./tutorial/src/query_params_str_validations/tutorial004.py!}
 ```
 
 This specific regular expression checks that the received parameter value:
@@ -87,7 +87,7 @@ The same way that you can pass `None` as the first argument to be used as the de
 Let's say that you want to declare the `q` query parameter to have a `min_length` of `3`, and to have a default value of `"fixedquery"`:
 
 ```Python hl_lines="7"
-{!./tutorial/src/query-params-str-validations/tutorial005.py!}
+{!./tutorial/src/query_params_str_validations/tutorial005.py!}
 ```
 
 !!! note
@@ -116,7 +116,7 @@ q: str = Query(None, min_length=3)
 So, when you need to declare a value as required while using `Query`, you can use `...` as the first argument:
 
 ```Python hl_lines="7"
-{!./tutorial/src/query-params-str-validations/tutorial006.py!}
+{!./tutorial/src/query_params_str_validations/tutorial006.py!}
 ```
 
 !!! info 
@@ -133,13 +133,13 @@ That information will be included in the generated OpenAPI and used by the docum
 You can add a `title`:
 
 ```Python hl_lines="7"
-{!./tutorial/src/query-params-str-validations/tutorial007.py!}
+{!./tutorial/src/query_params_str_validations/tutorial007.py!}
 ```
 
 And a `description`:
 
 ```Python hl_lines="11"
-{!./tutorial/src/query-params-str-validations/tutorial008.py!}
+{!./tutorial/src/query_params_str_validations/tutorial008.py!}
 ```
 
 ## Alias parameters
@@ -161,7 +161,7 @@ But you still need it to be exactly `item-query`...
 Then you can declare an `alias`, and that alias is what will be used to find the parameter value:
 
 ```Python hl_lines="7"
-{!./tutorial/src/query-params-str-validations/tutorial009.py!}
+{!./tutorial/src/query_params_str_validations/tutorial009.py!}
 ```
 
 ## Deprecating parameters
@@ -173,7 +173,7 @@ You have to leave it there a while because there are clients using it, but you w
 Then pass the parameter `deprecated=True` to `Query`:
 
 ```Python hl_lines="16"
-{!./tutorial/src/query-params-str-validations/tutorial010.py!}
+{!./tutorial/src/query_params_str_validations/tutorial010.py!}
 ```
 
 The docs will show it like this:
index 6109cb74211ff6af0e50f88c256de10a4b4f55a6..dd75e00a5dbd2e361cf22a01d3eab09d8e1af698 100644 (file)
@@ -1,7 +1,7 @@
 When you declare other function parameters that are not part of the path parameters, they are automatically interpreted as "query" parameters.
 
 ```Python hl_lines="9"
-{!./tutorial/src/query-params/tutorial001.py!}
+{!./tutorial/src/query_params/tutorial001.py!}
 ```
 
 The query is the set of key-value pairs that go after the `?` in a URL, separated by `&` characters.
@@ -62,7 +62,7 @@ The parameter values in your function will be:
 The same way, you can declare optional query parameters, by setting their default to `None`:
 
 ```Python hl_lines="7"
-{!./tutorial/src/query-params/tutorial002.py!}
+{!./tutorial/src/query_params/tutorial002.py!}
 ```
 
 In this case, the function parameter `q` will be optional, and will be `None` by default.
@@ -75,7 +75,7 @@ In this case, the function parameter `q` will be optional, and will be `None` by
 You can also declare `bool` types, and they will be converted:
 
 ```Python hl_lines="7"
-{!./tutorial/src/query-params/tutorial003.py!}
+{!./tutorial/src/query_params/tutorial003.py!}
 ```
 
 In this case, if you go to:
@@ -120,7 +120,7 @@ And you don't have to declare them in any specific order.
 They will be detected by name:
 
 ```Python hl_lines="6 8"
-{!./tutorial/src/query-params/tutorial004.py!}
+{!./tutorial/src/query_params/tutorial004.py!}
 ```
 
 ## Required query parameters
@@ -132,7 +132,7 @@ If you don't want to add a specific value but just make it optional, set the def
 But when you want to make a query parameter required, you can just do not declare any default value:
 
 ```Python hl_lines="6 7"
-{!./tutorial/src/query-params/tutorial005.py!}
+{!./tutorial/src/query_params/tutorial005.py!}
 ```
 
 Here the query parameter `needy` is a required query parameter of type `str`.
index b8fa8f1e401cac39afbb55e61a883c52857178fb..aecde928a211ee6afb1c252157308de069efe8f3 100644 (file)
@@ -5,7 +5,7 @@ You can define files to be uploaded by the client using `File`.
 Import `File` from `fastapi`:
 
 ```Python hl_lines="1"
-{!./tutorial/src/request-files/tutorial001.py!}
+{!./tutorial/src/request_files/tutorial001.py!}
 ```
 
 ## Define `File` parameters
@@ -13,7 +13,7 @@ Import `File` from `fastapi`:
 Create file parameters the same way you would for `Body` or `Form`:
 
 ```Python hl_lines="7"
-{!./tutorial/src/request-files/tutorial001.py!}
+{!./tutorial/src/request_files/tutorial001.py!}
 ```
 
 The files will be uploaded as form data and you will receive the contents as `bytes`.
index ed777c2aeb6d20a95d5b219fc3a00c39bca53fd7..3c32d13adc057390f3c392cd835490b9db0cb8c4 100644 (file)
@@ -3,7 +3,7 @@ You can define files and form fields at the same time using `File` and `Form`.
 ## Import `File` and `Form`
 
 ```Python hl_lines="1"
-{!./tutorial/src/request-forms-and-files/tutorial001.py!}
+{!./tutorial/src/request_forms_and_files/tutorial001.py!}
 ```
 
 ## Define `File` and `Form` parameters
@@ -11,7 +11,7 @@ You can define files and form fields at the same time using `File` and `Form`.
 Create file and form parameters the same way you would for `Body` or `Query`:
 
 ```Python hl_lines="7"
-{!./tutorial/src/request-forms-and-files/tutorial001.py!}
+{!./tutorial/src/request_forms_and_files/tutorial001.py!}
 ```
 
 The files and form fields will be uploaded as form data and you will receive the files and form fields.
index 757e9313c6d30286b523009b594b40e7a26ed384..7c506fc2300c107b06cc25a089f0b37e732ba922 100644 (file)
@@ -5,7 +5,7 @@ When you need to receive form fields instead of JSON, you can use `Form`.
 Import `Form` from `fastapi`:
 
 ```Python hl_lines="1"
-{!./tutorial/src/request-forms/tutorial001.py!}
+{!./tutorial/src/request_forms/tutorial001.py!}
 ```
 
 ## Define `Form` parameters
@@ -13,7 +13,7 @@ Import `Form` from `fastapi`:
 Create form parameters the same way you would for `Body` or `Query`:
 
 ```Python hl_lines="7"
-{!./tutorial/src/request-forms/tutorial001.py!}
+{!./tutorial/src/request_forms/tutorial001.py!}
 ```
 
 For example, in one of the ways the OAuth2 specification can be used (called "password flow") it is required to send a `username` and `password` as form fields.
index c1c28780339ca864c91b7ccfad0d520cfcf438da..33bc15d843bcd296fa31f08f99f301a5e786abd9 100644 (file)
@@ -6,8 +6,8 @@ You can declare the model used for the response with the parameter `response_mod
 * `@app.delete()`
 * etc.
 
-```Python hl_lines="17"
-{!./tutorial/src/response-model/tutorial001.py!}
+```Python hl_lines="18"
+{!./tutorial/src/response_model/tutorial001.py!}
 ```
 
 !!! note
@@ -28,14 +28,14 @@ But most importantly:
 
 Here we are declaring a `UserIn` model, it will contain a plaintext password:
 
-```Python hl_lines="8 10"
-{!./tutorial/src/response-model/tutorial002.py!}
+```Python hl_lines="9 11"
+{!./tutorial/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"
-{!./tutorial/src/response-model/tutorial002.py!}
+```Python hl_lines="17 18"
+{!./tutorial/src/response_model/tutorial002.py!}
 ```
 
 Now, whenever a browser is creating a user with a password, the API will return the same password in the response.
@@ -51,20 +51,20 @@ But if we use sthe same model for another path operation, we could be sending th
 
 We can instead create an input model with the plaintext password and an output model without it:
 
-```Python hl_lines="8 10 15"
-{!./tutorial/src/response-model/tutorial003.py!}
+```Python hl_lines="9 11 16"
+{!./tutorial/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"
-{!./tutorial/src/response-model/tutorial003.py!}
+```Python hl_lines="24"
+{!./tutorial/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"
-{!./tutorial/src/response-model/tutorial003.py!}
+```Python hl_lines="22"
+{!./tutorial/src/response_model/tutorial003.py!}
 ```
 
 So, **FastAPI** will take care of filtering out all the data that is not declared in the output model (using Pydantic).
index 5b5d528a30282cc8032b7b5aac5349f84a9f23ba..1fbca84bdb6d55ae0ca0e94bddbf678e8a4c44ed 100644 (file)
@@ -23,16 +23,16 @@ In this example, we'll use **PostgreSQL**.
 
 For now, don't pay attention to the rest, only the imports:
 
-```Python hl_lines="3 4 5"
-{!./tutorial/src/sql-databases/tutorial001.py!}
+```Python hl_lines="2 3 4"
+{!./tutorial/src/sql_databases/tutorial001.py!}
 ```
 
 ## Define the database
 
 Define the database that SQLAlchemy should connect to:
 
-```Python hl_lines="8"
-{!./tutorial/src/sql-databases/tutorial001.py!}
+```Python hl_lines="7"
+{!./tutorial/src/sql_databases/tutorial001.py!}
 ```
 
 !!! tip
@@ -40,14 +40,14 @@ Define the database that SQLAlchemy should connect to:
 
 ## Create the SQLAlchemy `engine`
 
-```Python hl_lines="10"
-{!./tutorial/src/sql-databases/tutorial001.py!}
+```Python hl_lines="9"
+{!./tutorial/src/sql_databases/tutorial001.py!}
 ```
 
 ## Create a `scoped_session`
 
-```Python hl_lines="11 12 13"
-{!./tutorial/src/sql-databases/tutorial001.py!}
+```Python hl_lines="10 11 12"
+{!./tutorial/src/sql_databases/tutorial001.py!}
 ```
 
 !!! note "Very Technical Details"
@@ -70,13 +70,13 @@ That way you don't have to declare them explicitly.
 So, your models will behave very similarly to, for example, Flask-SQLAlchemy.
 
 ```Python hl_lines="15 16 17 18 19"
-{!./tutorial/src/sql-databases/tutorial001.py!}
+{!./tutorial/src/sql_databases/tutorial001.py!}
 ```
 
 ## Create the SQLAlchemy `Base` model
 
 ```Python hl_lines="22"
-{!./tutorial/src/sql-databases/tutorial001.py!}
+{!./tutorial/src/sql_databases/tutorial001.py!}
 ```
 
 ## Create your application data model
@@ -86,7 +86,7 @@ Now this is finally code specific to your app.
 Here's a user model that will be a table in the database:
 
 ```Python hl_lines="25 26 27 28 29"
-{!./tutorial/src/sql-databases/tutorial001.py!}
+{!./tutorial/src/sql_databases/tutorial001.py!}
 ```
 
 ## Get a user
@@ -94,7 +94,7 @@ Here's a user model that will be a table in the database:
 By creating a function that is only dedicated to getting your user from a `username` (or any other parameter) independent of your path operation function, you can more easily re-use it in multiple parts and also add <abbr title="Automated test, written in code, that checks if another piece of code is working correctly.">unit tests</abbr> for it:
 
 ```Python hl_lines="32 33"
-{!./tutorial/src/sql-databases/tutorial001.py!}
+{!./tutorial/src/sql_databases/tutorial001.py!}
 ```
 
 ## Create your **FastAPI** code
@@ -104,7 +104,7 @@ Now, finally, here's the standard **FastAPI** code.
 Create your app and path operation function:
 
 ```Python hl_lines="37 40 41 42 43"
-{!./tutorial/src/sql-databases/tutorial001.py!}
+{!./tutorial/src/sql_databases/tutorial001.py!}
 ```
 
 As we are using SQLAlchemy's `scoped_session`, we don't even have to create a dependency with `Depends`.
@@ -132,7 +132,7 @@ user = get_user(username, db_session)
 Then we should declare the path operation without `async def`, just with a normal `def`:
 
 ```Python hl_lines="41"
-{!./tutorial/src/sql-databases/tutorial001.py!}
+{!./tutorial/src/sql_databases/tutorial001.py!}
 ```
 
 ## Migrations
diff --git a/docs/tutorial/src/bigger_applications/__init__.py b/docs/tutorial/src/bigger_applications/__init__.py
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/docs/tutorial/src/bigger_applications/app/__init__.py b/docs/tutorial/src/bigger_applications/app/__init__.py
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/docs/tutorial/src/bigger_applications/app/routers/__init__.py b/docs/tutorial/src/bigger_applications/app/routers/__init__.py
new file mode 100644 (file)
index 0000000..e69de29
similarity index 100%
rename from docs/tutorial/src/bigger-applications/tutorial001.py
rename to docs/tutorial/src/bigger_applications/app/routers/tutorial001.py
index aa365ed05c0d5d524860995afb3a3be016f53325..37e3fbc4a361338b668f753b895475a0c708e3d3 100644 (file)
@@ -8,11 +8,11 @@ async def read_users():
     return [{"username": "Foo"}, {"username": "Bar"}]
 
 
-@router.get("/users/{username}")
-async def read_user(username: str):
-    return {"username": username}
-
-
 @router.get("/users/me")
 async def read_user_me():
     return {"username": "fakecurrentuser"}
+
+
+@router.get("/users/{username}")
+async def read_user(username: str):
+    return {"username": username}
similarity index 53%
rename from docs/tutorial/src/bigger-applications/tutorial003.py
rename to docs/tutorial/src/bigger_applications/app/tutorial003.py
index 88559fcd48b92f09aedd03691a90420b2deb8a78..4c2a348db3e7a5b2f6f7e3fb97e8c76163c77021 100644 (file)
@@ -1,7 +1,7 @@
 from fastapi import FastAPI
 
-from .tutorial01 import router as users_router
-from .tutorial02 import router as items_router
+from .routers.tutorial001 import router as users_router
+from .routers.tutorial002 import router as items_router
 
 app = FastAPI()
 
index e99a46ccc8925607eadefa5ae28a3ba295f5e982..b3ccb12dd8145f28db6f66d0c67f74bc06005597 100644 (file)
@@ -1,6 +1,7 @@
-from fastapi import FastAPI
 from pydantic import BaseModel
 
+from fastapi import FastAPI
+
 
 class Item(BaseModel):
     name: str
index cce5eca49fb1633b260e012115f6a6a392d969b3..4b7412f425f4f02a018cd3c23b9698a8d13dd6d9 100644 (file)
@@ -1,6 +1,7 @@
-from fastapi import FastAPI
 from pydantic import BaseModel
 
+from fastapi import FastAPI
+
 
 class Item(BaseModel):
     name: str
index 1d3e902400d103bf399ac71492f05668ae170bd9..312de2dafa6f980465f037c2ab4348a75be442fd 100644 (file)
@@ -1,6 +1,7 @@
-from fastapi import FastAPI
 from pydantic import BaseModel
 
+from fastapi import FastAPI
+
 
 class Item(BaseModel):
     name: str
index c389af241971da0e6c3aa433b468d9d550a71be7..499d5690d93bad24c38762ac0d7386f8400f9131 100644 (file)
@@ -1,6 +1,7 @@
-from fastapi import FastAPI
 from pydantic import BaseModel
 
+from fastapi import FastAPI
+
 
 class Item(BaseModel):
     name: str
similarity index 96%
rename from docs/tutorial/src/body-multiple-params/tutorial001.py
rename to docs/tutorial/src/body_multiple_params/tutorial001.py
index 7918a2f96b859953b4b7a63c3a37e9c0cdef49b6..4c1286ab3d494c32170a6a183cff9f57b6ca1d8a 100644 (file)
@@ -1,6 +1,7 @@
-from fastapi import FastAPI, Path
 from pydantic import BaseModel
 
+from fastapi import FastAPI, Path
+
 app = FastAPI()
 
 
@@ -15,7 +16,7 @@ class Item(BaseModel):
 async def update_item(
     *,
     item_id: int = Path(..., title="The ID of the item to get", ge=0, le=1000),
-    q: str,
+    q: str = None,
     item: Item = None,
 ):
     results = {"item_id": item_id}
similarity index 99%
rename from docs/tutorial/src/body-multiple-params/tutorial002.py
rename to docs/tutorial/src/body_multiple_params/tutorial002.py
index 5c9e8344d834292b2110c24d405058ce939ddbf9..1db8d6e19b8ddde9fa21a7c75e544203d4ebd24d 100644 (file)
@@ -1,6 +1,7 @@
-from fastapi import FastAPI
 from pydantic import BaseModel
 
+from fastapi import FastAPI
+
 app = FastAPI()
 
 
similarity index 99%
rename from docs/tutorial/src/body-multiple-params/tutorial003.py
rename to docs/tutorial/src/body_multiple_params/tutorial003.py
index 301f1a86214ede899d78357cbaff8dff54d82812..fa385d9cd45d20558f60cebb2cf60b65749541fc 100644 (file)
@@ -1,6 +1,7 @@
-from fastapi import Body, FastAPI
 from pydantic import BaseModel
 
+from fastapi import Body, FastAPI
+
 app = FastAPI()
 
 
similarity index 99%
rename from docs/tutorial/src/body-multiple-params/tutorial004.py
rename to docs/tutorial/src/body_multiple_params/tutorial004.py
index 359b33cccb1e81181dd10172cc26b8699b3359b5..8ac1b48019429dde0c35e9efe3fa501eb18dac76 100644 (file)
@@ -1,6 +1,7 @@
-from fastapi import Body, FastAPI
 from pydantic import BaseModel
 
+from fastapi import Body, FastAPI
+
 app = FastAPI()
 
 
similarity index 99%
rename from docs/tutorial/src/body-multiple-params/tutorial005.py
rename to docs/tutorial/src/body_multiple_params/tutorial005.py
index 61f1b29170f0c3e68d3f501f3a1bf152cb225030..2ff02e8c01d9ce24a739dab5a66781a3eabac40c 100644 (file)
@@ -1,6 +1,7 @@
-from fastapi import Body, FastAPI
 from pydantic import BaseModel
 
+from fastapi import Body, FastAPI
+
 app = FastAPI()
 
 
similarity index 99%
rename from docs/tutorial/src/body-nested-models/tutorial001.py
rename to docs/tutorial/src/body_nested_models/tutorial001.py
index 9e0fa4494e8cbcef8e5ea695197769d4b2e3591f..80dffebc465bc639f9127594e1a112efe000cff9 100644 (file)
@@ -1,6 +1,7 @@
-from fastapi import FastAPI
 from pydantic import BaseModel
 
+from fastapi import FastAPI
+
 app = FastAPI()
 
 
similarity index 99%
rename from docs/tutorial/src/body-nested-models/tutorial002.py
rename to docs/tutorial/src/body_nested_models/tutorial002.py
index 8f769279b1fc49601e8581a18cc13a68bc71e120..fba2b55e55e9330781264534d979878fed8d77f3 100644 (file)
@@ -1,8 +1,9 @@
 from typing import List
 
-from fastapi import FastAPI
 from pydantic import BaseModel
 
+from fastapi import FastAPI
+
 app = FastAPI()
 
 
similarity index 99%
rename from docs/tutorial/src/body-nested-models/tutorial003.py
rename to docs/tutorial/src/body_nested_models/tutorial003.py
index bb539b12722a03855bd207c8d428bd7a85df93d2..412b5446db8247286280c454ed1ca7434528db43 100644 (file)
@@ -1,8 +1,9 @@
 from typing import Set
 
-from fastapi import FastAPI
 from pydantic import BaseModel
 
+from fastapi import FastAPI
+
 app = FastAPI()
 
 
similarity index 99%
rename from docs/tutorial/src/body-nested-models/tutorial004.py
rename to docs/tutorial/src/body_nested_models/tutorial004.py
index 257928ef39b3783388ea591f6c93b1d12521ec06..5973cc2c28e097ce41d3d2af9f605ae1ee351624 100644 (file)
@@ -1,8 +1,9 @@
 from typing import Set
 
-from fastapi import FastAPI
 from pydantic import BaseModel
 
+from fastapi import FastAPI
+
 app = FastAPI()
 
 
similarity index 99%
rename from docs/tutorial/src/body-nested-models/tutorial005.py
rename to docs/tutorial/src/body_nested_models/tutorial005.py
index f5f19b39046f4b32a0d10ecd09ffab858c96bbbb..dca4c9816d2bc2e643eccb16afaa771cca3971be 100644 (file)
@@ -1,9 +1,10 @@
 from typing import Set
 
-from fastapi import FastAPI
 from pydantic import BaseModel
 from pydantic.types import UrlStr
 
+from fastapi import FastAPI
+
 app = FastAPI()
 
 
similarity index 99%
rename from docs/tutorial/src/body-nested-models/tutorial006.py
rename to docs/tutorial/src/body_nested_models/tutorial006.py
index 09d8be768ada6fcf7e7d89b7581edb478a7d5ca5..96071c319a050e0e1d16ce995cc1249d71fc65ab 100644 (file)
@@ -1,9 +1,10 @@
 from typing import List, Set
 
-from fastapi import FastAPI
 from pydantic import BaseModel
 from pydantic.types import UrlStr
 
+from fastapi import FastAPI
+
 app = FastAPI()
 
 
similarity index 99%
rename from docs/tutorial/src/body-nested-models/tutorial007.py
rename to docs/tutorial/src/body_nested_models/tutorial007.py
index cda802d3e35ddf1d265d7a7187672e9af5c61062..d3f915f0cd5583d1c1bb64841f94fb55532ae74e 100644 (file)
@@ -1,9 +1,10 @@
 from typing import List, Set
 
-from fastapi import FastAPI
 from pydantic import BaseModel
 from pydantic.types import UrlStr
 
+from fastapi import FastAPI
+
 app = FastAPI()
 
 
similarity index 99%
rename from docs/tutorial/src/body-nested-models/tutorial008.py
rename to docs/tutorial/src/body_nested_models/tutorial008.py
index 34b868563bd904e5dc906849748389576cf69855..0f6a6e0bac32b89824a41017cfb93c9cc6dfb62d 100644 (file)
@@ -1,9 +1,10 @@
 from typing import List
 
-from fastapi import FastAPI
 from pydantic import BaseModel
 from pydantic.types import UrlStr
 
+from fastapi import FastAPI
+
 app = FastAPI()
 
 
similarity index 99%
rename from docs/tutorial/src/body-schema/tutorial001.py
rename to docs/tutorial/src/body_schema/tutorial001.py
index 6c8b101ba220a5fca81d045aacc3bdcdcce4411d..de9e82be770557cb0bf246d9c9f3a5386e0b23e3 100644 (file)
@@ -1,6 +1,7 @@
-from fastapi import Body, FastAPI
 from pydantic import BaseModel, Schema
 
+from fastapi import Body, FastAPI
+
 app = FastAPI()
 
 
similarity index 99%
rename from docs/tutorial/src/body-schema/tutorial002.py
rename to docs/tutorial/src/body_schema/tutorial002.py
index 1165fd7a05642f8d59353787bc0afb2e0539f07e..3f83fb380711fbf6562637d6e8bfa5f0cc3a2dd8 100644 (file)
@@ -1,6 +1,7 @@
-from fastapi import Body, FastAPI
 from pydantic import BaseModel
 
+from fastapi import Body, FastAPI
+
 app = FastAPI()
 
 
similarity index 99%
rename from docs/tutorial/src/custom-response/tutorial001.py
rename to docs/tutorial/src/custom_response/tutorial001.py
index bba3f342d6688e5206ad4620b8f9658e336863b5..1e4b0491f8773638fc07cb97de6729dc4ab96746 100644 (file)
@@ -1,6 +1,7 @@
-from fastapi import FastAPI
 from starlette.responses import UJSONResponse
 
+from fastapi import FastAPI
+
 app = FastAPI()
 
 
similarity index 99%
rename from docs/tutorial/src/custom-response/tutorial002.py
rename to docs/tutorial/src/custom_response/tutorial002.py
index 214e6426365b3b4e0fc23b5267eee673028c17f0..a9294d96eca173a48d192054c0828c7304c493f0 100644 (file)
@@ -1,6 +1,7 @@
-from fastapi import FastAPI
 from starlette.responses import HTMLResponse
 
+from fastapi import FastAPI
+
 app = FastAPI()
 
 
similarity index 99%
rename from docs/tutorial/src/custom-response/tutorial003.py
rename to docs/tutorial/src/custom_response/tutorial003.py
index ba0819cecd5388ed4c84bec0184fbbab93d7d04f..0186d15e945099a385fadc420b38794dea51fafc 100644 (file)
@@ -1,6 +1,7 @@
-from fastapi import FastAPI
 from starlette.responses import HTMLResponse
 
+from fastapi import FastAPI
+
 app = FastAPI()
 
 
similarity index 99%
rename from docs/tutorial/src/custom-response/tutorial004.py
rename to docs/tutorial/src/custom_response/tutorial004.py
index b19783c05b2a505dc9c6dcf2bc42f4bbbafc0ed9..ace5b900ca4a47b992ce6dbc592fb8b1534bbcd4 100644 (file)
@@ -1,6 +1,7 @@
-from fastapi import FastAPI
 from starlette.responses import HTMLResponse
 
+from fastapi import FastAPI
+
 app = FastAPI()
 
 
index 82a51634eb1e38a719fd16688921d1f374049497..8870a646dc88bd6333b3ad488284eb72b0e52472 100644 (file)
@@ -1,6 +1,7 @@
-from fastapi import Depends, FastAPI
 from pydantic import BaseModel
 
+from fastapi import Depends, FastAPI
+
 app = FastAPI()
 
 
index e015f958528a93423739c73c0cb31dd1788bc393..412445b3c4b9a685ad8b9b95490ad7698e0448df 100644 (file)
@@ -1,8 +1,9 @@
 from typing import List
 
-from fastapi import Cookie, Depends, FastAPI
 from pydantic import BaseModel
 
+from fastapi import Cookie, Depends, FastAPI
+
 app = FastAPI()
 
 
index 3697b170ae23fe9ae242eb9f54d2dacf7fca79e8..7d3fb54a7b6069d781953a1267a19071e58c82a1 100644 (file)
@@ -1,9 +1,10 @@
 from random import choice
 from typing import List
 
-from fastapi import Cookie, Depends, FastAPI
 from pydantic import BaseModel
 
+from fastapi import Cookie, Depends, FastAPI
+
 app = FastAPI()
 
 
similarity index 99%
rename from docs/tutorial/src/extra-models/tutorial001.py
rename to docs/tutorial/src/extra_models/tutorial001.py
index aa8e7dad456b1b7cf7ae944a955f647af6c551a7..2fbea7d0d4c58474e904f94639539db443829668 100644 (file)
@@ -1,7 +1,8 @@
-from fastapi import FastAPI
 from pydantic import BaseModel
 from pydantic.types import EmailStr
 
+from fastapi import FastAPI
+
 app = FastAPI()
 
 
similarity index 99%
rename from docs/tutorial/src/extra-models/tutorial002.py
rename to docs/tutorial/src/extra_models/tutorial002.py
index 605baf91f05e95c5763730b6a5983edd036847cf..68d9e2c637afbe06b903aeaf52159d2807e76d37 100644 (file)
@@ -1,7 +1,8 @@
-from fastapi import FastAPI
 from pydantic import BaseModel
 from pydantic.types import EmailStr
 
+from fastapi import FastAPI
+
 app = FastAPI()
 
 
similarity index 90%
rename from docs/tutorial/src/nosql-databases/tutorial001.py
rename to docs/tutorial/src/nosql_databases/tutorial001.py
index 9bcd139be196d75ce047ca1aa40e1b99520dd79c..ccb0cacf63f3e774d5184c30feec8bf4104bc4a4 100644 (file)
@@ -1,17 +1,19 @@
 from typing import Optional
 
-from fastapi import FastAPI
 from pydantic import BaseModel
 
 from couchbase import LOCKMODE_WAIT
 from couchbase.bucket import Bucket
 from couchbase.cluster import Cluster, PasswordAuthenticator
+from fastapi import FastAPI
 
 USERPROFILE_DOC_TYPE = "userprofile"
 
 
 def get_bucket():
-    cluster = Cluster("couchbase://couchbasehost:8091?fetch_mutation_tokens=1&operation_timeout=30&n1ql_timeout=300")
+    cluster = Cluster(
+        "couchbase://couchbasehost:8091?fetch_mutation_tokens=1&operation_timeout=30&n1ql_timeout=300"
+    )
     authenticator = PasswordAuthenticator("username", "password")
     cluster.authenticate(authenticator)
     bucket: Bucket = cluster.open_bucket("bucket_name", lockmode=LOCKMODE_WAIT)
similarity index 99%
rename from docs/tutorial/src/path-operation-configuration/tutorial001.py
rename to docs/tutorial/src/path_operation_configuration/tutorial001.py
index b486018672ada8c2c170f662c452bc7c23e64e0b..39142fb6203140860a531249f6701a5077c72992 100644 (file)
@@ -1,9 +1,10 @@
 from typing import Set
 
-from fastapi import FastAPI
 from pydantic import BaseModel
 from starlette.status import HTTP_201_CREATED
 
+from fastapi import FastAPI
+
 app = FastAPI()
 
 
similarity index 99%
rename from docs/tutorial/src/path-operation-configuration/tutorial002.py
rename to docs/tutorial/src/path_operation_configuration/tutorial002.py
index b5d0f12ca64ae8b67ce4a2c6863ece6b6cca1f78..d0a31c2d2bde2be8edb16dbdc44ea88ce6da1454 100644 (file)
@@ -1,8 +1,9 @@
 from typing import Set
 
-from fastapi import FastAPI
 from pydantic import BaseModel
 
+from fastapi import FastAPI
+
 app = FastAPI()
 
 
similarity index 99%
rename from docs/tutorial/src/path-operation-configuration/tutorial003.py
rename to docs/tutorial/src/path_operation_configuration/tutorial003.py
index 106607fd2d3ace29c56fb0f493d90806684b62ff..36217b039ad1b52b6aef766a2f3e33ad9537e068 100644 (file)
@@ -1,8 +1,9 @@
 from typing import Set
 
-from fastapi import FastAPI
 from pydantic import BaseModel
 
+from fastapi import FastAPI
+
 app = FastAPI()
 
 
similarity index 99%
rename from docs/tutorial/src/path-operation-configuration/tutorial004.py
rename to docs/tutorial/src/path_operation_configuration/tutorial004.py
index a4151a8cd0422c322d472fd11c5b5b85945427aa..d08a7722de8bc76a609669620cf4b067b5156e56 100644 (file)
@@ -1,8 +1,9 @@
 from typing import Set
 
-from fastapi import FastAPI
 from pydantic import BaseModel
 
+from fastapi import FastAPI
+
 app = FastAPI()
 
 
similarity index 99%
rename from docs/tutorial/src/path-operation-configuration/tutorial005.py
rename to docs/tutorial/src/path_operation_configuration/tutorial005.py
index f710e6c669d322132e2dcb4843a6941ea5ad37e6..a563bd81e0b6c2d4fc2f318acdec5aafc4ae82da 100644 (file)
@@ -1,8 +1,9 @@
 from typing import Set
 
-from fastapi import FastAPI
 from pydantic import BaseModel
 
+from fastapi import FastAPI
+
 app = FastAPI()
 
 
similarity index 99%
rename from docs/tutorial/src/response-model/tutorial001.py
rename to docs/tutorial/src/response_model/tutorial001.py
index 86dadcbda4c67ee8f35ac981e1516ee27598442e..19792ad84ee787730affc1b4f2f7352e75a1c16e 100644 (file)
@@ -1,8 +1,9 @@
 from typing import Set
 
-from fastapi import FastAPI
 from pydantic import BaseModel
 
+from fastapi import FastAPI
+
 app = FastAPI()
 
 
similarity index 99%
rename from docs/tutorial/src/response-model/tutorial002.py
rename to docs/tutorial/src/response_model/tutorial002.py
index 3fb475b9d9cc74862f2f14756156497aa6fd5662..5fdf7c97bb425fecbaaf136b0d3b1a712d64ea5a 100644 (file)
@@ -1,7 +1,8 @@
-from fastapi import FastAPI
 from pydantic import BaseModel
 from pydantic.types import EmailStr
 
+from fastapi import FastAPI
+
 app = FastAPI()
 
 
similarity index 99%
rename from docs/tutorial/src/response-model/tutorial003.py
rename to docs/tutorial/src/response_model/tutorial003.py
index c8ea361d8d45bc197dbbce97acb6a9f32eb52f5d..745f07839a1df6920152b4815c2e7d26a7f7f9e8 100644 (file)
@@ -1,7 +1,8 @@
-from fastapi import FastAPI
 from pydantic import BaseModel
 from pydantic.types import EmailStr
 
+from fastapi import FastAPI
+
 app = FastAPI()
 
 
index cfce061596c9daf4311f0b4cedc2dc3828813e7c..5b8de3e1402c054ef30b56364273e6713d1457db 100644 (file)
@@ -1,8 +1,9 @@
 from typing import Optional
 
+from pydantic import BaseModel
+
 from fastapi import Depends, FastAPI, Security
 from fastapi.security import OAuth2PasswordBearer
-from pydantic import BaseModel
 
 app = FastAPI()
 
index 4f3d2b82d327bdf9bcee6bf98521bf1a59c5c207..17f2781939af907d29d0b338f5a5c4e617f7585a 100644 (file)
@@ -1,10 +1,11 @@
 from typing import Optional
 
-from fastapi import Depends, FastAPI, Security
-from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm
 from pydantic import BaseModel
 from starlette.exceptions import HTTPException
 
+from fastapi import Depends, FastAPI, Security
+from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm
+
 fake_users_db = {
     "johndoe": {
         "username": "johndoe",
index 122d4a10104eacd9555c8b0b4b82ab3d79a573f3..31ef7461bee0531172030e0cd199d513f08a6be3 100644 (file)
@@ -2,14 +2,15 @@ from datetime import datetime, timedelta
 from typing import Optional
 
 import jwt
-from fastapi import Depends, FastAPI, Security
-from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm
 from jwt import PyJWTError
 from passlib.context import CryptContext
 from pydantic import BaseModel
 from starlette.exceptions import HTTPException
 from starlette.status import HTTP_403_FORBIDDEN
 
+from fastapi import Depends, FastAPI, Security
+from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm
+
 # to get a string like this run:
 # openssl rand -hex 32
 SECRET_KEY = "09d25e094faa6ca2556c818166b7a9563b93f7099f6f0f4caa6cf63b88e8d3e7"
index 2d5a0b8627e46feb03e9cbcd9d2bf6ecc26f38c5..a070b2b7fc9aa229e5bcc508b984b3fa36bd807e 100644 (file)
@@ -1,8 +1,5 @@
 from typing import Any, Callable, Dict, List, Optional, Type
 
-from fastapi import routing
-from fastapi.openapi.docs import get_redoc_html, get_swagger_ui_html
-from fastapi.openapi.utils import get_openapi
 from pydantic import BaseModel
 from starlette.applications import Starlette
 from starlette.exceptions import ExceptionMiddleware, HTTPException
@@ -11,6 +8,10 @@ from starlette.middleware.lifespan import LifespanMiddleware
 from starlette.requests import Request
 from starlette.responses import JSONResponse, Response
 
+from fastapi import routing
+from fastapi.openapi.docs import get_redoc_html, get_swagger_ui_html
+from fastapi.openapi.utils import get_openapi
+
 
 async def http_exception(request: Request, exc: HTTPException) -> JSONResponse:
     return JSONResponse({"detail": exc.detail}, status_code=exc.status_code)
index 748fe4a9eb990ae2657f3097cd36a876633607fc..eb4d1877b7200261e95db2dabdb92a4a54804262 100644 (file)
@@ -1,8 +1,9 @@
 from typing import Callable, List, Sequence
 
-from fastapi.security.base import SecurityBase
 from pydantic.fields import Field
 
+from fastapi.security.base import SecurityBase
+
 param_supported_types = (str, int, float, bool)
 
 
index e3b13da71fbf7ec9032f8b36fcac2b22accec4aa..31fabd25b9178db68794ab83e215f246f9308ffd 100644 (file)
@@ -7,7 +7,7 @@ from pydantic.types import UrlStr
 
 try:
     from pydantic.types import EmailStr  # type: ignore
-except ImportError:
+except ImportError:  # pragma: no cover
     logging.warning(
         "email-validator not installed, email fields will be treated as str.\n"
         + "To install, run: pip install email-validator"
index 0f0a03624952273c45d7762fe893bdbf04c5c00e..698f1589f55b7f5104a6af0504dc97f150a39a2f 100644 (file)
@@ -1,5 +1,12 @@
 from typing import Any, Dict, List, Optional, Sequence, Tuple, Type
 
+from pydantic.fields import Field
+from pydantic.schema import Schema, 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
+
 from fastapi import routing
 from fastapi.dependencies.models import Dependant
 from fastapi.dependencies.utils import get_flat_dependant
@@ -8,12 +15,6 @@ from fastapi.openapi.constants import METHODS_WITH_BODY, REF_PREFIX
 from fastapi.openapi.models import OpenAPI
 from fastapi.params import Body, Param
 from fastapi.utils import get_flat_models_from_routes, get_model_definitions
-from pydantic.fields import Field
-from pydantic.schema import Schema, 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
 
 validation_error_definition = {
     "title": "ValidationError",
index 12eba37ee7d2462a1a001f764aba54172f02a133..c4b045b715bcc4f5471b93dd72a41581cb8d73b8 100644 (file)
@@ -1,6 +1,7 @@
+from starlette.requests import Request
+
 from fastapi.openapi.models import APIKey, APIKeyIn
 from fastapi.security.base import SecurityBase
-from starlette.requests import Request
 
 
 class APIKeyBase(SecurityBase):
index b1cba1921fd24b013ae815b107926ea096fea9d3..480a1ae546f5e883e5abab9282f7222728e8fbf8 100644 (file)
@@ -1,9 +1,10 @@
+from starlette.requests import Request
+
 from fastapi.openapi.models import (
     HTTPBase as HTTPBaseModel,
     HTTPBearer as HTTPBearerModel,
 )
 from fastapi.security.base import SecurityBase
-from starlette.requests import Request
 
 
 class HTTPBase(SecurityBase):
index d915af184a6c5b5ccaefa2834c90150c25d6dd10..f190d805a77704015fd631b4dddd532a07b712a9 100644 (file)
@@ -1,12 +1,13 @@
 from typing import List, Optional
 
-from fastapi.openapi.models import OAuth2 as OAuth2Model, OAuthFlows as OAuthFlowsModel
-from fastapi.security.base import SecurityBase
 from pydantic import BaseModel, Schema
 from starlette.exceptions import HTTPException
 from starlette.requests import Request
 from starlette.status import HTTP_403_FORBIDDEN
 
+from fastapi.openapi.models import OAuth2 as OAuth2Model, OAuthFlows as OAuthFlowsModel
+from fastapi.security.base import SecurityBase
+
 
 class OAuth2PasswordRequestData(BaseModel):
     grant_type: str = "password"
index 7d73ed81f388ca620af2c768d7f6fc85281e299c..b6c0a32dc412f42a9abb78366248f6e09795d373 100644 (file)
@@ -1,6 +1,7 @@
+from starlette.requests import Request
+
 from fastapi.openapi.models import OpenIdConnect as OpenIdConnectModel
 from fastapi.security.base import SecurityBase
-from starlette.requests import Request
 
 
 class OpenIdConnect(SecurityBase):
index f3b4df82e8887ba92893f859b64c180214cd48d6..81ca910cfaa5f3c319cfbcaa64780b039057f391 100644 (file)
@@ -1,13 +1,14 @@
 import re
 from typing import Any, Dict, List, Sequence, Set, Type
 
-from fastapi import routing
-from fastapi.openapi.constants import REF_PREFIX
 from pydantic import BaseModel
 from pydantic.fields import Field
 from pydantic.schema import get_flat_models_from_fields, model_process_schema
 from starlette.routing import BaseRoute
 
+from fastapi import routing
+from fastapi.openapi.constants import REF_PREFIX
+
 
 def get_flat_models_from_routes(
     routes: Sequence[Type[BaseRoute]]
index 65da38d1901296e94094fbf0cbcb7c66c80cc711..a9a3406441497c10491ba233a6f14e6e44739afd 100644 (file)
@@ -8,7 +8,7 @@ export PYTHON_VERSION=`python -c "$VERSION_SCRIPT"`
 
 
 # PYTHONPATH=. pytest --cov=fastapi --cov=tests --cov-fail-under=100 --cov-report=term-missing ${@} --cov-report=html
-PYTHONPATH=. pytest --cov=fastapi --cov=tests --cov-report=term-missing ${@} --cov-report=html
+PYTHONPATH=.:./docs/tutorial/src pytest --cov=fastapi --cov=tests --cov=docs/tutorial/src --cov-report=term-missing ${@} --cov-report=html
 mypy fastapi --disallow-untyped-defs
 if [ "${PYTHON_VERSION}" = '3.7' ]; then
     echo "Skipping 'black' on 3.7. See issue https://github.com/ambv/black/issues/494"
index 468f4c764fc0e53671d42cbdee4630f206207ff4..004fc3630e8026d223b4b5529a16f3b91d37f3da 100644 (file)
-from fastapi import (
-    Body,
-    Cookie,
-    Depends,
-    FastAPI,
-    File,
-    Form,
-    Header,
-    Path,
-    Query,
-    Security,
-)
-from fastapi.security import (
-    HTTPBasic,
-    OAuth2,
-    OAuth2PasswordBearer,
-    OAuth2PasswordRequestForm,
-)
-from pydantic import BaseModel
-from starlette.responses import HTMLResponse, JSONResponse, PlainTextResponse
-from starlette.status import HTTP_202_ACCEPTED
-
-from .endpoints.a import router as router_a
-from .endpoints.b import router as router_b
+from fastapi import FastAPI
 
 app = FastAPI()
 
 
-app.include_router(router_a)
-app.include_router(router_b, prefix="/b")
-
-
-@app.get("/text")
-def get_text():
-    return "Hello World"
-
-
-@app.get("/path/{item_id}")
-def get_id(item_id):
-    return item_id
-
-
-@app.get("/path/str/{item_id}")
-def get_str_id(item_id: str):
-    return item_id
-
-
-@app.get("/path/int/{item_id}")
-def get_int_id(item_id: int):
-    return item_id
-
-
-@app.get("/path/float/{item_id}")
-def get_float_id(item_id: float):
-    return item_id
-
-
-@app.get("/path/bool/{item_id}")
-def get_bool_id(item_id: bool):
-    return item_id
-
-
-@app.get("/path/param/{item_id}")
-def get_path_param_id(item_id: str = Path(None)):
-    return item_id
-
-
-@app.get("/path/param-required/{item_id}")
-def get_path_param_required_id(item_id: str = Path(...)):
-    return item_id
-
-
-@app.get("/path/param-minlength/{item_id}")
-def get_path_param_min_length(item_id: str = Path(..., min_length=3)):
-    return item_id
-
-
-@app.get("/path/param-maxlength/{item_id}")
-def get_path_param_max_length(item_id: str = Path(..., max_length=3)):
-    return item_id
-
-
-@app.get("/path/param-min_maxlength/{item_id}")
-def get_path_param_min_max_length(item_id: str = Path(..., max_length=3, min_length=2)):
-    return item_id
-
-
-@app.get("/path/param-gt/{item_id}")
-def get_path_param_gt(item_id: float = Path(..., gt=3)):
-    return item_id
-
-
-@app.get("/path/param-gt0/{item_id}")
-def get_path_param_gt0(item_id: float = Path(..., gt=0)):
-    return item_id
-
-
-@app.get("/path/param-ge/{item_id}")
-def get_path_param_ge(item_id: float = Path(..., ge=3)):
-    return item_id
-
-
-@app.get("/path/param-lt/{item_id}")
-def get_path_param_lt(item_id: float = Path(..., lt=3)):
-    return item_id
-
-
-@app.get("/path/param-lt0/{item_id}")
-def get_path_param_lt0(item_id: float = Path(..., lt=0)):
-    return item_id
-
-
-@app.get("/path/param-le/{item_id}")
-def get_path_param_le(item_id: float = Path(..., le=3)):
-    return item_id
-
-
-@app.get("/path/param-lt-gt/{item_id}")
-def get_path_param_lt_gt(item_id: float = Path(..., lt=3, gt=1)):
-    return item_id
-
-
-@app.get("/path/param-le-ge/{item_id}")
-def get_path_param_le_ge(item_id: float = Path(..., le=3, ge=1)):
-    return item_id
-
-
-@app.get("/path/param-lt-int/{item_id}")
-def get_path_param_lt_int(item_id: int = Path(..., lt=3)):
-    return item_id
-
-
-@app.get("/path/param-gt-int/{item_id}")
-def get_path_param_gt_int(item_id: int = Path(..., gt=3)):
-    return item_id
-
-
-@app.get("/path/param-le-int/{item_id}")
-def get_path_param_le_int(item_id: int = Path(..., le=3)):
-    return item_id
-
-
-@app.get("/path/param-ge-int/{item_id}")
-def get_path_param_ge_int(item_id: int = Path(..., ge=3)):
-    return item_id
-
-
-@app.get("/path/param-lt-gt-int/{item_id}")
-def get_path_param_lt_gt_int(item_id: int = Path(..., lt=3, gt=1)):
-    return item_id
-
-
-@app.get("/path/param-le-ge-int/{item_id}")
-def get_path_param_le_ge_int(item_id: int = Path(..., le=3, ge=1)):
-    return item_id
-
-
-@app.get("/query")
-def get_query(query):
-    if query is None:
-        return "foo bar"
-    return f"foo bar {query}"
-
-
-@app.get("/query/optional")
-def get_query_optional(query=None):
-    if query is None:
-        return "foo bar"
-    return f"foo bar {query}"
-
-
-@app.get("/query/int")
-def get_query_type(query: int):
-    if query is None:
-        return "foo bar"
-    return f"foo bar {query}"
-
-
-@app.get("/query/int/optional")
-def get_query_type_optional(query: int = None):
-    if query is None:
-        return "foo bar"
-    return f"foo bar {query}"
-
-
-@app.get("/query/int/default")
-def get_query_type_optional(query: int = 10):
-    return f"foo bar {query}"
-
-
-@app.get("/query/param")
-def get_query_param(query=Query(None)):
-    if query is None:
-        return "foo bar"
-    return f"foo bar {query}"
-
-
-@app.get("/query/param-required")
-def get_query_param_required(query=Query(...)):
-    if query is None:
-        return "foo bar"
-    return f"foo bar {query}"
-
-
-@app.get("/query/param-required/int")
-def get_query_param_required_type(query: int = Query(...)):
-    if query is None:
-        return "foo bar"
-    return f"foo bar {query}"
-
-
-@app.get("/cookie")
-def get_cookie(coo=Cookie(None)):
-    return coo
-
-
-@app.get("/header")
-def get_header(head_name=Header(None)):
-    return head_name
-
-
-@app.get("/header_under")
-def get_header(head_name=Header(None, convert_underscores=False)):
-    return head_name
-
-
-@app.get("/security")
-def get_security(sec=Security(HTTPBasic())):
-    return sec
-
-
-reusable_oauth2 = OAuth2(
-    flows={
-        "password": {
-            "tokenUrl": "/token",
-            "scopes": {"read:user": "Read a User", "write:user": "Create a user"},
-        }
-    }
-)
-
-
-@app.get("/security/oauth2")
-def get_security_oauth2(sec=Security(reusable_oauth2, scopes=["read:user"])):
-    return sec
-
-
-reusable_oauth2b = OAuth2PasswordBearer(tokenUrl="/token")
-
-
-class User(BaseModel):
-    username: str
-
-
-def get_current_user(oauth_header: str = Security(reusable_oauth2b)):
-    user = User(username=oauth_header)
-    return user
-
-
-@app.get("/security/oauth2b")
-def read_current_user(current_user: User = Depends(get_current_user)):
-    return current_user
-
-
-@app.post("/token")
-def post_token(request_data: OAuth2PasswordRequestForm = Form(...)):
-    data = request_data.parse()
-    access_token = data.username + ":" + data.password
-    return {"access_token": access_token}
-
-
-class Item(BaseModel):
-    name: str
-    price: float
-    is_offer: bool
-
-
-@app.put("/items/{item_id}")
-def put_item(item_id: str, item: Item):
-    return item
-
-
-@app.post("/items/")
-def post_item(item: Item):
-    return item
-
-
-@app.post("/items-all-params/{item_id}")
-def post_items_all_params(
-    item_id: str = Path(...),
-    body: Item = Body(...),
-    query_a: int = Query(None),
-    query_b=Query(None),
-    coo: str = Cookie(None),
-    x_head: int = Header(None),
-    x_under: str = Header(None, convert_underscores=False),
-):
-    return {
-        "item_id": item_id,
-        "body": body,
-        "query_a": query_a,
-        "query_b": query_b,
-        "coo": coo,
-        "x_head": x_head,
-        "x_under": x_under,
-    }
-
-
-@app.post("/items-all-params-defaults/{item_id}")
-def post_items_all_params_default(
-    item_id: str,
-    body_item_a: Item,
-    body_item_b: Item,
-    query_a: int,
-    query_b: int,
-    coo: str = Cookie(None),
-    x_head: int = Header(None),
-    x_under: str = Header(None, convert_underscores=False),
-):
-    return {
-        "item_id": item_id,
-        "body_item_a": body_item_a,
-        "body_item_b": body_item_b,
-        "query_a": query_a,
-        "query_b": query_b,
-        "coo": coo,
-        "x_head": x_head,
-        "x_under": x_under,
-    }
-
-
-@app.delete("/items/{item_id}")
-def delete_item(item_id: str):
-    return item_id
-
-
-@app.options("/options/")
-def options():
-    return JSONResponse(headers={"x-fastapi": "fast"})
-
-
-@app.head("/head/")
-def head():
-    return {"not sent": "nope"}
-
-
-@app.patch("/patch/{user_id}")
-def patch(user_id: str, increment: float):
-    return {"user_id": user_id, "total": 5 + increment}
-
-
-@app.trace("/trace/")
-def trace():
-    return PlainTextResponse(media_type="message/http")
-
-
-@app.get("/model", response_model=Item, status_code=HTTP_202_ACCEPTED)
-def model():
-    return {"name": "Foo", "price": "5.0", "password": "not sent"}
-
-
-@app.get(
-    "/metadata",
-    tags=["tag1", "tag2"],
-    summary="The summary",
-    description="The description",
-    response_description="Response description",
-    deprecated=True,
-    operation_id="a_very_long_and_strange_operation_id",
-)
-def get_meta():
-    return "Foo"
-
-
-@app.get("/html", content_type=HTMLResponse)
-def get_html():
-    return """
-    <html>
-    <body>
-    <h1>
-    Some text inside
-    </h1>
-    </body>
-    </html>
-    """
-
-
-class FakeDB:
-    def __init__(self):
-        self.data = {
-            "johndoe": {
-                "username": "johndoe",
-                "password": "shouldbehashed",
-                "fist_name": "John",
-                "last_name": "Doe",
-            }
-        }
-
-
-class DBConnectionManager:
-    def __init__(self):
-        self.db = FakeDB()
-
-    def __call__(self):
-        return self.db
-
-
-connection_manager = DBConnectionManager()
-
-
-class TokenUserData(BaseModel):
-    username: str
-    password: str
-
-
-class UserInDB(BaseModel):
-    username: str
-    password: str
-    fist_name: str
-    last_name: str
-
-
-def require_token(
-    token: str = Security(reusable_oauth2, scopes=["read:user", "write:user"])
-):
-    raw_token = token.replace("Bearer ", "")
-    # Never do this plaintext password usage in production
-    username, password = raw_token.split(":")
-    return TokenUserData(username=username, password=password)
-
-
-def require_user(
-    db: FakeDB = Depends(connection_manager),
-    user_data: TokenUserData = Depends(require_token),
-):
-    return db.data[user_data.username]
+@app.api_route("/api_route")
+def non_operation():
+    return {"message": "Hello World"}
 
 
-class UserOut(BaseModel):
-    username: str
-    fist_name: str
-    last_name: str
+def non_decorated_route():
+    return {"message": "Hello World"}
 
 
-@app.get("/dependency", response_model=UserOut)
-def get_dependency(user: UserInDB = Depends(require_user)):
-    return user
+app.add_api_route("/non_decorated_route", non_decorated_route)
diff --git a/tests/main_old.py b/tests/main_old.py
new file mode 100644 (file)
index 0000000..1329ffc
--- /dev/null
@@ -0,0 +1,447 @@
+from pydantic import BaseModel
+from starlette.responses import HTMLResponse, JSONResponse, PlainTextResponse
+from starlette.status import HTTP_202_ACCEPTED
+
+from fastapi import (
+    Body,
+    Cookie,
+    Depends,
+    FastAPI,
+    File,
+    Form,
+    Header,
+    Path,
+    Query,
+    Security,
+)
+from fastapi.security import (
+    HTTPBasic,
+    OAuth2,
+    OAuth2PasswordBearer,
+    OAuth2PasswordRequestForm,
+)
+
+from .endpoints.a import router as router_a
+from .endpoints.b import router as router_b
+
+app = FastAPI()
+
+
+app.include_router(router_a)
+app.include_router(router_b, prefix="/b")
+
+
+@app.get("/text")
+def get_text():
+    return "Hello World"
+
+
+@app.get("/path/{item_id}")
+def get_id(item_id):
+    return item_id
+
+
+@app.get("/path/str/{item_id}")
+def get_str_id(item_id: str):
+    return item_id
+
+
+@app.get("/path/int/{item_id}")
+def get_int_id(item_id: int):
+    return item_id
+
+
+@app.get("/path/float/{item_id}")
+def get_float_id(item_id: float):
+    return item_id
+
+
+@app.get("/path/bool/{item_id}")
+def get_bool_id(item_id: bool):
+    return item_id
+
+
+@app.get("/path/param/{item_id}")
+def get_path_param_id(item_id: str = Path(None)):
+    return item_id
+
+
+@app.get("/path/param-required/{item_id}")
+def get_path_param_required_id(item_id: str = Path(...)):
+    return item_id
+
+
+@app.get("/path/param-minlength/{item_id}")
+def get_path_param_min_length(item_id: str = Path(..., min_length=3)):
+    return item_id
+
+
+@app.get("/path/param-maxlength/{item_id}")
+def get_path_param_max_length(item_id: str = Path(..., max_length=3)):
+    return item_id
+
+
+@app.get("/path/param-min_maxlength/{item_id}")
+def get_path_param_min_max_length(item_id: str = Path(..., max_length=3, min_length=2)):
+    return item_id
+
+
+@app.get("/path/param-gt/{item_id}")
+def get_path_param_gt(item_id: float = Path(..., gt=3)):
+    return item_id
+
+
+@app.get("/path/param-gt0/{item_id}")
+def get_path_param_gt0(item_id: float = Path(..., gt=0)):
+    return item_id
+
+
+@app.get("/path/param-ge/{item_id}")
+def get_path_param_ge(item_id: float = Path(..., ge=3)):
+    return item_id
+
+
+@app.get("/path/param-lt/{item_id}")
+def get_path_param_lt(item_id: float = Path(..., lt=3)):
+    return item_id
+
+
+@app.get("/path/param-lt0/{item_id}")
+def get_path_param_lt0(item_id: float = Path(..., lt=0)):
+    return item_id
+
+
+@app.get("/path/param-le/{item_id}")
+def get_path_param_le(item_id: float = Path(..., le=3)):
+    return item_id
+
+
+@app.get("/path/param-lt-gt/{item_id}")
+def get_path_param_lt_gt(item_id: float = Path(..., lt=3, gt=1)):
+    return item_id
+
+
+@app.get("/path/param-le-ge/{item_id}")
+def get_path_param_le_ge(item_id: float = Path(..., le=3, ge=1)):
+    return item_id
+
+
+@app.get("/path/param-lt-int/{item_id}")
+def get_path_param_lt_int(item_id: int = Path(..., lt=3)):
+    return item_id
+
+
+@app.get("/path/param-gt-int/{item_id}")
+def get_path_param_gt_int(item_id: int = Path(..., gt=3)):
+    return item_id
+
+
+@app.get("/path/param-le-int/{item_id}")
+def get_path_param_le_int(item_id: int = Path(..., le=3)):
+    return item_id
+
+
+@app.get("/path/param-ge-int/{item_id}")
+def get_path_param_ge_int(item_id: int = Path(..., ge=3)):
+    return item_id
+
+
+@app.get("/path/param-lt-gt-int/{item_id}")
+def get_path_param_lt_gt_int(item_id: int = Path(..., lt=3, gt=1)):
+    return item_id
+
+
+@app.get("/path/param-le-ge-int/{item_id}")
+def get_path_param_le_ge_int(item_id: int = Path(..., le=3, ge=1)):
+    return item_id
+
+
+@app.get("/query")
+def get_query(query):
+    if query is None:
+        return "foo bar"
+    return f"foo bar {query}"
+
+
+@app.get("/query/optional")
+def get_query_optional(query=None):
+    if query is None:
+        return "foo bar"
+    return f"foo bar {query}"
+
+
+@app.get("/query/int")
+def get_query_type(query: int):
+    if query is None:
+        return "foo bar"
+    return f"foo bar {query}"
+
+
+@app.get("/query/int/optional")
+def get_query_type_optional(query: int = None):
+    if query is None:
+        return "foo bar"
+    return f"foo bar {query}"
+
+
+@app.get("/query/int/default")
+def get_query_type_optional(query: int = 10):
+    return f"foo bar {query}"
+
+
+@app.get("/query/param")
+def get_query_param(query=Query(None)):
+    if query is None:
+        return "foo bar"
+    return f"foo bar {query}"
+
+
+@app.get("/query/param-required")
+def get_query_param_required(query=Query(...)):
+    if query is None:
+        return "foo bar"
+    return f"foo bar {query}"
+
+
+@app.get("/query/param-required/int")
+def get_query_param_required_type(query: int = Query(...)):
+    if query is None:
+        return "foo bar"
+    return f"foo bar {query}"
+
+
+@app.get("/cookie")
+def get_cookie(coo=Cookie(None)):
+    return coo
+
+
+@app.get("/header")
+def get_header(head_name=Header(None)):
+    return head_name
+
+
+@app.get("/header_under")
+def get_header(head_name=Header(None, convert_underscores=False)):
+    return head_name
+
+
+@app.get("/security")
+def get_security(sec=Security(HTTPBasic())):
+    return sec
+
+
+reusable_oauth2 = OAuth2(
+    flows={
+        "password": {
+            "tokenUrl": "/token",
+            "scopes": {"read:user": "Read a User", "write:user": "Create a user"},
+        }
+    }
+)
+
+
+@app.get("/security/oauth2")
+def get_security_oauth2(sec=Security(reusable_oauth2, scopes=["read:user"])):
+    return sec
+
+
+reusable_oauth2b = OAuth2PasswordBearer(tokenUrl="/token")
+
+
+class User(BaseModel):
+    username: str
+
+
+def get_current_user(oauth_header: str = Security(reusable_oauth2b)):
+    user = User(username=oauth_header)
+    return user
+
+
+@app.get("/security/oauth2b")
+def read_current_user(current_user: User = Depends(get_current_user)):
+    return current_user
+
+
+@app.post("/token")
+def post_token(request_data: OAuth2PasswordRequestForm = Form(...)):
+    data = request_data.parse()
+    access_token = data.username + ":" + data.password
+    return {"access_token": access_token}
+
+
+class Item(BaseModel):
+    name: str
+    price: float
+    is_offer: bool
+
+
+@app.put("/items/{item_id}")
+def put_item(item_id: str, item: Item):
+    return item
+
+
+@app.post("/items/")
+def post_item(item: Item):
+    return item
+
+
+@app.post("/items-all-params/{item_id}")
+def post_items_all_params(
+    item_id: str = Path(...),
+    body: Item = Body(...),
+    query_a: int = Query(None),
+    query_b=Query(None),
+    coo: str = Cookie(None),
+    x_head: int = Header(None),
+    x_under: str = Header(None, convert_underscores=False),
+):
+    return {
+        "item_id": item_id,
+        "body": body,
+        "query_a": query_a,
+        "query_b": query_b,
+        "coo": coo,
+        "x_head": x_head,
+        "x_under": x_under,
+    }
+
+
+@app.post("/items-all-params-defaults/{item_id}")
+def post_items_all_params_default(
+    item_id: str,
+    body_item_a: Item,
+    body_item_b: Item,
+    query_a: int,
+    query_b: int,
+    coo: str = Cookie(None),
+    x_head: int = Header(None),
+    x_under: str = Header(None, convert_underscores=False),
+):
+    return {
+        "item_id": item_id,
+        "body_item_a": body_item_a,
+        "body_item_b": body_item_b,
+        "query_a": query_a,
+        "query_b": query_b,
+        "coo": coo,
+        "x_head": x_head,
+        "x_under": x_under,
+    }
+
+
+@app.delete("/items/{item_id}")
+def delete_item(item_id: str):
+    return item_id
+
+
+@app.options("/options/")
+def options():
+    return JSONResponse(headers={"x-fastapi": "fast"})
+
+
+@app.head("/head/")
+def head():
+    return {"not sent": "nope"}
+
+
+@app.patch("/patch/{user_id}")
+def patch(user_id: str, increment: float):
+    return {"user_id": user_id, "total": 5 + increment}
+
+
+@app.trace("/trace/")
+def trace():
+    return PlainTextResponse(media_type="message/http")
+
+
+@app.get("/model", response_model=Item, status_code=HTTP_202_ACCEPTED)
+def model():
+    return {"name": "Foo", "price": "5.0", "password": "not sent"}
+
+
+@app.get(
+    "/metadata",
+    tags=["tag1", "tag2"],
+    summary="The summary",
+    description="The description",
+    response_description="Response description",
+    deprecated=True,
+    operation_id="a_very_long_and_strange_operation_id",
+)
+def get_meta():
+    return "Foo"
+
+
+@app.get("/html", content_type=HTMLResponse)
+def get_html():
+    return """
+    <html>
+    <body>
+    <h1>
+    Some text inside
+    </h1>
+    </body>
+    </html>
+    """
+
+
+class FakeDB:
+    def __init__(self):
+        self.data = {
+            "johndoe": {
+                "username": "johndoe",
+                "password": "shouldbehashed",
+                "fist_name": "John",
+                "last_name": "Doe",
+            }
+        }
+
+
+class DBConnectionManager:
+    def __init__(self):
+        self.db = FakeDB()
+
+    def __call__(self):
+        return self.db
+
+
+connection_manager = DBConnectionManager()
+
+
+class TokenUserData(BaseModel):
+    username: str
+    password: str
+
+
+class UserInDB(BaseModel):
+    username: str
+    password: str
+    fist_name: str
+    last_name: str
+
+
+def require_token(
+    token: str = Security(reusable_oauth2, scopes=["read:user", "write:user"])
+):
+    raw_token = token.replace("Bearer ", "")
+    # Never do this plaintext password usage in production
+    username, password = raw_token.split(":")
+    return TokenUserData(username=username, password=password)
+
+
+def require_user(
+    db: FakeDB = Depends(connection_manager),
+    user_data: TokenUserData = Depends(require_token),
+):
+    return db.data[user_data.username]
+
+
+class UserOut(BaseModel):
+    username: str
+    fist_name: str
+    last_name: str
+
+
+@app.get("/dependency", response_model=UserOut)
+def get_dependency(user: UserInDB = Depends(require_user)):
+    return user
diff --git a/tests/test_application.py b/tests/test_application.py
new file mode 100644 (file)
index 0000000..8b868f4
--- /dev/null
@@ -0,0 +1,63 @@
+import pytest
+from starlette.testclient import TestClient
+
+from .main import app
+
+client = TestClient(app)
+
+openapi_schema = {
+    "openapi": "3.0.2",
+    "info": {"title": "Fast API", "version": "0.1.0"},
+    "paths": {
+        "/api_route": {
+            "get": {
+                "responses": {
+                    "200": {
+                        "description": "Successful Response",
+                        "content": {"application/json": {"schema": {}}},
+                    }
+                },
+                "summary": "Non Operation Get",
+                "operationId": "non_operation_api_route_get",
+            }
+        },
+        "/non_decorated_route": {
+            "get": {
+                "responses": {
+                    "200": {
+                        "description": "Successful Response",
+                        "content": {"application/json": {"schema": {}}},
+                    }
+                },
+                "summary": "Non Decorated Route Get",
+                "operationId": "non_decorated_route_non_decorated_route_get",
+            }
+        },
+    },
+}
+
+
+@pytest.mark.parametrize(
+    "path,expected_status,expected_response",
+    [
+        ("/api_route", 200, {"message": "Hello World"}),
+        ("/nonexistent", 404, {"detail": "Not Found"}),
+        ("/openapi.json", 200, openapi_schema),
+    ],
+)
+def test_get_path(path, expected_status, expected_response):
+    response = client.get(path)
+    assert response.status_code == expected_status
+    assert response.json() == expected_response
+
+
+def test_swagger_ui():
+    response = client.get("/docs")
+    assert response.status_code == 200
+    assert "swagger-ui-dist" in response.text
+
+
+def test_redoc():
+    response = client.get("/redoc")
+    assert response.status_code == 200
+    assert "redoc@next" in response.text
diff --git a/tests/test_tutorial/__init__.py b/tests/test_tutorial/__init__.py
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/tests/test_tutorial/test_bigger_applications/__init__.py b/tests/test_tutorial/test_bigger_applications/__init__.py
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/tests/test_tutorial/test_bigger_applications/test_tutorial003.py b/tests/test_tutorial/test_bigger_applications/test_tutorial003.py
new file mode 100644 (file)
index 0000000..680bc8e
--- /dev/null
@@ -0,0 +1,155 @@
+import pytest
+from starlette.testclient import TestClient
+
+from bigger_applications.app.tutorial003 import app
+
+client = TestClient(app)
+
+openapi_schema = {
+    "openapi": "3.0.2",
+    "info": {"title": "Fast API", "version": "0.1.0"},
+    "paths": {
+        "/users/": {
+            "get": {
+                "responses": {
+                    "200": {
+                        "description": "Successful Response",
+                        "content": {"application/json": {"schema": {}}},
+                    }
+                },
+                "summary": "Read Users Get",
+                "operationId": "read_users_users__get",
+            }
+        },
+        "/users/{username}": {
+            "get": {
+                "responses": {
+                    "200": {
+                        "description": "Successful Response",
+                        "content": {"application/json": {"schema": {}}},
+                    },
+                    "422": {
+                        "description": "Validation Error",
+                        "content": {
+                            "application/json": {
+                                "schema": {
+                                    "$ref": "#/components/schemas/HTTPValidationError"
+                                }
+                            }
+                        },
+                    },
+                },
+                "summary": "Read User Get",
+                "operationId": "read_user_users__username__get",
+                "parameters": [
+                    {
+                        "required": True,
+                        "schema": {"title": "Username", "type": "string"},
+                        "name": "username",
+                        "in": "path",
+                    }
+                ],
+            }
+        },
+        "/users/me": {
+            "get": {
+                "responses": {
+                    "200": {
+                        "description": "Successful Response",
+                        "content": {"application/json": {"schema": {}}},
+                    }
+                },
+                "summary": "Read User Me Get",
+                "operationId": "read_user_me_users_me_get",
+            }
+        },
+        "/items/": {
+            "get": {
+                "responses": {
+                    "200": {
+                        "description": "Successful Response",
+                        "content": {"application/json": {"schema": {}}},
+                    }
+                },
+                "summary": "Read Items Get",
+                "operationId": "read_items_items__get",
+            }
+        },
+        "/items/{item_id}": {
+            "get": {
+                "responses": {
+                    "200": {
+                        "description": "Successful Response",
+                        "content": {"application/json": {"schema": {}}},
+                    },
+                    "422": {
+                        "description": "Validation Error",
+                        "content": {
+                            "application/json": {
+                                "schema": {
+                                    "$ref": "#/components/schemas/HTTPValidationError"
+                                }
+                            }
+                        },
+                    },
+                },
+                "summary": "Read Item Get",
+                "operationId": "read_item_items__item_id__get",
+                "parameters": [
+                    {
+                        "required": True,
+                        "schema": {"title": "Item_Id", "type": "string"},
+                        "name": "item_id",
+                        "in": "path",
+                    }
+                ],
+            }
+        },
+    },
+    "components": {
+        "schemas": {
+            "ValidationError": {
+                "title": "ValidationError",
+                "required": ["loc", "msg", "type"],
+                "type": "object",
+                "properties": {
+                    "loc": {
+                        "title": "Location",
+                        "type": "array",
+                        "items": {"type": "string"},
+                    },
+                    "msg": {"title": "Message", "type": "string"},
+                    "type": {"title": "Error Type", "type": "string"},
+                },
+            },
+            "HTTPValidationError": {
+                "title": "HTTPValidationError",
+                "type": "object",
+                "properties": {
+                    "detail": {
+                        "title": "Detail",
+                        "type": "array",
+                        "items": {"$ref": "#/components/schemas/ValidationError"},
+                    }
+                },
+            },
+        }
+    },
+}
+
+
+@pytest.mark.parametrize(
+    "path,expected_status,expected_response",
+    [
+        ("/users", 200, [{"username": "Foo"}, {"username": "Bar"}]),
+        ("/users/foo", 200, {"username": "foo"}),
+        ("/users/me", 200, {"username": "fakecurrentuser"}),
+        ("/items", 200, [{"name": "Item Foo"}, {"name": "item Bar"}]),
+        ("/items/bar", 200, {"name": "Fake Specific Item", "item_id": "bar"}),
+        ("/openapi.json", 200, openapi_schema),
+    ],
+)
+def test_get_path(path, expected_status, expected_response):
+    response = client.get(path)
+    assert response.status_code == expected_status
+    assert response.json() == expected_response
diff --git a/tests/test_tutorial/test_body/__init__.py b/tests/test_tutorial/test_body/__init__.py
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/tests/test_tutorial/test_body/test_tutorial001.py b/tests/test_tutorial/test_body/test_tutorial001.py
new file mode 100644 (file)
index 0000000..f665eb0
--- /dev/null
@@ -0,0 +1,163 @@
+import pytest
+from starlette.testclient import TestClient
+
+from body.tutorial001 import app
+
+client = TestClient(app)
+
+openapi_schema = {
+    "openapi": "3.0.2",
+    "info": {"title": "Fast API", "version": "0.1.0"},
+    "paths": {
+        "/items/": {
+            "post": {
+                "responses": {
+                    "200": {
+                        "description": "Successful Response",
+                        "content": {"application/json": {"schema": {}}},
+                    },
+                    "422": {
+                        "description": "Validation Error",
+                        "content": {
+                            "application/json": {
+                                "schema": {
+                                    "$ref": "#/components/schemas/HTTPValidationError"
+                                }
+                            }
+                        },
+                    },
+                },
+                "summary": "Create Item Post",
+                "operationId": "create_item_items__post",
+                "requestBody": {
+                    "content": {
+                        "application/json": {
+                            "schema": {"$ref": "#/components/schemas/Item"}
+                        }
+                    },
+                    "required": True,
+                },
+            }
+        }
+    },
+    "components": {
+        "schemas": {
+            "Item": {
+                "title": "Item",
+                "required": ["name", "price"],
+                "type": "object",
+                "properties": {
+                    "name": {"title": "Name", "type": "string"},
+                    "price": {"title": "Price", "type": "number"},
+                    "description": {"title": "Description", "type": "string"},
+                    "tax": {"title": "Tax", "type": "number"},
+                },
+            },
+            "ValidationError": {
+                "title": "ValidationError",
+                "required": ["loc", "msg", "type"],
+                "type": "object",
+                "properties": {
+                    "loc": {
+                        "title": "Location",
+                        "type": "array",
+                        "items": {"type": "string"},
+                    },
+                    "msg": {"title": "Message", "type": "string"},
+                    "type": {"title": "Error Type", "type": "string"},
+                },
+            },
+            "HTTPValidationError": {
+                "title": "HTTPValidationError",
+                "type": "object",
+                "properties": {
+                    "detail": {
+                        "title": "Detail",
+                        "type": "array",
+                        "items": {"$ref": "#/components/schemas/ValidationError"},
+                    }
+                },
+            },
+        }
+    },
+}
+
+
+def test_openapi_scheme():
+    response = client.get("/openapi.json")
+    assert response.status_code == 200
+    assert response.json() == openapi_schema
+
+
+price_missing = {
+    "detail": [
+        {
+            "loc": ["body", "item", "price"],
+            "msg": "field required",
+            "type": "value_error.missing",
+        }
+    ]
+}
+
+price_not_float = {
+    "detail": [
+        {
+            "loc": ["body", "item", "price"],
+            "msg": "value is not a valid float",
+            "type": "type_error.float",
+        }
+    ]
+}
+
+name_price_missing = {
+    "detail": [
+        {
+            "loc": ["body", "item", "name"],
+            "msg": "field required",
+            "type": "value_error.missing",
+        },
+        {
+            "loc": ["body", "item", "price"],
+            "msg": "field required",
+            "type": "value_error.missing",
+        },
+    ]
+}
+
+
+@pytest.mark.parametrize(
+    "path,body,expected_status,expected_response",
+    [
+        (
+            "/items/",
+            {"name": "Foo", "price": 50.5},
+            200,
+            {"name": "Foo", "price": 50.5, "description": None, "tax": None},
+        ),
+        (
+            "/items/",
+            {"name": "Foo", "price": "50.5"},
+            200,
+            {"name": "Foo", "price": 50.5, "description": None, "tax": None},
+        ),
+        (
+            "/items/",
+            {"name": "Foo", "price": "50.5", "description": "Some Foo"},
+            200,
+            {"name": "Foo", "price": 50.5, "description": "Some Foo", "tax": None},
+        ),
+        (
+            "/items/",
+            {"name": "Foo", "price": "50.5", "description": "Some Foo", "tax": 0.3},
+            200,
+            {"name": "Foo", "price": 50.5, "description": "Some Foo", "tax": 0.3},
+        ),
+        ("/items/", {"name": "Foo"}, 422, price_missing),
+        ("/items/", {"name": "Foo", "price": "twenty"}, 422, price_not_float),
+        ("/items/", {}, 422, name_price_missing),
+    ],
+)
+def test_post_body(path, body, expected_status, expected_response):
+    response = client.post(path, json=body)
+    assert response.status_code == expected_status
+    assert response.json() == expected_response
diff --git a/tests/test_tutorial/test_body_multiple_params/__init__.py b/tests/test_tutorial/test_body_multiple_params/__init__.py
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/tests/test_tutorial/test_body_multiple_params/test_tutorial001.py b/tests/test_tutorial/test_body_multiple_params/test_tutorial001.py
new file mode 100644 (file)
index 0000000..6f2aa94
--- /dev/null
@@ -0,0 +1,148 @@
+import pytest
+from starlette.testclient import TestClient
+
+from body_multiple_params.tutorial001 import app
+
+client = TestClient(app)
+
+openapi_schema = {
+    "openapi": "3.0.2",
+    "info": {"title": "Fast API", "version": "0.1.0"},
+    "paths": {
+        "/items/{item_id}": {
+            "put": {
+                "responses": {
+                    "200": {
+                        "description": "Successful Response",
+                        "content": {"application/json": {"schema": {}}},
+                    },
+                    "422": {
+                        "description": "Validation Error",
+                        "content": {
+                            "application/json": {
+                                "schema": {
+                                    "$ref": "#/components/schemas/HTTPValidationError"
+                                }
+                            }
+                        },
+                    },
+                },
+                "summary": "Update Item Put",
+                "operationId": "update_item_items__item_id__put",
+                "parameters": [
+                    {
+                        "required": True,
+                        "schema": {
+                            "title": "The ID of the item to get",
+                            "maximum": 1000.0,
+                            "minimum": 0.0,
+                            "type": "integer",
+                        },
+                        "name": "item_id",
+                        "in": "path",
+                    },
+                    {
+                        "required": False,
+                        "schema": {"title": "Q", "type": "string"},
+                        "name": "q",
+                        "in": "query",
+                    },
+                ],
+                "requestBody": {
+                    "content": {
+                        "application/json": {
+                            "schema": {"$ref": "#/components/schemas/Item"}
+                        }
+                    }
+                },
+            }
+        }
+    },
+    "components": {
+        "schemas": {
+            "Item": {
+                "title": "Item",
+                "required": ["name", "price"],
+                "type": "object",
+                "properties": {
+                    "name": {"title": "Name", "type": "string"},
+                    "price": {"title": "Price", "type": "number"},
+                    "description": {"title": "Description", "type": "string"},
+                    "tax": {"title": "Tax", "type": "number"},
+                },
+            },
+            "ValidationError": {
+                "title": "ValidationError",
+                "required": ["loc", "msg", "type"],
+                "type": "object",
+                "properties": {
+                    "loc": {
+                        "title": "Location",
+                        "type": "array",
+                        "items": {"type": "string"},
+                    },
+                    "msg": {"title": "Message", "type": "string"},
+                    "type": {"title": "Error Type", "type": "string"},
+                },
+            },
+            "HTTPValidationError": {
+                "title": "HTTPValidationError",
+                "type": "object",
+                "properties": {
+                    "detail": {
+                        "title": "Detail",
+                        "type": "array",
+                        "items": {"$ref": "#/components/schemas/ValidationError"},
+                    }
+                },
+            },
+        }
+    },
+}
+
+
+def test_openapi_scheme():
+    response = client.get("/openapi.json")
+    assert response.status_code == 200
+    assert response.json() == openapi_schema
+
+
+item_id_not_int = {
+    "detail": [
+        {
+            "loc": ["path", "item_id"],
+            "msg": "value is not a valid integer",
+            "type": "type_error.integer",
+        }
+    ]
+}
+
+
+@pytest.mark.parametrize(
+    "path,body,expected_status,expected_response",
+    [
+        (
+            "/items/5?q=bar",
+            {"name": "Foo", "price": 50.5},
+            200,
+            {
+                "item_id": 5,
+                "item": {
+                    "name": "Foo",
+                    "price": 50.5,
+                    "description": None,
+                    "tax": None,
+                },
+                "q": "bar",
+            },
+        ),
+        ("/items/5?q=bar", None, 200, {"item_id": 5, "q": "bar"}),
+        ("/items/5", None, 200, {"item_id": 5}),
+        ("/items/foo", None, 422, item_id_not_int),
+    ],
+)
+def test_post_body(path, body, expected_status, expected_response):
+    response = client.put(path, json=body)
+    print(response.text)
+    assert response.status_code == expected_status
+    assert response.json() == expected_response
diff --git a/tests/test_tutorial/test_dependencies/__init__.py b/tests/test_tutorial/test_dependencies/__init__.py
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/tests/test_tutorial/test_dependencies/test_tutorial001.py b/tests/test_tutorial/test_dependencies/test_tutorial001.py
new file mode 100644 (file)
index 0000000..7721812
--- /dev/null
@@ -0,0 +1,100 @@
+import pytest
+from starlette.testclient import TestClient
+
+from dependencies.tutorial001 import app
+
+client = TestClient(app)
+
+openapi_schema = {
+    "openapi": "3.0.2",
+    "info": {"title": "Fast API", "version": "0.1.0"},
+    "paths": {
+        "/items/": {
+            "get": {
+                "responses": {
+                    "200": {
+                        "description": "Successful Response",
+                        "content": {"application/json": {"schema": {}}},
+                    },
+                    "422": {
+                        "description": "Validation Error",
+                        "content": {
+                            "application/json": {
+                                "schema": {
+                                    "$ref": "#/components/schemas/HTTPValidationError"
+                                }
+                            }
+                        },
+                    },
+                },
+                "summary": "Read Items Get",
+                "operationId": "read_items_items__get",
+                "parameters": [
+                    {
+                        "required": False,
+                        "schema": {"title": "Q", "type": "string"},
+                        "name": "q",
+                        "in": "query",
+                    },
+                    {
+                        "required": False,
+                        "schema": {"title": "Skip", "type": "integer", "default": 0},
+                        "name": "skip",
+                        "in": "query",
+                    },
+                    {
+                        "required": False,
+                        "schema": {"title": "Limit", "type": "integer", "default": 100},
+                        "name": "limit",
+                        "in": "query",
+                    },
+                ],
+            }
+        }
+    },
+    "components": {
+        "schemas": {
+            "ValidationError": {
+                "title": "ValidationError",
+                "required": ["loc", "msg", "type"],
+                "type": "object",
+                "properties": {
+                    "loc": {
+                        "title": "Location",
+                        "type": "array",
+                        "items": {"type": "string"},
+                    },
+                    "msg": {"title": "Message", "type": "string"},
+                    "type": {"title": "Error Type", "type": "string"},
+                },
+            },
+            "HTTPValidationError": {
+                "title": "HTTPValidationError",
+                "type": "object",
+                "properties": {
+                    "detail": {
+                        "title": "Detail",
+                        "type": "array",
+                        "items": {"$ref": "#/components/schemas/ValidationError"},
+                    }
+                },
+            },
+        }
+    },
+}
+
+
+@pytest.mark.parametrize(
+    "path,expected_status,expected_response",
+    [
+        ("/items", 200, {"q": None, "skip": 0, "limit": 100}),
+        ("/items?q=foo", 200, {"q": "foo", "skip": 0, "limit": 100}),
+        ("/items?q=foo&skip=5", 200, {"q": "foo", "skip": 5, "limit": 100}),
+        ("/items?q=foo&skip=5&limit=30", 200, {"q": "foo", "skip": 5, "limit": 30}),
+        ("/openapi.json", 200, openapi_schema),
+    ],
+)
+def test_get(path, expected_status, expected_response):
+    response = client.get(path)
+    assert response.status_code == expected_status
+    assert response.json() == expected_response
diff --git a/tests/test_tutorial/test_first_steps/__init__.py b/tests/test_tutorial/test_first_steps/__init__.py
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/tests/test_tutorial/test_first_steps/test_tutorial001.py b/tests/test_tutorial/test_first_steps/test_tutorial001.py
new file mode 100644 (file)
index 0000000..39f5311
--- /dev/null
@@ -0,0 +1,39 @@
+import pytest
+from starlette.testclient import TestClient
+
+from first_steps.tutorial001 import app
+
+client = TestClient(app)
+
+openapi_schema = {
+    "openapi": "3.0.2",
+    "info": {"title": "Fast API", "version": "0.1.0"},
+    "paths": {
+        "/": {
+            "get": {
+                "responses": {
+                    "200": {
+                        "description": "Successful Response",
+                        "content": {"application/json": {"schema": {}}},
+                    }
+                },
+                "summary": "Root Get",
+                "operationId": "root__get",
+            }
+        }
+    },
+}
+
+
+@pytest.mark.parametrize(
+    "path,expected_status,expected_response",
+    [
+        ("/", 200, {"message": "Hello World"}),
+        ("/nonexistent", 404, {"detail": "Not Found"}),
+        ("/openapi.json", 200, openapi_schema),
+    ],
+)
+def test_get_path(path, expected_status, expected_response):
+    response = client.get(path)
+    assert response.status_code == expected_status
+    assert response.json() == expected_response
diff --git a/tests/test_tutorial/test_security/__init__.py b/tests/test_tutorial/test_security/__init__.py
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/tests/test_tutorial/test_security/test_tutorial001.py b/tests/test_tutorial/test_security/test_tutorial001.py
new file mode 100644 (file)
index 0000000..7c73a66
--- /dev/null
@@ -0,0 +1,57 @@
+from starlette.testclient import TestClient
+
+from security.tutorial001 import app
+
+client = TestClient(app)
+
+openapi_schema = {
+    "openapi": "3.0.2",
+    "info": {"title": "Fast API", "version": "0.1.0"},
+    "paths": {
+        "/items/": {
+            "get": {
+                "responses": {
+                    "200": {
+                        "description": "Successful Response",
+                        "content": {"application/json": {"schema": {}}},
+                    }
+                },
+                "summary": "Read Items Get",
+                "operationId": "read_items_items__get",
+                "security": [{"OAuth2PasswordBearer": []}],
+            }
+        }
+    },
+    "components": {
+        "securitySchemes": {
+            "OAuth2PasswordBearer": {
+                "type": "oauth2",
+                "flows": {"password": {"scopes": {}, "tokenUrl": "/token"}},
+            }
+        }
+    },
+}
+
+
+def test_openapi_scheme():
+    response = client.get("/openapi.json")
+    assert response.status_code == 200
+    assert response.json() == openapi_schema
+
+
+def test_no_token():
+    response = client.get("/items")
+    assert response.status_code == 403
+    assert response.json() == {"detail": "Not authenticated"}
+
+
+def test_token():
+    response = client.get("/items", headers={"Authorization": "Bearer testtoken"})
+    assert response.status_code == 200
+    assert response.json() == {"token": "testtoken"}
+
+
+def test_incorrect_token():
+    response = client.get("/items", headers={"Authorization": "Notexistent testtoken"})
+    assert response.status_code == 403
+    assert response.json() == {"detail": "Not authenticated"}
similarity index 100%
rename from tests/test_path.py
rename to tests/xtest_path.py
similarity index 100%
rename from tests/test_query.py
rename to tests/xtest_query.py