Browse Source

Merge branch 'master' into feat/exception-on-duplicate-path

pull/5595/head
Irfanuddin Shafi Ahmed 3 years ago
committed by GitHub
parent
commit
a096af742a
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 5
      .flake8
  2. 15
      .pre-commit-config.yaml
  3. 4
      README.md
  4. 2
      docs/az/docs/index.md
  5. 2
      docs/de/docs/features.md
  6. 2
      docs/de/docs/index.md
  7. 14
      docs/en/docs/advanced/async-tests.md
  8. 4
      docs/en/docs/advanced/openapi-callbacks.md
  9. 6
      docs/en/docs/advanced/websockets.md
  10. 2
      docs/en/docs/alternatives.md
  11. 2
      docs/en/docs/features.md
  12. 114
      docs/en/docs/help-fastapi.md
  13. 4
      docs/en/docs/index.md
  14. 9
      docs/en/docs/release-notes.md
  15. 12
      docs/en/docs/tutorial/testing.md
  16. 2
      docs/es/docs/features.md
  17. 4
      docs/es/docs/index.md
  18. 4
      docs/fa/docs/index.md
  19. 240
      docs/fr/docs/advanced/additional-responses.md
  20. 37
      docs/fr/docs/advanced/additional-status-codes.md
  21. 4
      docs/fr/docs/index.md
  22. 3
      docs/fr/mkdocs.yml
  23. 2
      docs/he/docs/index.md
  24. 4
      docs/id/docs/index.md
  25. 4
      docs/it/docs/index.md
  26. 186
      docs/ja/docs/advanced/websockets.md
  27. 2
      docs/ja/docs/features.md
  28. 4
      docs/ja/docs/index.md
  29. 8
      docs/ja/docs/tutorial/query-params.md
  30. 8
      docs/ja/docs/tutorial/testing.md
  31. 1
      docs/ja/mkdocs.yml
  32. 4
      docs/ko/docs/index.md
  33. 4
      docs/nl/docs/index.md
  34. 4
      docs/pl/docs/index.md
  35. 2
      docs/pt/docs/features.md
  36. 4
      docs/pt/docs/index.md
  37. 36
      docs/pt/docs/tutorial/request-forms-and-files.md
  38. 1
      docs/pt/mkdocs.yml
  39. 2
      docs/ru/docs/features.md
  40. 4
      docs/ru/docs/index.md
  41. 4
      docs/sq/docs/index.md
  42. 4
      docs/sv/docs/index.md
  43. 2
      docs/tr/docs/features.md
  44. 4
      docs/tr/docs/index.md
  45. 4
      docs/uk/docs/index.md
  46. 2
      docs/zh/docs/features.md
  47. 4
      docs/zh/docs/index.md
  48. 2
      docs_src/security/tutorial005.py
  49. 2
      docs_src/security/tutorial005_py310.py
  50. 2
      docs_src/security/tutorial005_py39.py
  51. 12
      docs_src/websockets/tutorial002.py
  52. 1
      fastapi/__init__.py
  53. 12
      fastapi/dependencies/utils.py
  54. 1
      fastapi/exceptions.py
  55. 2
      fastapi/routing.py
  56. 2
      fastapi/security/api_key.py
  57. 8
      fastapi/security/http.py
  58. 6
      fastapi/security/oauth2.py
  59. 2
      fastapi/security/open_id_connect_url.py
  60. 6
      fastapi/security/utils.py
  61. 37
      pyproject.toml
  62. 4
      scripts/docs.py
  63. 2
      scripts/format.sh
  64. 2
      scripts/lint.sh
  65. 6
      tests/test_custom_route_class.py
  66. 2
      tests/test_enforce_once_required_parameter.py
  67. 2
      tests/test_extra_routes.py
  68. 2
      tests/test_get_request_body.py
  69. 9
      tests/test_param_include_in_schema.py
  70. 7
      tests/test_security_api_key_cookie.py
  71. 7
      tests/test_security_api_key_cookie_description.py
  72. 7
      tests/test_security_api_key_cookie_optional.py
  73. 4
      tests/test_security_http_basic_optional.py
  74. 4
      tests/test_security_http_basic_realm.py
  75. 4
      tests/test_security_http_basic_realm_description.py
  76. 8
      tests/test_tuples.py
  77. 2
      tests/test_tutorial/test_advanced_middleware/test_tutorial001.py
  78. 16
      tests/test_tutorial/test_body/test_tutorial001.py
  79. 16
      tests/test_tutorial/test_body/test_tutorial001_py310.py
  80. 5
      tests/test_tutorial/test_cookie_params/test_tutorial001.py
  81. 15
      tests/test_tutorial/test_cookie_params/test_tutorial001_py310.py
  82. 2
      tests/test_tutorial/test_custom_request_and_route/test_tutorial001.py
  83. 2
      tests/test_tutorial/test_custom_response/test_tutorial006.py
  84. 2
      tests/test_tutorial/test_custom_response/test_tutorial006b.py
  85. 2
      tests/test_tutorial/test_custom_response/test_tutorial006c.py
  86. 2
      tests/test_tutorial/test_path_operation_advanced_configurations/test_tutorial006.py
  87. 6
      tests/test_tutorial/test_path_operation_advanced_configurations/test_tutorial007.py
  88. 4
      tests/test_tutorial/test_security/test_tutorial006.py
  89. 12
      tests/test_tutorial/test_websockets/test_tutorial002.py
  90. 2
      tests/test_ws_router.py

5
.flake8

@ -1,5 +0,0 @@
[flake8]
max-line-length = 88
select = C,E,F,W,B,B9
ignore = E203, E501, W503
exclude = __init__.py

15
.pre-commit-config.yaml

@ -18,19 +18,12 @@ repos:
args: args:
- --py3-plus - --py3-plus
- --keep-runtime-typing - --keep-runtime-typing
- repo: https://github.com/PyCQA/autoflake - repo: https://github.com/charliermarsh/ruff-pre-commit
rev: v1.7.7 rev: v0.0.114
hooks: hooks:
- id: autoflake - id: ruff
args: args:
- --recursive - --fix
- --in-place
- --remove-all-unused-imports
- --remove-unused-variables
- --expand-star-imports
- --exclude
- __init__.py
- --remove-duplicate-keys
- repo: https://github.com/pycqa/isort - repo: https://github.com/pycqa/isort
rev: 5.10.1 rev: 5.10.1
hooks: hooks:

4
README.md

@ -427,7 +427,7 @@ For a more complete example including more features, see the <a href="https://fa
* **GraphQL** integration with <a href="https://strawberry.rocks" class="external-link" target="_blank">Strawberry</a> and other libraries. * **GraphQL** integration with <a href="https://strawberry.rocks" class="external-link" target="_blank">Strawberry</a> and other libraries.
* Many extra features (thanks to Starlette) as: * Many extra features (thanks to Starlette) as:
* **WebSockets** * **WebSockets**
* extremely easy tests based on `requests` and `pytest` * extremely easy tests based on HTTPX and `pytest`
* **CORS** * **CORS**
* **Cookie Sessions** * **Cookie Sessions**
* ...and more. * ...and more.
@ -447,7 +447,7 @@ Used by Pydantic:
Used by Starlette: Used by Starlette:
* <a href="https://requests.readthedocs.io" target="_blank"><code>requests</code></a> - Required if you want to use the `TestClient`. * <a href="https://www.python-httpx.org" target="_blank"><code>httpx</code></a> - Required if you want to use the `TestClient`.
* <a href="https://jinja.palletsprojects.com" target="_blank"><code>jinja2</code></a> - Required if you want to use the default template configuration. * <a href="https://jinja.palletsprojects.com" target="_blank"><code>jinja2</code></a> - Required if you want to use the default template configuration.
* <a href="https://andrew-d.github.io/python-multipart/" target="_blank"><code>python-multipart</code></a> - Required if you want to support form <abbr title="converting the string that comes from an HTTP request into Python data">"parsing"</abbr>, with `request.form()`. * <a href="https://andrew-d.github.io/python-multipart/" target="_blank"><code>python-multipart</code></a> - Required if you want to support form <abbr title="converting the string that comes from an HTTP request into Python data">"parsing"</abbr>, with `request.form()`.
* <a href="https://pythonhosted.org/itsdangerous/" target="_blank"><code>itsdangerous</code></a> - Required for `SessionMiddleware` support. * <a href="https://pythonhosted.org/itsdangerous/" target="_blank"><code>itsdangerous</code></a> - Required for `SessionMiddleware` support.

2
docs/az/docs/index.md

@ -446,7 +446,7 @@ Used by Pydantic:
Used by Starlette: Used by Starlette:
* <a href="https://requests.readthedocs.io" target="_blank"><code>requests</code></a> - Required if you want to use the `TestClient`. * <a href="https://www.python-httpx.org" target="_blank"><code>httpx</code></a> - Required if you want to use the `TestClient`.
* <a href="https://jinja.palletsprojects.com" target="_blank"><code>jinja2</code></a> - Required if you want to use the default template configuration. * <a href="https://jinja.palletsprojects.com" target="_blank"><code>jinja2</code></a> - Required if you want to use the default template configuration.
* <a href="https://andrew-d.github.io/python-multipart/" target="_blank"><code>python-multipart</code></a> - Required if you want to support form <abbr title="converting the string that comes from an HTTP request into Python data">"parsing"</abbr>, with `request.form()`. * <a href="https://andrew-d.github.io/python-multipart/" target="_blank"><code>python-multipart</code></a> - Required if you want to support form <abbr title="converting the string that comes from an HTTP request into Python data">"parsing"</abbr>, with `request.form()`.
* <a href="https://pythonhosted.org/itsdangerous/" target="_blank"><code>itsdangerous</code></a> - Required for `SessionMiddleware` support. * <a href="https://pythonhosted.org/itsdangerous/" target="_blank"><code>itsdangerous</code></a> - Required for `SessionMiddleware` support.

2
docs/de/docs/features.md

@ -169,7 +169,7 @@ Mit **FastAPI** bekommen Sie viele von **Starlette**'s Funktionen (da FastAPI nu
* **WebSocket**-Unterstützung. * **WebSocket**-Unterstützung.
* Hintergrundaufgaben im selben Prozess. * Hintergrundaufgaben im selben Prozess.
* Ereignisse für das Starten und Herunterfahren. * Ereignisse für das Starten und Herunterfahren.
* Testclient basierend auf `requests`. * Testclient basierend auf HTTPX.
* **CORS**, GZip, statische Dateien, Antwortfluss. * **CORS**, GZip, statische Dateien, Antwortfluss.
* **Sitzungs und Cookie** Unterstützung. * **Sitzungs und Cookie** Unterstützung.
* 100% Testabdeckung. * 100% Testabdeckung.

2
docs/de/docs/index.md

@ -445,7 +445,7 @@ Used by Pydantic:
Used by Starlette: Used by Starlette:
* <a href="https://requests.readthedocs.io" target="_blank"><code>requests</code></a> - Required if you want to use the `TestClient`. * <a href="https://www.python-httpx.org" target="_blank"><code>httpx</code></a> - Required if you want to use the `TestClient`.
* <a href="https://jinja.palletsprojects.com" target="_blank"><code>jinja2</code></a> - Required if you want to use the default template configuration. * <a href="https://jinja.palletsprojects.com" target="_blank"><code>jinja2</code></a> - Required if you want to use the default template configuration.
* <a href="https://andrew-d.github.io/python-multipart/" target="_blank"><code>python-multipart</code></a> - Required if you want to support form <abbr title="converting the string that comes from an HTTP request into Python data">"parsing"</abbr>, with `request.form()`. * <a href="https://andrew-d.github.io/python-multipart/" target="_blank"><code>python-multipart</code></a> - Required if you want to support form <abbr title="converting the string that comes from an HTTP request into Python data">"parsing"</abbr>, with `request.form()`.
* <a href="https://pythonhosted.org/itsdangerous/" target="_blank"><code>itsdangerous</code></a> - Required for `SessionMiddleware` support. * <a href="https://pythonhosted.org/itsdangerous/" target="_blank"><code>itsdangerous</code></a> - Required for `SessionMiddleware` support.

14
docs/en/docs/advanced/async-tests.md

@ -1,6 +1,6 @@
# Async Tests # Async Tests
You have already seen how to test your **FastAPI** applications using the provided `TestClient`, but with it, you can't test or run any other `async` function in your (synchronous) pytest functions. You have already seen how to test your **FastAPI** applications using the provided `TestClient`. Up to now, you have only seen how to write synchronous tests, without using `async` functions.
Being able to use asynchronous functions in your tests could be useful, for example, when you're querying your database asynchronously. Imagine you want to test sending requests to your FastAPI application and then verify that your backend successfully wrote the correct data in the database, while using an async database library. Being able to use asynchronous functions in your tests could be useful, for example, when you're querying your database asynchronously. Imagine you want to test sending requests to your FastAPI application and then verify that your backend successfully wrote the correct data in the database, while using an async database library.
@ -8,7 +8,7 @@ Let's look at how we can make that work.
## pytest.mark.anyio ## pytest.mark.anyio
If we want to call asynchronous functions in our tests, our test functions have to be asynchronous. Anyio provides a neat plugin for this, that allows us to specify that some test functions are to be called asynchronously. If we want to call asynchronous functions in our tests, our test functions have to be asynchronous. AnyIO provides a neat plugin for this, that allows us to specify that some test functions are to be called asynchronously.
## HTTPX ## HTTPX
@ -16,13 +16,7 @@ Even if your **FastAPI** application uses normal `def` functions instead of `asy
The `TestClient` does some magic inside to call the asynchronous FastAPI application in your normal `def` test functions, using standard pytest. But that magic doesn't work anymore when we're using it inside asynchronous functions. By running our tests asynchronously, we can no longer use the `TestClient` inside our test functions. The `TestClient` does some magic inside to call the asynchronous FastAPI application in your normal `def` test functions, using standard pytest. But that magic doesn't work anymore when we're using it inside asynchronous functions. By running our tests asynchronously, we can no longer use the `TestClient` inside our test functions.
Luckily there's a nice alternative, called <a href="https://www.python-httpx.org/" class="external-link" target="_blank">HTTPX</a>. The `TestClient` is based on <a href="https://www.python-httpx.org" class="external-link" target="_blank">HTTPX</a>, and luckily, we can use it directly to test the API.
HTTPX is an HTTP client for Python 3 that allows us to query our FastAPI application similarly to how we did it with the `TestClient`.
If you're familiar with the <a href="https://requests.readthedocs.io/en/master/" class="external-link" target="_blank">Requests</a> library, you'll find that the API of HTTPX is almost identical.
The important difference for us is that with HTTPX we are not limited to synchronous, but can also make asynchronous requests.
## Example ## Example
@ -85,7 +79,7 @@ This is the equivalent to:
response = client.get('/') response = client.get('/')
``` ```
that we used to make our requests with the `TestClient`. ...that we used to make our requests with the `TestClient`.
!!! tip !!! tip
Note that we're using async/await with the new `AsyncClient` - the request is asynchronous. Note that we're using async/await with the new `AsyncClient` - the request is asynchronous.

4
docs/en/docs/advanced/openapi-callbacks.md

@ -50,7 +50,7 @@ It could be just one or two lines of code, like:
```Python ```Python
callback_url = "https://example.com/api/v1/invoices/events/" callback_url = "https://example.com/api/v1/invoices/events/"
requests.post(callback_url, json={"description": "Invoice paid", "paid": True}) httpx.post(callback_url, json={"description": "Invoice paid", "paid": True})
``` ```
But possibly the most important part of the callback is making sure that your API user (the external developer) implements the *external API* correctly, according to the data that *your API* is going to send in the request body of the callback, etc. But possibly the most important part of the callback is making sure that your API user (the external developer) implements the *external API* correctly, according to the data that *your API* is going to send in the request body of the callback, etc.
@ -64,7 +64,7 @@ This example doesn't implement the callback itself (that could be just a line of
!!! tip !!! tip
The actual callback is just an HTTP request. The actual callback is just an HTTP request.
When implementing the callback yourself, you could use something like <a href="https://www.encode.io/httpx/" class="external-link" target="_blank">HTTPX</a> or <a href="https://requests.readthedocs.io/" class="external-link" target="_blank">Requests</a>. When implementing the callback yourself, you could use something like <a href="https://www.python-httpx.org" class="external-link" target="_blank">HTTPX</a> or <a href="https://requests.readthedocs.io/" class="external-link" target="_blank">Requests</a>.
## Write the callback documentation code ## Write the callback documentation code

6
docs/en/docs/advanced/websockets.md

@ -112,17 +112,15 @@ In WebSocket endpoints you can import from `fastapi` and use:
They work the same way as for other FastAPI endpoints/*path operations*: They work the same way as for other FastAPI endpoints/*path operations*:
```Python hl_lines="58-65 68-83" ```Python hl_lines="66-77 76-91"
{!../../../docs_src/websockets/tutorial002.py!} {!../../../docs_src/websockets/tutorial002.py!}
``` ```
!!! info !!! info
In a WebSocket it doesn't really make sense to raise an `HTTPException`. So it's better to close the WebSocket connection directly. As this is a WebSocket it doesn't really make sense to raise an `HTTPException`, instead we raise a `WebSocketException`.
You can use a closing code from the <a href="https://tools.ietf.org/html/rfc6455#section-7.4.1" class="external-link" target="_blank">valid codes defined in the specification</a>. You can use a closing code from the <a href="https://tools.ietf.org/html/rfc6455#section-7.4.1" class="external-link" target="_blank">valid codes defined in the specification</a>.
In the future, there will be a `WebSocketException` that you will be able to `raise` from anywhere, and add exception handlers for it. It depends on the <a href="https://github.com/encode/starlette/pull/527" class="external-link" target="_blank">PR #527</a> in Starlette.
### Try the WebSockets with dependencies ### Try the WebSockets with dependencies
If your file is named `main.py`, run your application with: If your file is named `main.py`, run your application with:

2
docs/en/docs/alternatives.md

@ -367,7 +367,7 @@ It has:
* WebSocket support. * WebSocket support.
* In-process background tasks. * In-process background tasks.
* Startup and shutdown events. * Startup and shutdown events.
* Test client built on requests. * Test client built on HTTPX.
* CORS, GZip, Static Files, Streaming responses. * CORS, GZip, Static Files, Streaming responses.
* Session and Cookie support. * Session and Cookie support.
* 100% test coverage. * 100% test coverage.

2
docs/en/docs/features.md

@ -166,7 +166,7 @@ With **FastAPI** you get all of **Starlette**'s features (as FastAPI is just Sta
* **WebSocket** support. * **WebSocket** support.
* In-process background tasks. * In-process background tasks.
* Startup and shutdown events. * Startup and shutdown events.
* Test client built on `requests`. * Test client built on HTTPX.
* **CORS**, GZip, Static Files, Streaming responses. * **CORS**, GZip, Static Files, Streaming responses.
* **Session and Cookie** support. * **Session and Cookie** support.
* 100% test coverage. * 100% test coverage.

114
docs/en/docs/help-fastapi.md

@ -47,7 +47,7 @@ You can:
* <a href="https://github.com/tiangolo" class="external-link" target="_blank">Follow me on **GitHub**</a>. * <a href="https://github.com/tiangolo" class="external-link" target="_blank">Follow me on **GitHub**</a>.
* See other Open Source projects I have created that could help you. * See other Open Source projects I have created that could help you.
* Follow me to see when I create a new Open Source project. * Follow me to see when I create a new Open Source project.
* <a href="https://twitter.com/tiangolo" class="external-link" target="_blank">Follow me on **Twitter**</a>. * <a href="https://twitter.com/tiangolo" class="external-link" target="_blank">Follow me on **Twitter**</a> or <a href="https://fosstodon.org/@tiangolo" class="external-link" target="_blank">Mastodon</a>.
* Tell me how you use FastAPI (I love to hear that). * Tell me how you use FastAPI (I love to hear that).
* Hear when I make announcements or release new tools. * Hear when I make announcements or release new tools.
* You can also <a href="https://twitter.com/fastapi" class="external-link" target="_blank">follow @fastapi on Twitter</a> (a separate account). * You can also <a href="https://twitter.com/fastapi" class="external-link" target="_blank">follow @fastapi on Twitter</a> (a separate account).
@ -67,13 +67,54 @@ I love to hear about how **FastAPI** is being used, what you have liked in it, i
* <a href="https://www.slant.co/options/34241/~fastapi-review" class="external-link" target="_blank">Vote for **FastAPI** in Slant</a>. * <a href="https://www.slant.co/options/34241/~fastapi-review" class="external-link" target="_blank">Vote for **FastAPI** in Slant</a>.
* <a href="https://alternativeto.net/software/fastapi/" class="external-link" target="_blank">Vote for **FastAPI** in AlternativeTo</a>. * <a href="https://alternativeto.net/software/fastapi/" class="external-link" target="_blank">Vote for **FastAPI** in AlternativeTo</a>.
* <a href="https://stackshare.io/pypi-fastapi" class="external-link" target="_blank">Say you use **FastAPI** on StackShare</a>.
## Help others with issues in GitHub ## Help others with issues in GitHub
You can see <a href="https://github.com/tiangolo/fastapi/issues" class="external-link" target="_blank">existing issues</a> and try and help others, most of the times they are questions that you might already know the answer for. 🤓 You can see <a href="https://github.com/tiangolo/fastapi/issues" class="external-link" target="_blank">existing issues</a> and try and help others, most of the times those issues are questions that you might already know the answer for. 🤓
If you are helping a lot of people with issues, you might become an official [FastAPI Expert](fastapi-people.md#experts){.internal-link target=_blank}. 🎉 If you are helping a lot of people with issues, you might become an official [FastAPI Expert](fastapi-people.md#experts){.internal-link target=_blank}. 🎉
Just remember, the most important point is: try to be kind. People come with their frustrations and in many cases don't ask in the best way, but try as best as you can to be kind. 🤗
The idea is for the **FastAPI** community to be kind and welcoming. At the same time, don't accept bullying or disrespectful behavior towards others. We have to take care of each other.
---
Here's how to help others with issues:
### Understand the question
* Check if you can understand what is the **purpose** and use case of the person asking.
* Then check if the question (the vast majority are questions) is **clear**.
* In many cases the question asked is about an imaginary solution from the user, but there might be a **better** one. If you can understand the problem and use case better, you might be able to suggest a better **alternative solution**.
* If you can't understand the question, ask for more **details**.
### Reproduce the problem
For most of the cases and most of the questions there's something related to the person's **original code**.
In many cases they will only copy a fragment of the code, but that's not enough to **reproduce the problem**.
* You can ask them to provide a <a href="https://stackoverflow.com/help/minimal-reproducible-example" class="external-link" target="_blank">minimal, reproducible, example</a>, that you can **copy-paste** and run locally to see the same error or behavior they are seeing, or to understand their use case better.
* If you are feeling too generous, you can try to **create an example** like that yourself, just based on the description of the problem. Just have in mind that this might take a lot of time and it might be better to ask them to clarify the problem first.
### Suggest solutions
* After being able to understand the question, you can give them a possible **answer**.
* In many cases, it's better to understand their **underlying problem or use case**, because there might be a better way to solve it than what they are trying to do.
### Ask to close
If they reply, there's a high chance you will have solved their problem, congrats, **you're a hero**! 🦸
* Now you can ask them, if that solved their problem, to **close the issue**.
## Watch the GitHub repository ## Watch the GitHub repository
You can "watch" FastAPI in GitHub (clicking the "watch" button at the top right): <a href="https://github.com/tiangolo/fastapi" class="external-link" target="_blank">https://github.com/tiangolo/fastapi</a>. 👀 You can "watch" FastAPI in GitHub (clicking the "watch" button at the top right): <a href="https://github.com/tiangolo/fastapi" class="external-link" target="_blank">https://github.com/tiangolo/fastapi</a>. 👀
@ -91,6 +132,57 @@ You can <a href="https://github.com/tiangolo/fastapi/issues/new/choose" class="e
**Note**: if you create an issue, then I'm going to ask you to also help others. 😉 **Note**: if you create an issue, then I'm going to ask you to also help others. 😉
## Review Pull Requests
You can help me review pull requests from others.
Again, please try as best to be kind. 🤗
---
Here's what to have in mind and how to review a pull request:
### Understand the problem
* First, make sure you **understand the problem** that the pull request is trying to solve. It might have a longer discussion in an issue.
* There's also a good chance that the pull request is not actually needed because the problem can be solved in a **different way**. Then you can suggest or ask about that.
### Don't worry about style
* Don't worry too much about things like commit message styles, I will squash and merge customizing the commit manually.
* Also don't worry about style rules, there are already automatized tools checking that.
And if there's any other style or consistency need, I'll ask directly for that, or I'll add commits on top with the needed changes.
### Check the code
* Check and read the code, see if it makes sense, **run it locally** and see if it actually solves the problem.
* Then **comment** saying that you did that, that's how I will know you really checked it.
!!! info
Unfortunately, I can't simply trust PRs that just have several approvals.
Several times it has happened that there are PRs with 3, 5 or more approvals, probably because the description is appealing, but when I check the PRs, they are actually broken, have a bug, or don't solve the problem they claim to solve. 😅
So, it's really important that you actually read and run the code, and let me know in the comments that you did. 🤓
* If the PR can be simplified in a way, you can ask for that, but there's no need to be too picky, there might be a lot of subjective points of view (and I will have my own as well 🙈), so it's better if you can focus on the fundamental things.
### Tests
* Help me check that the PR has **tests**.
* Check that the tests **fail** before the PR. 🚨
* Then check that the tests **pass** after the PR. ✅
* Many PRs don't have tests, you can **remind** them to add tests, or you can even **suggest** some tests yourself. That's one of the things that consume most time and you can help a lot with that.
* Then also comment what you tried, that way I'll know that you checked it. 🤓
## Create a Pull Request ## Create a Pull Request
You can [contribute](contributing.md){.internal-link target=_blank} to the source code with Pull Requests, for example: You can [contribute](contributing.md){.internal-link target=_blank} to the source code with Pull Requests, for example:
@ -102,7 +194,25 @@ You can [contribute](contributing.md){.internal-link target=_blank} to the sourc
* You can also help to review the translations created by others. * You can also help to review the translations created by others.
* To propose new documentation sections. * To propose new documentation sections.
* To fix an existing issue/bug. * To fix an existing issue/bug.
* Make sure to add tests.
* To add a new feature. * To add a new feature.
* Make sure to add tests.
* Make sure to add documentation if it's relevant.
## Help Maintain FastAPI
Help me maintain **FastAPI**! 🤓
There's a lot of work to do, and for most of it, **YOU** can do it.
The main tasks that you can do right now are:
* [Help others with issues in GitHub](#help-others-with-issues-in-github){.internal-link target=_blank} (see the section above).
* [Review Pull Requests](#review-pull-requests){.internal-link target=_blank} (see the section above).
Those two tasks are what **consume time the most**. That's the main work of maintaining FastAPI.
If you can help me with that, **you are helping me maintain FastAPI** and making sure it keeps **advancing faster and better**. 🚀
## Join the chat ## Join the chat

4
docs/en/docs/index.md

@ -424,7 +424,7 @@ For a more complete example including more features, see the <a href="https://fa
* **GraphQL** integration with <a href="https://strawberry.rocks" class="external-link" target="_blank">Strawberry</a> and other libraries. * **GraphQL** integration with <a href="https://strawberry.rocks" class="external-link" target="_blank">Strawberry</a> and other libraries.
* Many extra features (thanks to Starlette) as: * Many extra features (thanks to Starlette) as:
* **WebSockets** * **WebSockets**
* extremely easy tests based on `requests` and `pytest` * extremely easy tests based on HTTPX and `pytest`
* **CORS** * **CORS**
* **Cookie Sessions** * **Cookie Sessions**
* ...and more. * ...and more.
@ -444,7 +444,7 @@ Used by Pydantic:
Used by Starlette: Used by Starlette:
* <a href="https://requests.readthedocs.io" target="_blank"><code>requests</code></a> - Required if you want to use the `TestClient`. * <a href="https://www.python-httpx.org" target="_blank"><code>httpx</code></a> - Required if you want to use the `TestClient`.
* <a href="https://jinja.palletsprojects.com" target="_blank"><code>jinja2</code></a> - Required if you want to use the default template configuration. * <a href="https://jinja.palletsprojects.com" target="_blank"><code>jinja2</code></a> - Required if you want to use the default template configuration.
* <a href="https://andrew-d.github.io/python-multipart/" target="_blank"><code>python-multipart</code></a> - Required if you want to support form <abbr title="converting the string that comes from an HTTP request into Python data">"parsing"</abbr>, with `request.form()`. * <a href="https://andrew-d.github.io/python-multipart/" target="_blank"><code>python-multipart</code></a> - Required if you want to support form <abbr title="converting the string that comes from an HTTP request into Python data">"parsing"</abbr>, with `request.form()`.
* <a href="https://pythonhosted.org/itsdangerous/" target="_blank"><code>itsdangerous</code></a> - Required for `SessionMiddleware` support. * <a href="https://pythonhosted.org/itsdangerous/" target="_blank"><code>itsdangerous</code></a> - Required for `SessionMiddleware` support.

9
docs/en/docs/release-notes.md

@ -2,6 +2,15 @@
## Latest Changes ## Latest Changes
* 📝 Update Help FastAPI: Help Maintain FastAPI. PR [#5632](https://github.com/tiangolo/fastapi/pull/5632) by [@tiangolo](https://github.com/tiangolo).
* ✨ Use Ruff for linting. PR [#5630](https://github.com/tiangolo/fastapi/pull/5630) by [@tiangolo](https://github.com/tiangolo).
* ✨ Re-export Starlette's `WebSocketException` and add it to docs. PR [#5629](https://github.com/tiangolo/fastapi/pull/5629) by [@tiangolo](https://github.com/tiangolo).
* 📝 Update references to Requests for tests to HTTPX, and add HTTPX to extras. PR [#5628](https://github.com/tiangolo/fastapi/pull/5628) by [@tiangolo](https://github.com/tiangolo).
* 🌐 Fix highlight lines for Japanese translation for `docs/tutorial/query-params.md`. PR [#2969](https://github.com/tiangolo/fastapi/pull/2969) by [@ftnext](https://github.com/ftnext).
* ⬆ Upgrade Starlette to `0.21.0`, including the new [`TestClient` based on HTTPX](https://github.com/encode/starlette/releases/tag/0.21.0). PR [#5471](https://github.com/tiangolo/fastapi/pull/5471) by [@pawelrubin](https://github.com/pawelrubin).
* 🌐 Add French translation for `docs/fr/docs/advanced/additional-status-code.md`. PR [#5477](https://github.com/tiangolo/fastapi/pull/5477) by [@axel584](https://github.com/axel584).
* 🌐 Add Portuguese translation for `docs/pt/docs/tutorial/request-forms-and-files.md`. PR [#5579](https://github.com/tiangolo/fastapi/pull/5579) by [@batlopes](https://github.com/batlopes).
* 🌐 Add Japanese translation for `docs/ja/docs/advanced/websockets.md`. PR [#4983](https://github.com/tiangolo/fastapi/pull/4983) by [@xryuseix](https://github.com/xryuseix).
* 🛠 Add Arabic issue number to Notify Translations GitHub Action. PR [#5610](https://github.com/tiangolo/fastapi/pull/5610) by [@tiangolo](https://github.com/tiangolo). * 🛠 Add Arabic issue number to Notify Translations GitHub Action. PR [#5610](https://github.com/tiangolo/fastapi/pull/5610) by [@tiangolo](https://github.com/tiangolo).
* ⬆ Bump dawidd6/action-download-artifact from 2.24.1 to 2.24.2. PR [#5609](https://github.com/tiangolo/fastapi/pull/5609) by [@dependabot[bot]](https://github.com/apps/dependabot). * ⬆ Bump dawidd6/action-download-artifact from 2.24.1 to 2.24.2. PR [#5609](https://github.com/tiangolo/fastapi/pull/5609) by [@dependabot[bot]](https://github.com/apps/dependabot).
* ⬆ Bump dawidd6/action-download-artifact from 2.24.0 to 2.24.1. PR [#5603](https://github.com/tiangolo/fastapi/pull/5603) by [@dependabot[bot]](https://github.com/apps/dependabot). * ⬆ Bump dawidd6/action-download-artifact from 2.24.0 to 2.24.1. PR [#5603](https://github.com/tiangolo/fastapi/pull/5603) by [@dependabot[bot]](https://github.com/apps/dependabot).

12
docs/en/docs/tutorial/testing.md

@ -2,16 +2,16 @@
Thanks to <a href="https://www.starlette.io/testclient/" class="external-link" target="_blank">Starlette</a>, testing **FastAPI** applications is easy and enjoyable. Thanks to <a href="https://www.starlette.io/testclient/" class="external-link" target="_blank">Starlette</a>, testing **FastAPI** applications is easy and enjoyable.
It is based on <a href="https://requests.readthedocs.io" class="external-link" target="_blank">Requests</a>, so it's very familiar and intuitive. It is based on <a href="https://www.python-httpx.org" class="external-link" target="_blank">HTTPX</a>, which in turn is designed based on Requests, so it's very familiar and intuitive.
With it, you can use <a href="https://docs.pytest.org/" class="external-link" target="_blank">pytest</a> directly with **FastAPI**. With it, you can use <a href="https://docs.pytest.org/" class="external-link" target="_blank">pytest</a> directly with **FastAPI**.
## Using `TestClient` ## Using `TestClient`
!!! info !!! info
To use `TestClient`, first install <a href="https://github.com/psf/requests" class="external-link" target="_blank">`requests`</a>. To use `TestClient`, first install <a href="https://www.python-httpx.org" class="external-link" target="_blank">`httpx`</a>.
E.g. `pip install requests`. E.g. `pip install httpx`.
Import `TestClient`. Import `TestClient`.
@ -19,7 +19,7 @@ Create a `TestClient` by passing your **FastAPI** application to it.
Create functions with a name that starts with `test_` (this is standard `pytest` conventions). Create functions with a name that starts with `test_` (this is standard `pytest` conventions).
Use the `TestClient` object the same way as you do with `requests`. Use the `TestClient` object the same way as you do with `httpx`.
Write simple `assert` statements with the standard Python expressions that you need to check (again, standard `pytest`). Write simple `assert` statements with the standard Python expressions that you need to check (again, standard `pytest`).
@ -130,7 +130,7 @@ You could then update `test_main.py` with the extended tests:
{!> ../../../docs_src/app_testing/app_b/test_main.py!} {!> ../../../docs_src/app_testing/app_b/test_main.py!}
``` ```
Whenever you need the client to pass information in the request and you don't know how to, you can search (Google) how to do it in `requests`. Whenever you need the client to pass information in the request and you don't know how to, you can search (Google) how to do it in `httpx`, or even how to do it with `requests`, as HTTPX's design is based on Requests' design.
Then you just do the same in your tests. Then you just do the same in your tests.
@ -142,7 +142,7 @@ E.g.:
* To pass *headers*, use a `dict` in the `headers` parameter. * To pass *headers*, use a `dict` in the `headers` parameter.
* For *cookies*, a `dict` in the `cookies` parameter. * For *cookies*, a `dict` in the `cookies` parameter.
For more information about how to pass data to the backend (using `requests` or the `TestClient`) check the <a href="https://requests.readthedocs.io" class="external-link" target="_blank">Requests documentation</a>. For more information about how to pass data to the backend (using `httpx` or the `TestClient`) check the <a href="https://www.python-httpx.org" class="external-link" target="_blank">HTTPX documentation</a>.
!!! info !!! info
Note that the `TestClient` receives data that can be converted to JSON, not Pydantic models. Note that the `TestClient` receives data that can be converted to JSON, not Pydantic models.

2
docs/es/docs/features.md

@ -167,7 +167,7 @@ Con **FastAPI** obtienes todas las características de **Starlette** (porque Fas
* Soporte para **GraphQL**. * Soporte para **GraphQL**.
* <abbr title="En español: tareas que se ejecutan en el fondo, sin frenar requests, en el mismo proceso. En ingles: In-process background tasks">Tareas en background</abbr>. * <abbr title="En español: tareas que se ejecutan en el fondo, sin frenar requests, en el mismo proceso. En ingles: In-process background tasks">Tareas en background</abbr>.
* Eventos de startup y shutdown. * Eventos de startup y shutdown.
* Cliente de pruebas construido con `requests`. * Cliente de pruebas construido con HTTPX.
* **CORS**, GZip, Static Files, Streaming responses. * **CORS**, GZip, Static Files, Streaming responses.
* Soporte para **Session and Cookie**. * Soporte para **Session and Cookie**.
* Cobertura de pruebas al 100%. * Cobertura de pruebas al 100%.

4
docs/es/docs/index.md

@ -418,7 +418,7 @@ Para un ejemplo más completo que incluye más características ve el <a href="h
* Muchas características extra (gracias a Starlette) como: * Muchas características extra (gracias a Starlette) como:
* **WebSockets** * **WebSockets**
* **GraphQL** * **GraphQL**
* pruebas extremadamente fáciles con `requests` y `pytest` * pruebas extremadamente fáciles con HTTPX y `pytest`
* **CORS** * **CORS**
* **Cookie Sessions** * **Cookie Sessions**
* ...y mucho más. * ...y mucho más.
@ -438,7 +438,7 @@ Usadas por Pydantic:
Usados por Starlette: Usados por Starlette:
* <a href="https://requests.readthedocs.io" target="_blank"><code>requests</code></a> - Requerido si quieres usar el `TestClient`. * <a href="https://www.python-httpx.org" target="_blank"><code>httpx</code></a> - Requerido si quieres usar el `TestClient`.
* <a href="https://jinja.palletsprojects.com" target="_blank"><code>jinja2</code></a> - Requerido si quieres usar la configuración por defecto de templates. * <a href="https://jinja.palletsprojects.com" target="_blank"><code>jinja2</code></a> - Requerido si quieres usar la configuración por defecto de templates.
* <a href="https://andrew-d.github.io/python-multipart/" target="_blank"><code>python-multipart</code></a> - Requerido si quieres dar soporte a <abbr title="convertir el string que viene de un HTTP request a datos de Python">"parsing"</abbr> de formularios, con `request.form()`. * <a href="https://andrew-d.github.io/python-multipart/" target="_blank"><code>python-multipart</code></a> - Requerido si quieres dar soporte a <abbr title="convertir el string que viene de un HTTP request a datos de Python">"parsing"</abbr> de formularios, con `request.form()`.
* <a href="https://pythonhosted.org/itsdangerous/" target="_blank"><code>itsdangerous</code></a> - Requerido para dar soporte a `SessionMiddleware`. * <a href="https://pythonhosted.org/itsdangerous/" target="_blank"><code>itsdangerous</code></a> - Requerido para dar soporte a `SessionMiddleware`.

4
docs/fa/docs/index.md

@ -421,7 +421,7 @@ item: Item
* قابلیت‌های اضافی دیگر (بر اساس Starlette) شامل: * قابلیت‌های اضافی دیگر (بر اساس Starlette) شامل:
* **<abbr title="WebSocket">وب‌سوکت</abbr>** * **<abbr title="WebSocket">وب‌سوکت</abbr>**
* **GraphQL** * **GraphQL**
* تست‌های خودکار آسان مبتنی بر `requests` و `pytest` * تست‌های خودکار آسان مبتنی بر HTTPX و `pytest`
* **CORS** * **CORS**
* **Cookie Sessions** * **Cookie Sessions**
* و موارد بیشمار دیگر. * و موارد بیشمار دیگر.
@ -441,7 +441,7 @@ item: Item
استفاده شده توسط Starlette: استفاده شده توسط Starlette:
* <a href="https://requests.readthedocs.io" target="_blank"><code>requests</code></a> - در صورتی که می‌خواهید از `TestClient` استفاده کنید. * <a href="https://www.python-httpx.org" target="_blank"><code>HTTPX</code></a> - در صورتی که می‌خواهید از `TestClient` استفاده کنید.
* <a href="https://github.com/Tinche/aiofiles" target="_blank"><code>aiofiles</code></a> - در صورتی که می‌خواهید از `FileResponse` و `StaticFiles` استفاده کنید. * <a href="https://github.com/Tinche/aiofiles" target="_blank"><code>aiofiles</code></a> - در صورتی که می‌خواهید از `FileResponse` و `StaticFiles` استفاده کنید.
* <a href="https://jinja.palletsprojects.com" target="_blank"><code>jinja2</code></a> - در صورتی که بخواهید از پیکربندی پیش‌فرض برای قالب‌ها استفاده کنید. * <a href="https://jinja.palletsprojects.com" target="_blank"><code>jinja2</code></a> - در صورتی که بخواهید از پیکربندی پیش‌فرض برای قالب‌ها استفاده کنید.
* <a href="https://andrew-d.github.io/python-multipart/" target="_blank"><code>python-multipart</code></a> - در صورتی که بخواهید با استفاده از `request.form()` از قابلیت <abbr title="تبدیل رشته متنی موجود در درخواست HTTP به انواع داده پایتون">"تجزیه (parse)"</abbr> فرم استفاده کنید. * <a href="https://andrew-d.github.io/python-multipart/" target="_blank"><code>python-multipart</code></a> - در صورتی که بخواهید با استفاده از `request.form()` از قابلیت <abbr title="تبدیل رشته متنی موجود در درخواست HTTP به انواع داده پایتون">"تجزیه (parse)"</abbr> فرم استفاده کنید.

240
docs/fr/docs/advanced/additional-responses.md

@ -0,0 +1,240 @@
# Réponses supplémentaires dans OpenAPI
!!! Attention
Ceci concerne un sujet plutôt avancé.
Si vous débutez avec **FastAPI**, vous n'en aurez peut-être pas besoin.
Vous pouvez déclarer des réponses supplémentaires, avec des codes HTTP, des types de médias, des descriptions, etc.
Ces réponses supplémentaires seront incluses dans le schéma OpenAPI, elles apparaîtront donc également dans la documentation de l'API.
Mais pour ces réponses supplémentaires, vous devez vous assurer de renvoyer directement une `Response` comme `JSONResponse`, avec votre code HTTP et votre contenu.
## Réponse supplémentaire avec `model`
Vous pouvez ajouter à votre décorateur de *paramètre de chemin* un paramètre `responses`.
Il prend comme valeur un `dict` dont les clés sont des codes HTTP pour chaque réponse, comme `200`, et la valeur de ces clés sont d'autres `dict` avec des informations pour chacun d'eux.
Chacun de ces `dict` de réponse peut avoir une clé `model`, contenant un modèle Pydantic, tout comme `response_model`.
**FastAPI** prendra ce modèle, générera son schéma JSON et l'inclura au bon endroit dans OpenAPI.
Par exemple, pour déclarer une autre réponse avec un code HTTP `404` et un modèle Pydantic `Message`, vous pouvez écrire :
```Python hl_lines="18 22"
{!../../../docs_src/additional_responses/tutorial001.py!}
```
!!! Remarque
Gardez à l'esprit que vous devez renvoyer directement `JSONResponse`.
!!! Info
La clé `model` ne fait pas partie d'OpenAPI.
**FastAPI** prendra le modèle Pydantic à partir de là, générera le `JSON Schema` et le placera au bon endroit.
Le bon endroit est :
* Dans la clé `content`, qui a pour valeur un autre objet JSON (`dict`) qui contient :
* Une clé avec le type de support, par ex. `application/json`, qui contient comme valeur un autre objet JSON, qui contient :
* Une clé `schema`, qui a pour valeur le schéma JSON du modèle, voici le bon endroit.
* **FastAPI** ajoute ici une référence aux schémas JSON globaux à un autre endroit de votre OpenAPI au lieu de l'inclure directement. De cette façon, d'autres applications et clients peuvent utiliser ces schémas JSON directement, fournir de meilleurs outils de génération de code, etc.
Les réponses générées au format OpenAPI pour cette *opération de chemin* seront :
```JSON hl_lines="3-12"
{
"responses": {
"404": {
"description": "Additional Response",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Message"
}
}
}
},
"200": {
"description": "Successful Response",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Item"
}
}
}
},
"422": {
"description": "Validation Error",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/HTTPValidationError"
}
}
}
}
}
}
```
Les schémas sont référencés à un autre endroit du modèle OpenAPI :
```JSON hl_lines="4-16"
{
"components": {
"schemas": {
"Message": {
"title": "Message",
"required": [
"message"
],
"type": "object",
"properties": {
"message": {
"title": "Message",
"type": "string"
}
}
},
"Item": {
"title": "Item",
"required": [
"id",
"value"
],
"type": "object",
"properties": {
"id": {
"title": "Id",
"type": "string"
},
"value": {
"title": "Value",
"type": "string"
}
}
},
"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"
}
}
}
}
}
}
}
```
## Types de médias supplémentaires pour la réponse principale
Vous pouvez utiliser ce même paramètre `responses` pour ajouter différents types de médias pour la même réponse principale.
Par exemple, vous pouvez ajouter un type de média supplémentaire `image/png`, en déclarant que votre *opération de chemin* peut renvoyer un objet JSON (avec le type de média `application/json`) ou une image PNG :
```Python hl_lines="19-24 28"
{!../../../docs_src/additional_responses/tutorial002.py!}
```
!!! Remarque
Notez que vous devez retourner l'image en utilisant directement un `FileResponse`.
!!! Info
À moins que vous ne spécifiiez explicitement un type de média différent dans votre paramètre `responses`, FastAPI supposera que la réponse a le même type de média que la classe de réponse principale (par défaut `application/json`).
Mais si vous avez spécifié une classe de réponse personnalisée avec `None` comme type de média, FastAPI utilisera `application/json` pour toute réponse supplémentaire associée à un modèle.
## Combinaison d'informations
Vous pouvez également combiner des informations de réponse provenant de plusieurs endroits, y compris les paramètres `response_model`, `status_code` et `responses`.
Vous pouvez déclarer un `response_model`, en utilisant le code HTTP par défaut `200` (ou un code personnalisé si vous en avez besoin), puis déclarer des informations supplémentaires pour cette même réponse dans `responses`, directement dans le schéma OpenAPI.
**FastAPI** conservera les informations supplémentaires des `responses` et les combinera avec le schéma JSON de votre modèle.
Par exemple, vous pouvez déclarer une réponse avec un code HTTP `404` qui utilise un modèle Pydantic et a une `description` personnalisée.
Et une réponse avec un code HTTP `200` qui utilise votre `response_model`, mais inclut un `example` personnalisé :
```Python hl_lines="20-31"
{!../../../docs_src/additional_responses/tutorial003.py!}
```
Tout sera combiné et inclus dans votre OpenAPI, et affiché dans la documentation de l'API :
<img src="/img/tutorial/additional-responses/image01.png">
## Combinez les réponses prédéfinies et les réponses personnalisées
Vous voulez peut-être avoir des réponses prédéfinies qui s'appliquent à de nombreux *paramètre de chemin*, mais vous souhaitez les combiner avec des réponses personnalisées nécessaires à chaque *opération de chemin*.
Dans ces cas, vous pouvez utiliser la technique Python "d'affection par décomposition" (appelé _unpacking_ en anglais) d'un `dict` avec `**dict_to_unpack` :
``` Python
old_dict = {
"old key": "old value",
"second old key": "second old value",
}
new_dict = {**old_dict, "new key": "new value"}
```
Ici, `new_dict` contiendra toutes les paires clé-valeur de `old_dict` plus la nouvelle paire clé-valeur :
``` Python
{
"old key": "old value",
"second old key": "second old value",
"new key": "new value",
}
```
Vous pouvez utiliser cette technique pour réutiliser certaines réponses prédéfinies dans vos *paramètres de chemin* et les combiner avec des réponses personnalisées supplémentaires.
Par exemple:
```Python hl_lines="13-17 26"
{!../../../docs_src/additional_responses/tutorial004.py!}
```
## Plus d'informations sur les réponses OpenAPI
Pour voir exactement ce que vous pouvez inclure dans les réponses, vous pouvez consulter ces sections dans la spécification OpenAPI :
* <a href="https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.2.md#responsesObject" class="external-link" target="_blank">Objet Responses de OpenAPI </a>, il inclut le `Response Object`.
* <a href="https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.2.md#responseObject" class="external-link" target="_blank">Objet Response de OpenAPI </a>, vous pouvez inclure n'importe quoi directement dans chaque réponse à l'intérieur de votre paramètre `responses`. Y compris `description`, `headers`, `content` (à l'intérieur de cela, vous déclarez différents types de médias et schémas JSON) et `links`.

37
docs/fr/docs/advanced/additional-status-codes.md

@ -0,0 +1,37 @@
# Codes HTTP supplémentaires
Par défaut, **FastAPI** renverra les réponses à l'aide d'une structure de données `JSONResponse`, en plaçant la réponse de votre *chemin d'accès* à l'intérieur de cette `JSONResponse`.
Il utilisera le code HTTP par défaut ou celui que vous avez défini dans votre *chemin d'accès*.
## Codes HTTP supplémentaires
Si vous souhaitez renvoyer des codes HTTP supplémentaires en plus du code principal, vous pouvez le faire en renvoyant directement une `Response`, comme une `JSONResponse`, et en définissant directement le code HTTP supplémentaire.
Par exemple, disons que vous voulez avoir un *chemin d'accès* qui permet de mettre à jour les éléments et renvoie les codes HTTP 200 "OK" en cas de succès.
Mais vous voulez aussi qu'il accepte de nouveaux éléments. Et lorsque les éléments n'existaient pas auparavant, il les crée et renvoie un code HTTP de 201 "Créé".
Pour y parvenir, importez `JSONResponse` et renvoyez-y directement votre contenu, en définissant le `status_code` que vous souhaitez :
```Python hl_lines="4 25"
{!../../../docs_src/additional_status_codes/tutorial001.py!}
```
!!! Attention
Lorsque vous renvoyez une `Response` directement, comme dans l'exemple ci-dessus, elle sera renvoyée directement.
Elle ne sera pas sérialisée avec un modèle.
Assurez-vous qu'il contient les données souhaitées et que les valeurs soient dans un format JSON valides (si vous utilisez une `JSONResponse`).
!!! note "Détails techniques"
Vous pouvez également utiliser `from starlette.responses import JSONResponse`.
Pour plus de commodités, **FastAPI** fournit les objets `starlette.responses` sous forme d'un alias accessible par `fastapi.responses`. Mais la plupart des réponses disponibles proviennent directement de Starlette. Il en est de même avec l'objet `statut`.
## Documents OpenAPI et API
Si vous renvoyez directement des codes HTTP et des réponses supplémentaires, ils ne seront pas inclus dans le schéma OpenAPI (la documentation de l'API), car FastAPI n'a aucun moyen de savoir à l'avance ce que vous allez renvoyer.
Mais vous pouvez documenter cela dans votre code, en utilisant : [Réponses supplémentaires dans OpenAPI](additional-responses.md){.internal-link target=_blank}.

4
docs/fr/docs/index.md

@ -426,7 +426,7 @@ For a more complete example including more features, see the <a href="https://fa
* Many extra features (thanks to Starlette) as: * Many extra features (thanks to Starlette) as:
* **WebSockets** * **WebSockets**
* **GraphQL** * **GraphQL**
* extremely easy tests based on `requests` and `pytest` * extremely easy tests based on HTTPX and `pytest`
* **CORS** * **CORS**
* **Cookie Sessions** * **Cookie Sessions**
* ...and more. * ...and more.
@ -446,7 +446,7 @@ Used by Pydantic:
Used by Starlette: Used by Starlette:
* <a href="https://requests.readthedocs.io" target="_blank"><code>requests</code></a> - Required if you want to use the `TestClient`. * <a href="https://www.python-httpx.org" target="_blank"><code>HTTPX</code></a> - Required if you want to use the `TestClient`.
* <a href="https://jinja.palletsprojects.com" target="_blank"><code>jinja2</code></a> - Required if you want to use the default template configuration. * <a href="https://jinja.palletsprojects.com" target="_blank"><code>jinja2</code></a> - Required if you want to use the default template configuration.
* <a href="https://andrew-d.github.io/python-multipart/" target="_blank"><code>python-multipart</code></a> - Required if you want to support form <abbr title="converting the string that comes from an HTTP request into Python data">"parsing"</abbr>, with `request.form()`. * <a href="https://andrew-d.github.io/python-multipart/" target="_blank"><code>python-multipart</code></a> - Required if you want to support form <abbr title="converting the string that comes from an HTTP request into Python data">"parsing"</abbr>, with `request.form()`.
* <a href="https://pythonhosted.org/itsdangerous/" target="_blank"><code>itsdangerous</code></a> - Required for `SessionMiddleware` support. * <a href="https://pythonhosted.org/itsdangerous/" target="_blank"><code>itsdangerous</code></a> - Required for `SessionMiddleware` support.

3
docs/fr/mkdocs.yml

@ -67,6 +67,9 @@ nav:
- tutorial/query-params.md - tutorial/query-params.md
- tutorial/body.md - tutorial/body.md
- tutorial/background-tasks.md - tutorial/background-tasks.md
- Guide utilisateur avancé:
- advanced/additional-status-codes.md
- advanced/additional-responses.md
- async.md - async.md
- Déploiement: - Déploiement:
- deployment/index.md - deployment/index.md

2
docs/he/docs/index.md

@ -445,7 +445,7 @@ item: Item
בשימוש Starlette: בשימוש Starlette:
- <a href="https://requests.readthedocs.io" target="_blank"><code>requests</code></a> - דרוש אם ברצונכם להשתמש ב - `TestClient`. - <a href="https://www.python-httpx.org" target="_blank"><code>httpx</code></a> - דרוש אם ברצונכם להשתמש ב - `TestClient`.
- <a href="https://jinja.palletsprojects.com" target="_blank"><code>jinja2</code></a> - דרוש אם ברצונכם להשתמש בברירת המחדל של תצורת הטמפלייטים. - <a href="https://jinja.palletsprojects.com" target="_blank"><code>jinja2</code></a> - דרוש אם ברצונכם להשתמש בברירת המחדל של תצורת הטמפלייטים.
- <a href="https://andrew-d.github.io/python-multipart/" target="_blank"><code>python-multipart</code></a> - דרוש אם ברצונכם לתמוך ב <abbr title="המרת המחרוזת שמגיעה מבקשת HTTP למידע פייתון">"פרסור"</abbr> טפסים, באצמעות <code dir="ltr">request.form()</code>. - <a href="https://andrew-d.github.io/python-multipart/" target="_blank"><code>python-multipart</code></a> - דרוש אם ברצונכם לתמוך ב <abbr title="המרת המחרוזת שמגיעה מבקשת HTTP למידע פייתון">"פרסור"</abbr> טפסים, באצמעות <code dir="ltr">request.form()</code>.
- <a href="https://pythonhosted.org/itsdangerous/" target="_blank"><code>itsdangerous</code></a> - דרוש אם ברצונכם להשתמש ב - `SessionMiddleware`. - <a href="https://pythonhosted.org/itsdangerous/" target="_blank"><code>itsdangerous</code></a> - דרוש אם ברצונכם להשתמש ב - `SessionMiddleware`.

4
docs/id/docs/index.md

@ -426,7 +426,7 @@ For a more complete example including more features, see the <a href="https://fa
* Many extra features (thanks to Starlette) as: * Many extra features (thanks to Starlette) as:
* **WebSockets** * **WebSockets**
* **GraphQL** * **GraphQL**
* extremely easy tests based on `requests` and `pytest` * extremely easy tests based on HTTPX and `pytest`
* **CORS** * **CORS**
* **Cookie Sessions** * **Cookie Sessions**
* ...and more. * ...and more.
@ -446,7 +446,7 @@ Used by Pydantic:
Used by Starlette: Used by Starlette:
* <a href="https://requests.readthedocs.io" target="_blank"><code>requests</code></a> - Required if you want to use the `TestClient`. * <a href="https://www.python-httpx.org" target="_blank"><code>httpx</code></a> - Required if you want to use the `TestClient`.
* <a href="https://jinja.palletsprojects.com" target="_blank"><code>jinja2</code></a> - Required if you want to use the default template configuration. * <a href="https://jinja.palletsprojects.com" target="_blank"><code>jinja2</code></a> - Required if you want to use the default template configuration.
* <a href="https://andrew-d.github.io/python-multipart/" target="_blank"><code>python-multipart</code></a> - Required if you want to support form <abbr title="converting the string that comes from an HTTP request into Python data">"parsing"</abbr>, with `request.form()`. * <a href="https://andrew-d.github.io/python-multipart/" target="_blank"><code>python-multipart</code></a> - Required if you want to support form <abbr title="converting the string that comes from an HTTP request into Python data">"parsing"</abbr>, with `request.form()`.
* <a href="https://pythonhosted.org/itsdangerous/" target="_blank"><code>itsdangerous</code></a> - Required for `SessionMiddleware` support. * <a href="https://pythonhosted.org/itsdangerous/" target="_blank"><code>itsdangerous</code></a> - Required for `SessionMiddleware` support.

4
docs/it/docs/index.md

@ -423,7 +423,7 @@ For a more complete example including more features, see the <a href="https://fa
* Many extra features (thanks to Starlette) as: * Many extra features (thanks to Starlette) as:
* **WebSockets** * **WebSockets**
* **GraphQL** * **GraphQL**
* extremely easy tests based on `requests` and `pytest` * extremely easy tests based on HTTPX and `pytest`
* **CORS** * **CORS**
* **Cookie Sessions** * **Cookie Sessions**
* ...and more. * ...and more.
@ -443,7 +443,7 @@ Used by Pydantic:
Used by Starlette: Used by Starlette:
* <a href="https://requests.readthedocs.io" target="_blank"><code>requests</code></a> - Required if you want to use the `TestClient`. * <a href="https://www.python-httpx.org" target="_blank"><code>httpx</code></a> - Required if you want to use the `TestClient`.
* <a href="https://jinja.palletsprojects.com" target="_blank"><code>jinja2</code></a> - Required if you want to use the default template configuration. * <a href="https://jinja.palletsprojects.com" target="_blank"><code>jinja2</code></a> - Required if you want to use the default template configuration.
* <a href="https://andrew-d.github.io/python-multipart/" target="_blank"><code>python-multipart</code></a> - Required if you want to support form <abbr title="converting the string that comes from an HTTP request into Python data">"parsing"</abbr>, with `request.form()`. * <a href="https://andrew-d.github.io/python-multipart/" target="_blank"><code>python-multipart</code></a> - Required if you want to support form <abbr title="converting the string that comes from an HTTP request into Python data">"parsing"</abbr>, with `request.form()`.
* <a href="https://pythonhosted.org/itsdangerous/" target="_blank"><code>itsdangerous</code></a> - Required for `SessionMiddleware` support. * <a href="https://pythonhosted.org/itsdangerous/" target="_blank"><code>itsdangerous</code></a> - Required for `SessionMiddleware` support.

186
docs/ja/docs/advanced/websockets.md

@ -0,0 +1,186 @@
# WebSocket
**FastAPI**で<a href="https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API" class="external-link" target="_blank">WebSocket</a>が使用できます。
## `WebSockets`のインストール
まず `WebSockets`のインストールが必要です。
<div class="termy">
```console
$ pip install websockets
---> 100%
```
</div>
## WebSocket クライアント
### 本番環境
本番環境では、React、Vue.js、Angularなどの最新のフレームワークで作成されたフロントエンドを使用しているでしょう。
そして、バックエンドとWebSocketを使用して通信するために、おそらくフロントエンドのユーティリティを使用することになるでしょう。
または、ネイティブコードでWebSocketバックエンドと直接通信するネイティブモバイルアプリケーションがあるかもしれません。
他にも、WebSocketのエンドポイントと通信する方法があるかもしれません。
---
ただし、この例では非常にシンプルなHTML文書といくつかのJavaScriptを、すべてソースコードの中に入れて使用することにします。
もちろん、これは最適な方法ではありませんし、本番環境で使うことはないでしょう。
本番環境では、上記の方法のいずれかの選択肢を採用することになるでしょう。
しかし、これはWebSocketのサーバーサイドに焦点を当て、実用的な例を示す最も簡単な方法です。
```Python hl_lines="2 6-38 41-43"
{!../../../docs_src/websockets/tutorial001.py!}
```
## `websocket` を作成する
**FastAPI** アプリケーションで、`websocket` を作成します。
```Python hl_lines="1 46-47"
{!../../../docs_src/websockets/tutorial001.py!}
```
!!! note "技術詳細"
`from starlette.websockets import WebSocket` を使用しても構いません.
**FastAPI** は開発者の利便性のために、同じ `WebSocket` を提供します。しかし、こちらはStarletteから直接提供されるものです。
## メッセージの送受信
WebSocketルートでは、 `await` を使ってメッセージの送受信ができます。
```Python hl_lines="48-52"
{!../../../docs_src/websockets/tutorial001.py!}
```
バイナリやテキストデータ、JSONデータを送受信できます。
## 試してみる
ファイル名が `main.py` である場合、以下の方法でアプリケーションを実行します。
<div class="termy">
```console
$ uvicorn main:app --reload
<span style="color: green;">INFO</span>: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
```
</div>
ブラウザで <a href="http://127.0.0.1:8000" class="external-link" target="_blank">http://127.0.0.1:8000</a> を開きます。
次のようなシンプルなページが表示されます。
<img src="/img/tutorial/websockets/image01.png">
入力ボックスにメッセージを入力して送信できます。
<img src="/img/tutorial/websockets/image02.png">
そして、 WebSocketを使用した**FastAPI**アプリケーションが応答します。
<img src="/img/tutorial/websockets/image03.png">
複数のメッセージを送信(および受信)できます。
<img src="/img/tutorial/websockets/image04.png">
そして、これらの通信はすべて同じWebSocket接続を使用します。
## 依存関係
WebSocketエンドポイントでは、`fastapi` から以下をインポートして使用できます。
* `Depends`
* `Security`
* `Cookie`
* `Header`
* `Path`
* `Query`
これらは、他のFastAPI エンドポイント/*path operation* の場合と同じように機能します。
```Python hl_lines="58-65 68-83"
{!../../../docs_src/websockets/tutorial002.py!}
```
!!! info "情報"
WebSocket で `HTTPException` を発生させることはあまり意味がありません。したがって、WebSocketの接続を直接閉じる方がよいでしょう。
クロージングコードは、<a href="https://tools.ietf.org/html/rfc6455#section-7.4.1" class="external-link" target="_blank">仕様で定義された有効なコード</a>の中から使用することができます。
将来的には、どこからでも `raise` できる `WebSocketException` が用意され、専用の例外ハンドラを追加できるようになる予定です。これは、Starlette の <a href="https://github.com/encode/starlette/pull/527" class="external-link" target="_blank">PR #527</a> に依存するものです。
### 依存関係を用いてWebSocketsを試してみる
ファイル名が `main.py` である場合、以下の方法でアプリケーションを実行します。
<div class="termy">
```console
$ uvicorn main:app --reload
<span style="color: green;">INFO</span>: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
```
</div>
ブラウザで <a href="http://127.0.0.1:8000" class="external-link" target="_blank">http://127.0.0.1:8000</a> を開きます。
クライアントが設定できる項目は以下の通りです。
* パスで使用される「Item ID」
* クエリパラメータとして使用される「Token」
!!! tip "豆知識"
クエリ `token` は依存パッケージによって処理されることに注意してください。
これにより、WebSocketに接続してメッセージを送受信できます。
<img src="/img/tutorial/websockets/image05.png">
## 切断や複数クライアントへの対応
WebSocket接続が閉じられると、 `await websocket.receive_text()` は例外 `WebSocketDisconnect` を発生させ、この例のようにキャッチして処理することができます。
```Python hl_lines="81-83"
{!../../../docs_src/websockets/tutorial003.py!}
```
試してみるには、
* いくつかのブラウザタブでアプリを開きます。
* それらのタブでメッセージを記入してください。
* そして、タブのうち1つを閉じてください。
これにより例外 `WebSocketDisconnect` が発生し、他のすべてのクライアントは次のようなメッセージを受信します。
```
Client #1596980209979 left the chat
```
!!! tip "豆知識"
上記のアプリは、複数の WebSocket 接続に対してメッセージを処理し、ブロードキャストする方法を示すための最小限のシンプルな例です。
しかし、すべての接続がメモリ内の単一のリストで処理されるため、プロセスの実行中にのみ機能し、単一のプロセスでのみ機能することに注意してください。
もしFastAPIと簡単に統合できて、RedisやPostgreSQLなどでサポートされている、より堅牢なものが必要なら、<a href="https://github.com/encode/broadcaster" class="external-link" target="_blank">encode/broadcaster</a> を確認してください。
## その他のドキュメント
オプションの詳細については、Starletteのドキュメントを確認してください。
* <a href="https://www.starlette.io/websockets/" class="external-link" target="_blank"> `WebSocket` クラス</a>
* <a href="https://www.starlette.io/endpoints/#websocketendpoint" class="external-link" target="_blank">クラスベースのWebSocket処理</a>

2
docs/ja/docs/features.md

@ -169,7 +169,7 @@ FastAPIには非常に使いやすく、非常に強力な<abbr title='also know
* **GraphQL**のサポート * **GraphQL**のサポート
* プロセス内バックグラウンドタスク * プロセス内バックグラウンドタスク
* 起動およびシャットダウンイベント * 起動およびシャットダウンイベント
* `requests`に基づいて構築されたテストクライアント * `httpx`に基づいて構築されたテストクライアント
* **CORS**、GZip、静的ファイル、ストリーミング応答 * **CORS**、GZip、静的ファイル、ストリーミング応答
* **セッションとCookie**のサポート * **セッションとCookie**のサポート
* テストカバレッジ100% * テストカバレッジ100%

4
docs/ja/docs/index.md

@ -416,7 +416,7 @@ item: Item
- 以下のようなたくさんのおまけ機能(Starlette のおかげです): - 以下のようなたくさんのおまけ機能(Starlette のおかげです):
- **WebSockets** - **WebSockets**
- **GraphQL** - **GraphQL**
- `requests``pytest`をもとにした極限に簡単なテスト - `httpx``pytest`をもとにした極限に簡単なテスト
- **CORS** - **CORS**
- **クッキーセッション** - **クッキーセッション**
- ...などなど。 - ...などなど。
@ -436,7 +436,7 @@ Pydantic によって使用されるもの:
Starlette によって使用されるもの: Starlette によって使用されるもの:
- <a href="https://requests.readthedocs.io" target="_blank"><code>requests</code></a> - `TestClient`を使用するために必要です。 - <a href="https://www.python-httpx.org" target="_blank"><code>httpx</code></a> - `TestClient`を使用するために必要です。
- <a href="https://jinja.palletsprojects.com" target="_blank"><code>jinja2</code></a> - デフォルトのテンプレート設定を使用する場合は必要です。 - <a href="https://jinja.palletsprojects.com" target="_blank"><code>jinja2</code></a> - デフォルトのテンプレート設定を使用する場合は必要です。
- <a href="https://andrew-d.github.io/python-multipart/" target="_blank"><code>python-multipart</code></a> - <abbr title="converting the string that comes from an HTTP request into Python data">"parsing"</abbr>`request.form()`からの変換をサポートしたい場合は必要です。 - <a href="https://andrew-d.github.io/python-multipart/" target="_blank"><code>python-multipart</code></a> - <abbr title="converting the string that comes from an HTTP request into Python data">"parsing"</abbr>`request.form()`からの変換をサポートしたい場合は必要です。
- <a href="https://pythonhosted.org/itsdangerous/" target="_blank"><code>itsdangerous</code></a> - `SessionMiddleware` サポートのためには必要です。 - <a href="https://pythonhosted.org/itsdangerous/" target="_blank"><code>itsdangerous</code></a> - `SessionMiddleware` サポートのためには必要です。

8
docs/ja/docs/tutorial/query-params.md

@ -64,7 +64,7 @@ http://127.0.0.1:8000/items/?skip=20
同様に、デフォルト値を `None` とすることで、オプショナルなクエリパラメータを宣言できます: 同様に、デフォルト値を `None` とすることで、オプショナルなクエリパラメータを宣言できます:
```Python hl_lines="7" ```Python hl_lines="9"
{!../../../docs_src/query_params/tutorial002.py!} {!../../../docs_src/query_params/tutorial002.py!}
``` ```
@ -82,7 +82,7 @@ http://127.0.0.1:8000/items/?skip=20
`bool` 型も宣言できます。これは以下の様に変換されます: `bool` 型も宣言できます。これは以下の様に変換されます:
```Python hl_lines="7" ```Python hl_lines="9"
{!../../../docs_src/query_params/tutorial003.py!} {!../../../docs_src/query_params/tutorial003.py!}
``` ```
@ -126,7 +126,7 @@ http://127.0.0.1:8000/items/foo?short=yes
名前で判別されます: 名前で判別されます:
```Python hl_lines="6 8" ```Python hl_lines="8 10"
{!../../../docs_src/query_params/tutorial004.py!} {!../../../docs_src/query_params/tutorial004.py!}
``` ```
@ -184,7 +184,7 @@ http://127.0.0.1:8000/items/foo-item?needy=sooooneedy
そして当然、あるパラメータを必須に、別のパラメータにデフォルト値を設定し、また別のパラメータをオプショナルにできます: そして当然、あるパラメータを必須に、別のパラメータにデフォルト値を設定し、また別のパラメータをオプショナルにできます:
```Python hl_lines="7" ```Python hl_lines="10"
{!../../../docs_src/query_params/tutorial006.py!} {!../../../docs_src/query_params/tutorial006.py!}
``` ```

8
docs/ja/docs/tutorial/testing.md

@ -2,7 +2,7 @@
<a href="https://www.starlette.io/testclient/" class="external-link" target="_blank">Starlette</a> のおかげで、**FastAPI** アプリケーションのテストは簡単で楽しいものになっています。 <a href="https://www.starlette.io/testclient/" class="external-link" target="_blank">Starlette</a> のおかげで、**FastAPI** アプリケーションのテストは簡単で楽しいものになっています。
<a href="http://docs.python-requests.org" class="external-link" target="_blank">Requests</a> がベースなので、非常に使いやすく直感的です。 <a href="https://www.python-httpx.org" class="external-link" target="_blank">HTTPX</a> がベースなので、非常に使いやすく直感的です。
これを使用すると、**FastAPI** と共に <a href="https://docs.pytest.org/" class="external-link" target="_blank">pytest</a> を直接利用できます。 これを使用すると、**FastAPI** と共に <a href="https://docs.pytest.org/" class="external-link" target="_blank">pytest</a> を直接利用できます。
@ -14,7 +14,7 @@
`test_` から始まる名前の関数を作成します (これは `pytest` の標準的なコンベンションです)。 `test_` から始まる名前の関数を作成します (これは `pytest` の標準的なコンベンションです)。
`requests` と同じ様に `TestClient` オブジェクトを使用します。 `httpx` と同じ様に `TestClient` オブジェクトを使用します。
チェックしたい Python の標準的な式と共に、シンプルに `assert` 文を記述します。 チェックしたい Python の標準的な式と共に、シンプルに `assert` 文を記述します。
@ -94,7 +94,7 @@
{!> ../../../docs_src/app_testing/app_b/test_main.py!} {!> ../../../docs_src/app_testing/app_b/test_main.py!}
``` ```
リクエストに情報を渡せるクライアントが必要で、その方法がわからない場合はいつでも、`requests` での実現方法を検索 (Google) できます。 リクエストに情報を渡せるクライアントが必要で、その方法がわからない場合はいつでも、`httpx` での実現方法を検索 (Google) できます。
テストでも同じことを行います。 テストでも同じことを行います。
@ -106,7 +106,7 @@
* *ヘッダー* を渡すには、`headers` パラメータに `dict` を渡します。 * *ヘッダー* を渡すには、`headers` パラメータに `dict` を渡します。
* *cookies* の場合、 `cookies` パラメータに `dict` です。 * *cookies* の場合、 `cookies` パラメータに `dict` です。
(`requests` または `TestClient` を使用して) バックエンドにデータを渡す方法の詳細は、<a href="http://docs.python-requests.org" class="external-link" target="_blank">Requestsのドキュメント</a>を確認してください。 (`httpx` または `TestClient` を使用して) バックエンドにデータを渡す方法の詳細は、<a href="https://www.python-httpx.org" class="external-link" target="_blank">HTTPXのドキュメント</a>を確認してください。
!!! info "情報" !!! info "情報"
`TestClient` は、Pydanticモデルではなく、JSONに変換できるデータを受け取ることに注意してください。 `TestClient` は、Pydanticモデルではなく、JSONに変換できるデータを受け取ることに注意してください。

1
docs/ja/mkdocs.yml

@ -86,6 +86,7 @@ nav:
- advanced/response-directly.md - advanced/response-directly.md
- advanced/custom-response.md - advanced/custom-response.md
- advanced/nosql-databases.md - advanced/nosql-databases.md
- advanced/websockets.md
- advanced/conditional-openapi.md - advanced/conditional-openapi.md
- async.md - async.md
- デプロイ: - デプロイ:

4
docs/ko/docs/index.md

@ -422,7 +422,7 @@ item: Item
* (Starlette 덕분에) 많은 추가 기능: * (Starlette 덕분에) 많은 추가 기능:
* **웹 소켓** * **웹 소켓**
* **GraphQL** * **GraphQL**
* `requests``pytest`에 기반한 극히 쉬운 테스트 * HTTPX`pytest`에 기반한 극히 쉬운 테스트
* **CORS** * **CORS**
* **쿠키 세션** * **쿠키 세션**
* ...기타 등등. * ...기타 등등.
@ -442,7 +442,7 @@ Pydantic이 사용하는:
Starlette이 사용하는: Starlette이 사용하는:
* <a href="http://docs.python-requests.org" target="_blank"><code>requests</code></a> - `TestClient`를 사용하려면 필요. * <a href="https://www.python-httpx.org" target="_blank"><code>HTTPX</code></a> - `TestClient`를 사용하려면 필요.
* <a href="http://jinja.pocoo.org" target="_blank"><code>jinja2</code></a> - 기본 템플릿 설정을 사용하려면 필요. * <a href="http://jinja.pocoo.org" target="_blank"><code>jinja2</code></a> - 기본 템플릿 설정을 사용하려면 필요.
* <a href="https://andrew-d.github.io/python-multipart/" target="_blank"><code>python-multipart</code></a> - `request.form()`과 함께 <abbr title="HTTP 요청에서 파이썬 데이터로 가는 문자열 변환">"parsing"</abbr>의 지원을 원하면 필요. * <a href="https://andrew-d.github.io/python-multipart/" target="_blank"><code>python-multipart</code></a> - `request.form()`과 함께 <abbr title="HTTP 요청에서 파이썬 데이터로 가는 문자열 변환">"parsing"</abbr>의 지원을 원하면 필요.
* <a href="https://pythonhosted.org/itsdangerous/" target="_blank"><code>itsdangerous</code></a> - `SessionMiddleware` 지원을 위해 필요. * <a href="https://pythonhosted.org/itsdangerous/" target="_blank"><code>itsdangerous</code></a> - `SessionMiddleware` 지원을 위해 필요.

4
docs/nl/docs/index.md

@ -429,7 +429,7 @@ For a more complete example including more features, see the <a href="https://fa
* **GraphQL** integration with <a href="https://strawberry.rocks" class="external-link" target="_blank">Strawberry</a> and other libraries. * **GraphQL** integration with <a href="https://strawberry.rocks" class="external-link" target="_blank">Strawberry</a> and other libraries.
* Many extra features (thanks to Starlette) as: * Many extra features (thanks to Starlette) as:
* **WebSockets** * **WebSockets**
* extremely easy tests based on `requests` and `pytest` * extremely easy tests based on HTTPX and `pytest`
* **CORS** * **CORS**
* **Cookie Sessions** * **Cookie Sessions**
* ...and more. * ...and more.
@ -449,7 +449,7 @@ Used by Pydantic:
Used by Starlette: Used by Starlette:
* <a href="https://requests.readthedocs.io" target="_blank"><code>requests</code></a> - Required if you want to use the `TestClient`. * <a href="https://www.python-httpx.org" target="_blank"><code>httpx</code></a> - Required if you want to use the `TestClient`.
* <a href="https://jinja.palletsprojects.com" target="_blank"><code>jinja2</code></a> - Required if you want to use the default template configuration. * <a href="https://jinja.palletsprojects.com" target="_blank"><code>jinja2</code></a> - Required if you want to use the default template configuration.
* <a href="https://andrew-d.github.io/python-multipart/" target="_blank"><code>python-multipart</code></a> - Required if you want to support form <abbr title="converting the string that comes from an HTTP request into Python data">"parsing"</abbr>, with `request.form()`. * <a href="https://andrew-d.github.io/python-multipart/" target="_blank"><code>python-multipart</code></a> - Required if you want to support form <abbr title="converting the string that comes from an HTTP request into Python data">"parsing"</abbr>, with `request.form()`.
* <a href="https://pythonhosted.org/itsdangerous/" target="_blank"><code>itsdangerous</code></a> - Required for `SessionMiddleware` support. * <a href="https://pythonhosted.org/itsdangerous/" target="_blank"><code>itsdangerous</code></a> - Required for `SessionMiddleware` support.

4
docs/pl/docs/index.md

@ -420,7 +420,7 @@ Dla bardziej kompletnych przykładów posiadających więcej funkcjonalności, z
* Wiele dodatkowych funkcji (dzięki Starlette) takie jak: * Wiele dodatkowych funkcji (dzięki Starlette) takie jak:
* **WebSockety** * **WebSockety**
* **GraphQL** * **GraphQL**
* bardzo proste testy bazujące na `requests` oraz `pytest` * bardzo proste testy bazujące na HTTPX oraz `pytest`
* **CORS** * **CORS**
* **Sesje cookie** * **Sesje cookie**
* ...i więcej. * ...i więcej.
@ -440,7 +440,7 @@ Używane przez Pydantic:
Używane przez Starlette: Używane przez Starlette:
* <a href="https://requests.readthedocs.io" target="_blank"><code>requests</code></a> - Wymagane jeżeli chcesz korzystać z `TestClient`. * <a href="https://www.python-httpx.org" target="_blank"><code>httpx</code></a> - Wymagane jeżeli chcesz korzystać z `TestClient`.
* <a href="https://github.com/Tinche/aiofiles" target="_blank"><code>aiofiles</code></a> - Wymagane jeżeli chcesz korzystać z `FileResponse` albo `StaticFiles`. * <a href="https://github.com/Tinche/aiofiles" target="_blank"><code>aiofiles</code></a> - Wymagane jeżeli chcesz korzystać z `FileResponse` albo `StaticFiles`.
* <a href="https://jinja.palletsprojects.com" target="_blank"><code>jinja2</code></a> - Wymagane jeżeli chcesz używać domyślnej konfiguracji szablonów. * <a href="https://jinja.palletsprojects.com" target="_blank"><code>jinja2</code></a> - Wymagane jeżeli chcesz używać domyślnej konfiguracji szablonów.
* <a href="https://andrew-d.github.io/python-multipart/" target="_blank"><code>python-multipart</code></a> - Wymagane jeżelich chcesz wsparcie <abbr title="przetwarzania stringa którzy przychodzi z żądaniem HTTP na dane używane przez Pythona">"parsowania"</abbr> formularzy, używając `request.form()`. * <a href="https://andrew-d.github.io/python-multipart/" target="_blank"><code>python-multipart</code></a> - Wymagane jeżelich chcesz wsparcie <abbr title="przetwarzania stringa którzy przychodzi z żądaniem HTTP na dane używane przez Pythona">"parsowania"</abbr> formularzy, używając `request.form()`.

2
docs/pt/docs/features.md

@ -167,7 +167,7 @@ Com **FastAPI**, você terá todos os recursos do **Starlette** (já que FastAPI
* Suporte a **GraphQL**. * Suporte a **GraphQL**.
* Tarefas em processo _background_. * Tarefas em processo _background_.
* Eventos na inicialização e encerramento. * Eventos na inicialização e encerramento.
* Cliente de testes construído sobre `requests`. * Cliente de testes construído sobre HTTPX.
* Respostas em **CORS**, GZip, Static Files, Streaming. * Respostas em **CORS**, GZip, Static Files, Streaming.
* Suporte a **Session e Cookie**. * Suporte a **Session e Cookie**.
* 100% de cobertura de testes. * 100% de cobertura de testes.

4
docs/pt/docs/index.md

@ -415,7 +415,7 @@ Para um exemplo mais completo incluindo mais recursos, veja <a href="https://fas
* Muitos recursos extras (graças ao Starlette) como: * Muitos recursos extras (graças ao Starlette) como:
* **WebSockets** * **WebSockets**
* **GraphQL** * **GraphQL**
* testes extrememamente fáceis baseados em `requests` e `pytest` * testes extrememamente fáceis baseados em HTTPX e `pytest`
* **CORS** * **CORS**
* **Cookie Sessions** * **Cookie Sessions**
* ...e mais. * ...e mais.
@ -435,7 +435,7 @@ Usados por Pydantic:
Usados por Starlette: Usados por Starlette:
* <a href="https://requests.readthedocs.io" target="_blank"><code>requests</code></a> - Necessário se você quiser utilizar o `TestClient`. * <a href="https://www.python-httpx.org" target="_blank"><code>httpx</code></a> - Necessário se você quiser utilizar o `TestClient`.
* <a href="https://jinja.palletsprojects.com" target="_blank"><code>jinja2</code></a> - Necessário se você quiser utilizar a configuração padrão de templates. * <a href="https://jinja.palletsprojects.com" target="_blank"><code>jinja2</code></a> - Necessário se você quiser utilizar a configuração padrão de templates.
* <a href="https://andrew-d.github.io/python-multipart/" target="_blank"><code>python-multipart</code></a> - Necessário se você quiser suporte com <abbr title="converte uma string que chega de uma requisição HTTP para dados Python">"parsing"</abbr> de formulário, com `request.form()`. * <a href="https://andrew-d.github.io/python-multipart/" target="_blank"><code>python-multipart</code></a> - Necessário se você quiser suporte com <abbr title="converte uma string que chega de uma requisição HTTP para dados Python">"parsing"</abbr> de formulário, com `request.form()`.
* <a href="https://pythonhosted.org/itsdangerous/" target="_blank"><code>itsdangerous</code></a> - Necessário para suporte a `SessionMiddleware`. * <a href="https://pythonhosted.org/itsdangerous/" target="_blank"><code>itsdangerous</code></a> - Necessário para suporte a `SessionMiddleware`.

36
docs/pt/docs/tutorial/request-forms-and-files.md

@ -0,0 +1,36 @@
# Formulários e Arquivos da Requisição
Você pode definir arquivos e campos de formulário ao mesmo tempo usando `File` e `Form`.
!!! info "Informação"
Para receber arquivos carregados e/ou dados de formulário, primeiro instale <a href="https://andrew-d.github.io/python-multipart/" class="external-link" target="_blank">`python-multipart`</a>.
Por exemplo: `pip install python-multipart`.
## Importe `File` e `Form`
```Python hl_lines="1"
{!../../../docs_src/request_forms_and_files/tutorial001.py!}
```
## Defina parâmetros de `File` e `Form`
Crie parâmetros de arquivo e formulário da mesma forma que você faria para `Body` ou `Query`:
```Python hl_lines="8"
{!../../../docs_src/request_forms_and_files/tutorial001.py!}
```
Os arquivos e campos de formulário serão carregados como dados de formulário e você receberá os arquivos e campos de formulário.
E você pode declarar alguns dos arquivos como `bytes` e alguns como `UploadFile`.
!!! warning "Aviso"
Você pode declarar vários parâmetros `File` e `Form` em uma *operação de caminho*, mas não é possível declarar campos `Body` para receber como JSON, pois a requisição terá o corpo codificado usando `multipart/form-data` ao invés de `application/json`.
Isso não é uma limitação do **FastAPI** , é parte do protocolo HTTP.
## Recapitulando
Usar `File` e `Form` juntos quando precisar receber dados e arquivos na mesma requisição.

1
docs/pt/mkdocs.yml

@ -75,6 +75,7 @@ nav:
- tutorial/header-params.md - tutorial/header-params.md
- tutorial/response-status-code.md - tutorial/response-status-code.md
- tutorial/request-forms.md - tutorial/request-forms.md
- tutorial/request-forms-and-files.md
- tutorial/handling-errors.md - tutorial/handling-errors.md
- Segurança: - Segurança:
- tutorial/security/index.md - tutorial/security/index.md

2
docs/ru/docs/features.md

@ -169,7 +169,7 @@ FastAPI включает в себя чрезвычайно простую в и
* Поддержка **WebSocket**. * Поддержка **WebSocket**.
* Фоновые задачи для процессов. * Фоновые задачи для процессов.
* События запуска и выключения. * События запуска и выключения.
* Тестовый клиент построен на библиотеке `requests`. * Тестовый клиент построен на библиотеке HTTPX.
* **CORS**, GZip, статические файлы, потоковые ответы. * **CORS**, GZip, статические файлы, потоковые ответы.
* Поддержка **сессий и cookie**. * Поддержка **сессий и cookie**.
* 100% покрытие тестами. * 100% покрытие тестами.

4
docs/ru/docs/index.md

@ -424,7 +424,7 @@ item: Item
* **GraphQL** интеграция с <a href="https://strawberry.rocks" class="external-link" target="_blank">Strawberry</a> и другими библиотеками. * **GraphQL** интеграция с <a href="https://strawberry.rocks" class="external-link" target="_blank">Strawberry</a> и другими библиотеками.
* Множество дополнительных функций (благодаря Starlette), таких как: * Множество дополнительных функций (благодаря Starlette), таких как:
* **Веб-сокеты** * **Веб-сокеты**
* очень простые тесты на основе `requests` и `pytest` * очень простые тесты на основе HTTPX и `pytest`
* **CORS** * **CORS**
* **Cookie сеансы(сессии)** * **Cookie сеансы(сессии)**
* ...и многое другое. * ...и многое другое.
@ -444,7 +444,7 @@ item: Item
Используется Starlette: Используется Starlette:
* <a href="https://requests.readthedocs.io" target="_blank"><code>requests</code></a> - Обязательно, если вы хотите использовать `TestClient`. * <a href="https://www.python-httpx.org" target="_blank"><code>HTTPX</code></a> - Обязательно, если вы хотите использовать `TestClient`.
* <a href="https://jinja.palletsprojects.com" target="_blank"><code>jinja2</code></a> - Обязательно, если вы хотите использовать конфигурацию шаблона по умолчанию. * <a href="https://jinja.palletsprojects.com" target="_blank"><code>jinja2</code></a> - Обязательно, если вы хотите использовать конфигурацию шаблона по умолчанию.
* <a href="https://andrew-d.github.io/python-multipart/" target="_blank"><code>python-multipart</code></a> - Обязательно, если вы хотите поддерживать форму <abbr title="преобразование строки, полученной из HTTP-запроса, в данные Python">"парсинга"</abbr> с помощью `request.form()`. * <a href="https://andrew-d.github.io/python-multipart/" target="_blank"><code>python-multipart</code></a> - Обязательно, если вы хотите поддерживать форму <abbr title="преобразование строки, полученной из HTTP-запроса, в данные Python">"парсинга"</abbr> с помощью `request.form()`.
* <a href="https://pythonhosted.org/itsdangerous/" target="_blank"><code>itsdangerous</code></a> - Обязательно, для поддержки `SessionMiddleware`. * <a href="https://pythonhosted.org/itsdangerous/" target="_blank"><code>itsdangerous</code></a> - Обязательно, для поддержки `SessionMiddleware`.

4
docs/sq/docs/index.md

@ -426,7 +426,7 @@ For a more complete example including more features, see the <a href="https://fa
* Many extra features (thanks to Starlette) as: * Many extra features (thanks to Starlette) as:
* **WebSockets** * **WebSockets**
* **GraphQL** * **GraphQL**
* extremely easy tests based on `requests` and `pytest` * extremely easy tests based on HTTPX and `pytest`
* **CORS** * **CORS**
* **Cookie Sessions** * **Cookie Sessions**
* ...and more. * ...and more.
@ -446,7 +446,7 @@ Used by Pydantic:
Used by Starlette: Used by Starlette:
* <a href="https://requests.readthedocs.io" target="_blank"><code>requests</code></a> - Required if you want to use the `TestClient`. * <a href="https://www.python-httpx.org" target="_blank"><code>httpx</code></a> - Required if you want to use the `TestClient`.
* <a href="https://jinja.palletsprojects.com" target="_blank"><code>jinja2</code></a> - Required if you want to use the default template configuration. * <a href="https://jinja.palletsprojects.com" target="_blank"><code>jinja2</code></a> - Required if you want to use the default template configuration.
* <a href="https://andrew-d.github.io/python-multipart/" target="_blank"><code>python-multipart</code></a> - Required if you want to support form <abbr title="converting the string that comes from an HTTP request into Python data">"parsing"</abbr>, with `request.form()`. * <a href="https://andrew-d.github.io/python-multipart/" target="_blank"><code>python-multipart</code></a> - Required if you want to support form <abbr title="converting the string that comes from an HTTP request into Python data">"parsing"</abbr>, with `request.form()`.
* <a href="https://pythonhosted.org/itsdangerous/" target="_blank"><code>itsdangerous</code></a> - Required for `SessionMiddleware` support. * <a href="https://pythonhosted.org/itsdangerous/" target="_blank"><code>itsdangerous</code></a> - Required for `SessionMiddleware` support.

4
docs/sv/docs/index.md

@ -429,7 +429,7 @@ For a more complete example including more features, see the <a href="https://fa
* **GraphQL** integration with <a href="https://strawberry.rocks" class="external-link" target="_blank">Strawberry</a> and other libraries. * **GraphQL** integration with <a href="https://strawberry.rocks" class="external-link" target="_blank">Strawberry</a> and other libraries.
* Many extra features (thanks to Starlette) as: * Many extra features (thanks to Starlette) as:
* **WebSockets** * **WebSockets**
* extremely easy tests based on `requests` and `pytest` * extremely easy tests based on HTTPX and `pytest`
* **CORS** * **CORS**
* **Cookie Sessions** * **Cookie Sessions**
* ...and more. * ...and more.
@ -449,7 +449,7 @@ Used by Pydantic:
Used by Starlette: Used by Starlette:
* <a href="https://requests.readthedocs.io" target="_blank"><code>requests</code></a> - Required if you want to use the `TestClient`. * <a href="https://www.python-httpx.org" target="_blank"><code>httpx</code></a> - Required if you want to use the `TestClient`.
* <a href="https://jinja.palletsprojects.com" target="_blank"><code>jinja2</code></a> - Required if you want to use the default template configuration. * <a href="https://jinja.palletsprojects.com" target="_blank"><code>jinja2</code></a> - Required if you want to use the default template configuration.
* <a href="https://andrew-d.github.io/python-multipart/" target="_blank"><code>python-multipart</code></a> - Required if you want to support form <abbr title="converting the string that comes from an HTTP request into Python data">"parsing"</abbr>, with `request.form()`. * <a href="https://andrew-d.github.io/python-multipart/" target="_blank"><code>python-multipart</code></a> - Required if you want to support form <abbr title="converting the string that comes from an HTTP request into Python data">"parsing"</abbr>, with `request.form()`.
* <a href="https://pythonhosted.org/itsdangerous/" target="_blank"><code>itsdangerous</code></a> - Required for `SessionMiddleware` support. * <a href="https://pythonhosted.org/itsdangerous/" target="_blank"><code>itsdangerous</code></a> - Required for `SessionMiddleware` support.

2
docs/tr/docs/features.md

@ -174,7 +174,7 @@ Bütün entegrasyonlar kullanımı kolay olmak üzere (zorunluluklar ile beraber
* **GraphQL** desteği. * **GraphQL** desteği.
* Kullanım halinde arka plan işlevleri. * Kullanım halinde arka plan işlevleri.
* Başlatma ve kapatma eventleri(startup and shutdown). * Başlatma ve kapatma eventleri(startup and shutdown).
* Test sunucusu `requests` üzerine kurulu. * Test sunucusu HTTPX üzerine kurulu.
* **CORS**, GZip, Static dosyalar, Streaming responseları. * **CORS**, GZip, Static dosyalar, Streaming responseları.
* **Session and Cookie** desteği. * **Session and Cookie** desteği.
* 100% test kapsayıcılığı. * 100% test kapsayıcılığı.

4
docs/tr/docs/index.md

@ -434,7 +434,7 @@ Daha fazla örnek ve özellik için <a href="https://fastapi.tiangolo.com/tutori
* Diğer ekstra özellikler (Starlette sayesinde): * Diğer ekstra özellikler (Starlette sayesinde):
* **WebSockets** * **WebSockets**
* **GraphQL** * **GraphQL**
* `requests` ve `pytest` sayesinde aşırı kolay testler. * HTTPX ve `pytest` sayesinde aşırı kolay testler.
* **CORS** * **CORS**
* **Cookie Sessions** * **Cookie Sessions**
* ...ve daha fazlası. * ...ve daha fazlası.
@ -454,7 +454,7 @@ Pydantic tarafında kullanılan:
Starlette tarafında kullanılan: Starlette tarafında kullanılan:
* <a href="http://docs.python-requests.org" target="_blank"><code>requests</code></a> - Eğer `TestClient` kullanmak istiyorsan gerekli. * <a href="https://www.python-httpx.org" target="_blank"><code>httpx</code></a> - Eğer `TestClient` kullanmak istiyorsan gerekli.
* <a href="http://jinja.pocoo.org" target="_blank"><code>jinja2</code></a> - Eğer kendine ait template konfigürasyonu oluşturmak istiyorsan gerekli * <a href="http://jinja.pocoo.org" target="_blank"><code>jinja2</code></a> - Eğer kendine ait template konfigürasyonu oluşturmak istiyorsan gerekli
* <a href="https://andrew-d.github.io/python-multipart/" target="_blank"><code>python-multipart</code></a> - Form kullanmak istiyorsan gerekli <abbr title="HTTP bağlantısından gelen stringi Python objesine çevirmek için">("dönüşümü")</abbr>. * <a href="https://andrew-d.github.io/python-multipart/" target="_blank"><code>python-multipart</code></a> - Form kullanmak istiyorsan gerekli <abbr title="HTTP bağlantısından gelen stringi Python objesine çevirmek için">("dönüşümü")</abbr>.
* <a href="https://pythonhosted.org/itsdangerous/" target="_blank"><code>itsdangerous</code></a> - `SessionMiddleware` desteği için gerekli. * <a href="https://pythonhosted.org/itsdangerous/" target="_blank"><code>itsdangerous</code></a> - `SessionMiddleware` desteği için gerekli.

4
docs/uk/docs/index.md

@ -426,7 +426,7 @@ For a more complete example including more features, see the <a href="https://fa
* Many extra features (thanks to Starlette) as: * Many extra features (thanks to Starlette) as:
* **WebSockets** * **WebSockets**
* **GraphQL** * **GraphQL**
* extremely easy tests based on `requests` and `pytest` * extremely easy tests based on HTTPX and `pytest`
* **CORS** * **CORS**
* **Cookie Sessions** * **Cookie Sessions**
* ...and more. * ...and more.
@ -446,7 +446,7 @@ Used by Pydantic:
Used by Starlette: Used by Starlette:
* <a href="https://requests.readthedocs.io" target="_blank"><code>requests</code></a> - Required if you want to use the `TestClient`. * <a href="https://www.python-httpx.org" target="_blank"><code>httpx</code></a> - Required if you want to use the `TestClient`.
* <a href="https://jinja.palletsprojects.com" target="_blank"><code>jinja2</code></a> - Required if you want to use the default template configuration. * <a href="https://jinja.palletsprojects.com" target="_blank"><code>jinja2</code></a> - Required if you want to use the default template configuration.
* <a href="https://andrew-d.github.io/python-multipart/" target="_blank"><code>python-multipart</code></a> - Required if you want to support form <abbr title="converting the string that comes from an HTTP request into Python data">"parsing"</abbr>, with `request.form()`. * <a href="https://andrew-d.github.io/python-multipart/" target="_blank"><code>python-multipart</code></a> - Required if you want to support form <abbr title="converting the string that comes from an HTTP request into Python data">"parsing"</abbr>, with `request.form()`.
* <a href="https://pythonhosted.org/itsdangerous/" target="_blank"><code>itsdangerous</code></a> - Required for `SessionMiddleware` support. * <a href="https://pythonhosted.org/itsdangerous/" target="_blank"><code>itsdangerous</code></a> - Required for `SessionMiddleware` support.

2
docs/zh/docs/features.md

@ -171,7 +171,7 @@ FastAPI 有一个使用非常简单,但是非常强大的<abbr title='也叫
* **支持 GraphQL** * **支持 GraphQL**
* 后台任务处理。 * 后台任务处理。
* Startup 和 shutdown 事件。 * Startup 和 shutdown 事件。
* 测试客户端基于 `requests` * 测试客户端基于 HTTPX
* **CORS**, GZip, 静态文件, 流响应。 * **CORS**, GZip, 静态文件, 流响应。
* 支持 **Session 和 Cookie** * 支持 **Session 和 Cookie**
* 100% 测试覆盖率。 * 100% 测试覆盖率。

4
docs/zh/docs/index.md

@ -422,7 +422,7 @@ item: Item
* 许多额外功能(归功于 Starlette)比如: * 许多额外功能(归功于 Starlette)比如:
* **WebSockets** * **WebSockets**
* **GraphQL** * **GraphQL**
* 基于 `requests``pytest` 的极其简单的测试 * 基于 HTTPX`pytest` 的极其简单的测试
* **CORS** * **CORS**
* **Cookie Sessions** * **Cookie Sessions**
* ......以及更多 * ......以及更多
@ -442,7 +442,7 @@ item: Item
用于 Starlette: 用于 Starlette:
* <a href="https://requests.readthedocs.io" target="_blank"><code>requests</code></a> - 使用 `TestClient` 时安装。 * <a href="https://www.python-httpx.org" target="_blank"><code>httpx</code></a> - 使用 `TestClient` 时安装。
* <a href="https://jinja.palletsprojects.com" target="_blank"><code>jinja2</code></a> - 使用默认模板配置时安装。 * <a href="https://jinja.palletsprojects.com" target="_blank"><code>jinja2</code></a> - 使用默认模板配置时安装。
* <a href="https://andrew-d.github.io/python-multipart/" target="_blank"><code>python-multipart</code></a> - 需要通过 `request.form()` 对表单进行<abbr title="将来自 HTTP 请求中的字符串转换为 Python 数据类型">「解析」</abbr>时安装。 * <a href="https://andrew-d.github.io/python-multipart/" target="_blank"><code>python-multipart</code></a> - 需要通过 `request.form()` 对表单进行<abbr title="将来自 HTTP 请求中的字符串转换为 Python 数据类型">「解析」</abbr>时安装。
* <a href="https://pythonhosted.org/itsdangerous/" target="_blank"><code>itsdangerous</code></a> - 需要 `SessionMiddleware` 支持时安装。 * <a href="https://pythonhosted.org/itsdangerous/" target="_blank"><code>itsdangerous</code></a> - 需要 `SessionMiddleware` 支持时安装。

2
docs_src/security/tutorial005.py

@ -107,7 +107,7 @@ async def get_current_user(
if security_scopes.scopes: if security_scopes.scopes:
authenticate_value = f'Bearer scope="{security_scopes.scope_str}"' authenticate_value = f'Bearer scope="{security_scopes.scope_str}"'
else: else:
authenticate_value = f"Bearer" authenticate_value = "Bearer"
credentials_exception = HTTPException( credentials_exception = HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED, status_code=status.HTTP_401_UNAUTHORIZED,
detail="Could not validate credentials", detail="Could not validate credentials",

2
docs_src/security/tutorial005_py310.py

@ -106,7 +106,7 @@ async def get_current_user(
if security_scopes.scopes: if security_scopes.scopes:
authenticate_value = f'Bearer scope="{security_scopes.scope_str}"' authenticate_value = f'Bearer scope="{security_scopes.scope_str}"'
else: else:
authenticate_value = f"Bearer" authenticate_value = "Bearer"
credentials_exception = HTTPException( credentials_exception = HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED, status_code=status.HTTP_401_UNAUTHORIZED,
detail="Could not validate credentials", detail="Could not validate credentials",

2
docs_src/security/tutorial005_py39.py

@ -107,7 +107,7 @@ async def get_current_user(
if security_scopes.scopes: if security_scopes.scopes:
authenticate_value = f'Bearer scope="{security_scopes.scope_str}"' authenticate_value = f'Bearer scope="{security_scopes.scope_str}"'
else: else:
authenticate_value = f"Bearer" authenticate_value = "Bearer"
credentials_exception = HTTPException( credentials_exception = HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED, status_code=status.HTTP_401_UNAUTHORIZED,
detail="Could not validate credentials", detail="Could not validate credentials",

12
docs_src/websockets/tutorial002.py

@ -1,6 +1,14 @@
from typing import Union from typing import Union
from fastapi import Cookie, Depends, FastAPI, Query, WebSocket, status from fastapi import (
Cookie,
Depends,
FastAPI,
Query,
WebSocket,
WebSocketException,
status,
)
from fastapi.responses import HTMLResponse from fastapi.responses import HTMLResponse
app = FastAPI() app = FastAPI()
@ -61,7 +69,7 @@ async def get_cookie_or_token(
token: Union[str, None] = Query(default=None), token: Union[str, None] = Query(default=None),
): ):
if session is None and token is None: if session is None and token is None:
await websocket.close(code=status.WS_1008_POLICY_VIOLATION) raise WebSocketException(code=status.WS_1008_POLICY_VIOLATION)
return session or token return session or token

1
fastapi/__init__.py

@ -8,6 +8,7 @@ from .applications import FastAPI as FastAPI
from .background import BackgroundTasks as BackgroundTasks from .background import BackgroundTasks as BackgroundTasks
from .datastructures import UploadFile as UploadFile from .datastructures import UploadFile as UploadFile
from .exceptions import HTTPException as HTTPException from .exceptions import HTTPException as HTTPException
from .exceptions import WebSocketException as WebSocketException
from .param_functions import Body as Body from .param_functions import Body as Body
from .param_functions import Cookie as Cookie from .param_functions import Cookie as Cookie
from .param_functions import Depends as Depends from .param_functions import Depends as Depends

12
fastapi/dependencies/utils.py

@ -426,21 +426,21 @@ def is_coroutine_callable(call: Callable[..., Any]) -> bool:
return inspect.iscoroutinefunction(call) return inspect.iscoroutinefunction(call)
if inspect.isclass(call): if inspect.isclass(call):
return False return False
dunder_call = getattr(call, "__call__", None) dunder_call = getattr(call, "__call__", None) # noqa: B004
return inspect.iscoroutinefunction(dunder_call) return inspect.iscoroutinefunction(dunder_call)
def is_async_gen_callable(call: Callable[..., Any]) -> bool: def is_async_gen_callable(call: Callable[..., Any]) -> bool:
if inspect.isasyncgenfunction(call): if inspect.isasyncgenfunction(call):
return True return True
dunder_call = getattr(call, "__call__", None) dunder_call = getattr(call, "__call__", None) # noqa: B004
return inspect.isasyncgenfunction(dunder_call) return inspect.isasyncgenfunction(dunder_call)
def is_gen_callable(call: Callable[..., Any]) -> bool: def is_gen_callable(call: Callable[..., Any]) -> bool:
if inspect.isgeneratorfunction(call): if inspect.isgeneratorfunction(call):
return True return True
dunder_call = getattr(call, "__call__", None) dunder_call = getattr(call, "__call__", None) # noqa: B004
return inspect.isgeneratorfunction(dunder_call) return inspect.isgeneratorfunction(dunder_call)
@ -724,14 +724,14 @@ def get_body_field(*, dependant: Dependant, name: str) -> Optional[ModelField]:
# in case a sub-dependency is evaluated with a single unique body field # in case a sub-dependency is evaluated with a single unique body field
# That is combined (embedded) with other body fields # That is combined (embedded) with other body fields
for param in flat_dependant.body_params: for param in flat_dependant.body_params:
setattr(param.field_info, "embed", True) setattr(param.field_info, "embed", True) # noqa: B010
model_name = "Body_" + name model_name = "Body_" + name
BodyModel: Type[BaseModel] = create_model(model_name) BodyModel: Type[BaseModel] = create_model(model_name)
for f in flat_dependant.body_params: for f in flat_dependant.body_params:
BodyModel.__fields__[f.name] = f BodyModel.__fields__[f.name] = f
required = any(True for f in flat_dependant.body_params if f.required) required = any(True for f in flat_dependant.body_params if f.required)
BodyFieldInfo_kwargs: Dict[str, Any] = dict(default=None) BodyFieldInfo_kwargs: Dict[str, Any] = {"default": None}
if any(isinstance(f.field_info, params.File) for f in flat_dependant.body_params): if any(isinstance(f.field_info, params.File) for f in flat_dependant.body_params):
BodyFieldInfo: Type[params.Body] = params.File BodyFieldInfo: Type[params.Body] = params.File
elif any(isinstance(f.field_info, params.Form) for f in flat_dependant.body_params): elif any(isinstance(f.field_info, params.Form) for f in flat_dependant.body_params):
@ -740,7 +740,7 @@ def get_body_field(*, dependant: Dependant, name: str) -> Optional[ModelField]:
BodyFieldInfo = params.Body BodyFieldInfo = params.Body
body_param_media_types = [ body_param_media_types = [
getattr(f.field_info, "media_type") f.field_info.media_type
for f in flat_dependant.body_params for f in flat_dependant.body_params
if isinstance(f.field_info, params.Body) if isinstance(f.field_info, params.Body)
] ]

1
fastapi/exceptions.py

@ -3,6 +3,7 @@ from typing import Any, Dict, Optional, Sequence, Type
from pydantic import BaseModel, ValidationError, create_model from pydantic import BaseModel, ValidationError, create_model
from pydantic.error_wrappers import ErrorList from pydantic.error_wrappers import ErrorList
from starlette.exceptions import HTTPException as StarletteHTTPException from starlette.exceptions import HTTPException as StarletteHTTPException
from starlette.exceptions import WebSocketException as WebSocketException # noqa: F401
class HTTPException(StarletteHTTPException): class HTTPException(StarletteHTTPException):

2
fastapi/routing.py

@ -715,7 +715,7 @@ class APIRouter(routing.Router):
), "A path prefix must not end with '/', as the routes will start with '/'" ), "A path prefix must not end with '/', as the routes will start with '/'"
else: else:
for r in router.routes: for r in router.routes:
path = getattr(r, "path") path = getattr(r, "path") # noqa: B009
name = getattr(r, "name", "unknown") name = getattr(r, "name", "unknown")
if path is not None and not path: if path is not None and not path:
raise Exception( raise Exception(

2
fastapi/security/api_key.py

@ -54,7 +54,7 @@ class APIKeyHeader(APIKeyBase):
self.auto_error = auto_error self.auto_error = auto_error
async def __call__(self, request: Request) -> Optional[str]: async def __call__(self, request: Request) -> Optional[str]:
api_key: str = request.headers.get(self.model.name) api_key = request.headers.get(self.model.name)
if not api_key: if not api_key:
if self.auto_error: if self.auto_error:
raise HTTPException( raise HTTPException(

8
fastapi/security/http.py

@ -38,7 +38,7 @@ class HTTPBase(SecurityBase):
async def __call__( async def __call__(
self, request: Request self, request: Request
) -> Optional[HTTPAuthorizationCredentials]: ) -> Optional[HTTPAuthorizationCredentials]:
authorization: str = request.headers.get("Authorization") authorization = request.headers.get("Authorization")
scheme, credentials = get_authorization_scheme_param(authorization) scheme, credentials = get_authorization_scheme_param(authorization)
if not (authorization and scheme and credentials): if not (authorization and scheme and credentials):
if self.auto_error: if self.auto_error:
@ -67,7 +67,7 @@ class HTTPBasic(HTTPBase):
async def __call__( # type: ignore async def __call__( # type: ignore
self, request: Request self, request: Request
) -> Optional[HTTPBasicCredentials]: ) -> Optional[HTTPBasicCredentials]:
authorization: str = request.headers.get("Authorization") authorization = request.headers.get("Authorization")
scheme, param = get_authorization_scheme_param(authorization) scheme, param = get_authorization_scheme_param(authorization)
if self.realm: if self.realm:
unauthorized_headers = {"WWW-Authenticate": f'Basic realm="{self.realm}"'} unauthorized_headers = {"WWW-Authenticate": f'Basic realm="{self.realm}"'}
@ -113,7 +113,7 @@ class HTTPBearer(HTTPBase):
async def __call__( async def __call__(
self, request: Request self, request: Request
) -> Optional[HTTPAuthorizationCredentials]: ) -> Optional[HTTPAuthorizationCredentials]:
authorization: str = request.headers.get("Authorization") authorization = request.headers.get("Authorization")
scheme, credentials = get_authorization_scheme_param(authorization) scheme, credentials = get_authorization_scheme_param(authorization)
if not (authorization and scheme and credentials): if not (authorization and scheme and credentials):
if self.auto_error: if self.auto_error:
@ -148,7 +148,7 @@ class HTTPDigest(HTTPBase):
async def __call__( async def __call__(
self, request: Request self, request: Request
) -> Optional[HTTPAuthorizationCredentials]: ) -> Optional[HTTPAuthorizationCredentials]:
authorization: str = request.headers.get("Authorization") authorization = request.headers.get("Authorization")
scheme, credentials = get_authorization_scheme_param(authorization) scheme, credentials = get_authorization_scheme_param(authorization)
if not (authorization and scheme and credentials): if not (authorization and scheme and credentials):
if self.auto_error: if self.auto_error:

6
fastapi/security/oauth2.py

@ -126,7 +126,7 @@ class OAuth2(SecurityBase):
self.auto_error = auto_error self.auto_error = auto_error
async def __call__(self, request: Request) -> Optional[str]: async def __call__(self, request: Request) -> Optional[str]:
authorization: str = request.headers.get("Authorization") authorization = request.headers.get("Authorization")
if not authorization: if not authorization:
if self.auto_error: if self.auto_error:
raise HTTPException( raise HTTPException(
@ -157,7 +157,7 @@ class OAuth2PasswordBearer(OAuth2):
) )
async def __call__(self, request: Request) -> Optional[str]: async def __call__(self, request: Request) -> Optional[str]:
authorization: str = request.headers.get("Authorization") authorization = request.headers.get("Authorization")
scheme, param = get_authorization_scheme_param(authorization) scheme, param = get_authorization_scheme_param(authorization)
if not authorization or scheme.lower() != "bearer": if not authorization or scheme.lower() != "bearer":
if self.auto_error: if self.auto_error:
@ -200,7 +200,7 @@ class OAuth2AuthorizationCodeBearer(OAuth2):
) )
async def __call__(self, request: Request) -> Optional[str]: async def __call__(self, request: Request) -> Optional[str]:
authorization: str = request.headers.get("Authorization") authorization = request.headers.get("Authorization")
scheme, param = get_authorization_scheme_param(authorization) scheme, param = get_authorization_scheme_param(authorization)
if not authorization or scheme.lower() != "bearer": if not authorization or scheme.lower() != "bearer":
if self.auto_error: if self.auto_error:

2
fastapi/security/open_id_connect_url.py

@ -23,7 +23,7 @@ class OpenIdConnect(SecurityBase):
self.auto_error = auto_error self.auto_error = auto_error
async def __call__(self, request: Request) -> Optional[str]: async def __call__(self, request: Request) -> Optional[str]:
authorization: str = request.headers.get("Authorization") authorization = request.headers.get("Authorization")
if not authorization: if not authorization:
if self.auto_error: if self.auto_error:
raise HTTPException( raise HTTPException(

6
fastapi/security/utils.py

@ -1,7 +1,9 @@
from typing import Tuple from typing import Optional, Tuple
def get_authorization_scheme_param(authorization_header_value: str) -> Tuple[str, str]: def get_authorization_scheme_param(
authorization_header_value: Optional[str],
) -> Tuple[str, str]:
if not authorization_header_value: if not authorization_header_value:
return "", "" return "", ""
scheme, _, param = authorization_header_value.partition(" ") scheme, _, param = authorization_header_value.partition(" ")

37
pyproject.toml

@ -39,7 +39,7 @@ classifiers = [
"Topic :: Internet :: WWW/HTTP", "Topic :: Internet :: WWW/HTTP",
] ]
dependencies = [ dependencies = [
"starlette==0.20.4", "starlette==0.21.0",
"pydantic >=1.6.2,!=1.7,!=1.7.1,!=1.7.2,!=1.7.3,!=1.8,!=1.8.1,<2.0.0", "pydantic >=1.6.2,!=1.7,!=1.7.1,!=1.7.2,!=1.7.3,!=1.8,!=1.8.1,<2.0.0",
] ]
dynamic = ["version"] dynamic = ["version"]
@ -53,10 +53,9 @@ test = [
"pytest >=7.1.3,<8.0.0", "pytest >=7.1.3,<8.0.0",
"coverage[toml] >= 6.5.0,<7.0", "coverage[toml] >= 6.5.0,<7.0",
"mypy ==0.982", "mypy ==0.982",
"flake8 >=3.8.3,<6.0.0", "ruff ==0.0.114",
"black == 22.8.0", "black == 22.8.0",
"isort >=5.0.6,<6.0.0", "isort >=5.0.6,<6.0.0",
"requests >=2.24.0,<3.0.0",
"httpx >=0.23.0,<0.24.0", "httpx >=0.23.0,<0.24.0",
"email_validator >=1.1.1,<2.0.0", "email_validator >=1.1.1,<2.0.0",
# TODO: once removing databases from tutorial, upgrade SQLAlchemy # TODO: once removing databases from tutorial, upgrade SQLAlchemy
@ -88,13 +87,12 @@ doc = [
"pyyaml >=5.3.1,<7.0.0", "pyyaml >=5.3.1,<7.0.0",
] ]
dev = [ dev = [
"autoflake >=1.4.0,<2.0.0", "ruff ==0.0.114",
"flake8 >=3.8.3,<6.0.0",
"uvicorn[standard] >=0.12.0,<0.19.0", "uvicorn[standard] >=0.12.0,<0.19.0",
"pre-commit >=2.17.0,<3.0.0", "pre-commit >=2.17.0,<3.0.0",
] ]
all = [ all = [
"requests >=2.24.0,<3.0.0", "httpx >=0.23.0,<0.24.0",
"jinja2 >=2.11.2,<4.0.0", "jinja2 >=2.11.2,<4.0.0",
"python-multipart >=0.0.5,<0.0.6", "python-multipart >=0.0.5,<0.0.6",
"itsdangerous >=1.1.0,<3.0.0", "itsdangerous >=1.1.0,<3.0.0",
@ -157,3 +155,30 @@ source = [
"fastapi" "fastapi"
] ]
context = '${CONTEXT}' context = '${CONTEXT}'
[tool.ruff]
select = [
"E", # pycodestyle errors
"W", # pycodestyle warnings
"F", # pyflakes
# "I", # isort
"C", # flake8-comprehensions
"B", # flake8-bugbear
]
ignore = [
"E501", # line too long, handled by black
"B008", # do not perform function calls in argument defaults
]
[tool.ruff.per-file-ignores]
"__init__.py" = ["F401"]
"docs_src/dependencies/tutorial007.py" = ["F821"]
"docs_src/dependencies/tutorial008.py" = ["F821"]
"docs_src/dependencies/tutorial009.py" = ["F821"]
"docs_src/dependencies/tutorial010.py" = ["F821"]
"docs_src/custom_response/tutorial007.py" = ["B007"]
"docs_src/dataclasses/tutorial003.py" = ["I001"]
[tool.ruff.isort]
known-third-party = ["fastapi", "pydantic", "starlette"]

4
scripts/docs.py

@ -332,7 +332,7 @@ def serve():
os.chdir("site") os.chdir("site")
server_address = ("", 8008) server_address = ("", 8008)
server = HTTPServer(server_address, SimpleHTTPRequestHandler) server = HTTPServer(server_address, SimpleHTTPRequestHandler)
typer.echo(f"Serving at: http://127.0.0.1:8008") typer.echo("Serving at: http://127.0.0.1:8008")
server.serve_forever() server.serve_forever()
@ -420,7 +420,7 @@ def get_file_to_nav_map(nav: list) -> Dict[str, Tuple[str, ...]]:
file_to_nav = {} file_to_nav = {}
for item in nav: for item in nav:
if type(item) is str: if type(item) is str:
file_to_nav[item] = tuple() file_to_nav[item] = ()
elif type(item) is dict: elif type(item) is dict:
item_key = list(item.keys())[0] item_key = list(item.keys())[0]
sub_nav = item[item_key] sub_nav = item[item_key]

2
scripts/format.sh

@ -1,6 +1,6 @@
#!/bin/sh -e #!/bin/sh -e
set -x set -x
autoflake --remove-all-unused-imports --recursive --remove-unused-variables --in-place docs_src fastapi tests scripts --exclude=__init__.py ruff fastapi tests docs_src scripts --fix
black fastapi tests docs_src scripts black fastapi tests docs_src scripts
isort fastapi tests docs_src scripts isort fastapi tests docs_src scripts

2
scripts/lint.sh

@ -4,6 +4,6 @@ set -e
set -x set -x
mypy fastapi mypy fastapi
flake8 fastapi tests ruff fastapi tests docs_src scripts
black fastapi tests --check black fastapi tests --check
isort fastapi tests docs_src scripts --check-only isort fastapi tests docs_src scripts --check-only

6
tests/test_custom_route_class.py

@ -110,6 +110,6 @@ def test_route_classes():
for r in app.router.routes: for r in app.router.routes:
assert isinstance(r, Route) assert isinstance(r, Route)
routes[r.path] = r routes[r.path] = r
assert getattr(routes["/a/"], "x_type") == "A" assert getattr(routes["/a/"], "x_type") == "A" # noqa: B009
assert getattr(routes["/a/b/"], "x_type") == "B" assert getattr(routes["/a/b/"], "x_type") == "B" # noqa: B009
assert getattr(routes["/a/b/c/"], "x_type") == "C" assert getattr(routes["/a/b/c/"], "x_type") == "C" # noqa: B009

2
tests/test_enforce_once_required_parameter.py

@ -101,7 +101,7 @@ def test_schema():
def test_get_invalid(): def test_get_invalid():
response = client.get("/foo", params={"client_id": None}) response = client.get("/foo")
assert response.status_code == status.HTTP_422_UNPROCESSABLE_ENTITY assert response.status_code == status.HTTP_422_UNPROCESSABLE_ENTITY

2
tests/test_extra_routes.py

@ -333,7 +333,7 @@ def test_get_api_route_not_decorated():
def test_delete(): def test_delete():
response = client.delete("/items/foo", json={"name": "Foo"}) response = client.request("DELETE", "/items/foo", json={"name": "Foo"})
assert response.status_code == 200, response.text assert response.status_code == 200, response.text
assert response.json() == {"item_id": "foo", "item": {"name": "Foo", "price": None}} assert response.json() == {"item_id": "foo", "item": {"name": "Foo", "price": None}}

2
tests/test_get_request_body.py

@ -104,5 +104,5 @@ def test_openapi_schema():
def test_get_with_body(): def test_get_with_body():
body = {"name": "Foo", "description": "Some description", "price": 5.5} body = {"name": "Foo", "description": "Some description", "price": 5.5}
response = client.get("/product", json=body) response = client.request("GET", "/product", json=body)
assert response.json() == body assert response.json() == body

9
tests/test_param_include_in_schema.py

@ -33,8 +33,6 @@ async def hidden_query(
return {"hidden_query": hidden_query} return {"hidden_query": hidden_query}
client = TestClient(app)
openapi_shema = { openapi_shema = {
"openapi": "3.0.2", "openapi": "3.0.2",
"info": {"title": "FastAPI", "version": "0.1.0"}, "info": {"title": "FastAPI", "version": "0.1.0"},
@ -161,6 +159,7 @@ openapi_shema = {
def test_openapi_schema(): def test_openapi_schema():
client = TestClient(app)
response = client.get("/openapi.json") response = client.get("/openapi.json")
assert response.status_code == 200 assert response.status_code == 200
assert response.json() == openapi_shema assert response.json() == openapi_shema
@ -184,7 +183,8 @@ def test_openapi_schema():
], ],
) )
def test_hidden_cookie(path, cookies, expected_status, expected_response): def test_hidden_cookie(path, cookies, expected_status, expected_response):
response = client.get(path, cookies=cookies) client = TestClient(app, cookies=cookies)
response = client.get(path)
assert response.status_code == expected_status assert response.status_code == expected_status
assert response.json() == expected_response assert response.json() == expected_response
@ -207,12 +207,14 @@ def test_hidden_cookie(path, cookies, expected_status, expected_response):
], ],
) )
def test_hidden_header(path, headers, expected_status, expected_response): def test_hidden_header(path, headers, expected_status, expected_response):
client = TestClient(app)
response = client.get(path, headers=headers) response = client.get(path, headers=headers)
assert response.status_code == expected_status assert response.status_code == expected_status
assert response.json() == expected_response assert response.json() == expected_response
def test_hidden_path(): def test_hidden_path():
client = TestClient(app)
response = client.get("/hidden_path/hidden_path") response = client.get("/hidden_path/hidden_path")
assert response.status_code == 200 assert response.status_code == 200
assert response.json() == {"hidden_path": "hidden_path"} assert response.json() == {"hidden_path": "hidden_path"}
@ -234,6 +236,7 @@ def test_hidden_path():
], ],
) )
def test_hidden_query(path, expected_status, expected_response): def test_hidden_query(path, expected_status, expected_response):
client = TestClient(app)
response = client.get(path) response = client.get(path)
assert response.status_code == expected_status assert response.status_code == expected_status
assert response.json() == expected_response assert response.json() == expected_response

7
tests/test_security_api_key_cookie.py

@ -22,8 +22,6 @@ def read_current_user(current_user: User = Depends(get_current_user)):
return current_user return current_user
client = TestClient(app)
openapi_schema = { openapi_schema = {
"openapi": "3.0.2", "openapi": "3.0.2",
"info": {"title": "FastAPI", "version": "0.1.0"}, "info": {"title": "FastAPI", "version": "0.1.0"},
@ -51,18 +49,21 @@ openapi_schema = {
def test_openapi_schema(): def test_openapi_schema():
client = TestClient(app)
response = client.get("/openapi.json") response = client.get("/openapi.json")
assert response.status_code == 200, response.text assert response.status_code == 200, response.text
assert response.json() == openapi_schema assert response.json() == openapi_schema
def test_security_api_key(): def test_security_api_key():
response = client.get("/users/me", cookies={"key": "secret"}) client = TestClient(app, cookies={"key": "secret"})
response = client.get("/users/me")
assert response.status_code == 200, response.text assert response.status_code == 200, response.text
assert response.json() == {"username": "secret"} assert response.json() == {"username": "secret"}
def test_security_api_key_no_key(): def test_security_api_key_no_key():
client = TestClient(app)
response = client.get("/users/me") response = client.get("/users/me")
assert response.status_code == 403, response.text assert response.status_code == 403, response.text
assert response.json() == {"detail": "Not authenticated"} assert response.json() == {"detail": "Not authenticated"}

7
tests/test_security_api_key_cookie_description.py

@ -22,8 +22,6 @@ def read_current_user(current_user: User = Depends(get_current_user)):
return current_user return current_user
client = TestClient(app)
openapi_schema = { openapi_schema = {
"openapi": "3.0.2", "openapi": "3.0.2",
"info": {"title": "FastAPI", "version": "0.1.0"}, "info": {"title": "FastAPI", "version": "0.1.0"},
@ -56,18 +54,21 @@ openapi_schema = {
def test_openapi_schema(): def test_openapi_schema():
client = TestClient(app)
response = client.get("/openapi.json") response = client.get("/openapi.json")
assert response.status_code == 200, response.text assert response.status_code == 200, response.text
assert response.json() == openapi_schema assert response.json() == openapi_schema
def test_security_api_key(): def test_security_api_key():
response = client.get("/users/me", cookies={"key": "secret"}) client = TestClient(app, cookies={"key": "secret"})
response = client.get("/users/me")
assert response.status_code == 200, response.text assert response.status_code == 200, response.text
assert response.json() == {"username": "secret"} assert response.json() == {"username": "secret"}
def test_security_api_key_no_key(): def test_security_api_key_no_key():
client = TestClient(app)
response = client.get("/users/me") response = client.get("/users/me")
assert response.status_code == 403, response.text assert response.status_code == 403, response.text
assert response.json() == {"detail": "Not authenticated"} assert response.json() == {"detail": "Not authenticated"}

7
tests/test_security_api_key_cookie_optional.py

@ -29,8 +29,6 @@ def read_current_user(current_user: User = Depends(get_current_user)):
return current_user return current_user
client = TestClient(app)
openapi_schema = { openapi_schema = {
"openapi": "3.0.2", "openapi": "3.0.2",
"info": {"title": "FastAPI", "version": "0.1.0"}, "info": {"title": "FastAPI", "version": "0.1.0"},
@ -58,18 +56,21 @@ openapi_schema = {
def test_openapi_schema(): def test_openapi_schema():
client = TestClient(app)
response = client.get("/openapi.json") response = client.get("/openapi.json")
assert response.status_code == 200, response.text assert response.status_code == 200, response.text
assert response.json() == openapi_schema assert response.json() == openapi_schema
def test_security_api_key(): def test_security_api_key():
response = client.get("/users/me", cookies={"key": "secret"}) client = TestClient(app, cookies={"key": "secret"})
response = client.get("/users/me")
assert response.status_code == 200, response.text assert response.status_code == 200, response.text
assert response.json() == {"username": "secret"} assert response.json() == {"username": "secret"}
def test_security_api_key_no_key(): def test_security_api_key_no_key():
client = TestClient(app)
response = client.get("/users/me") response = client.get("/users/me")
assert response.status_code == 200, response.text assert response.status_code == 200, response.text
assert response.json() == {"msg": "Create an account first"} assert response.json() == {"msg": "Create an account first"}

4
tests/test_security_http_basic_optional.py

@ -4,7 +4,6 @@ from typing import Optional
from fastapi import FastAPI, Security from fastapi import FastAPI, Security
from fastapi.security import HTTPBasic, HTTPBasicCredentials from fastapi.security import HTTPBasic, HTTPBasicCredentials
from fastapi.testclient import TestClient from fastapi.testclient import TestClient
from requests.auth import HTTPBasicAuth
app = FastAPI() app = FastAPI()
@ -51,8 +50,7 @@ def test_openapi_schema():
def test_security_http_basic(): def test_security_http_basic():
auth = HTTPBasicAuth(username="john", password="secret") response = client.get("/users/me", auth=("john", "secret"))
response = client.get("/users/me", auth=auth)
assert response.status_code == 200, response.text assert response.status_code == 200, response.text
assert response.json() == {"username": "john", "password": "secret"} assert response.json() == {"username": "john", "password": "secret"}

4
tests/test_security_http_basic_realm.py

@ -3,7 +3,6 @@ from base64 import b64encode
from fastapi import FastAPI, Security from fastapi import FastAPI, Security
from fastapi.security import HTTPBasic, HTTPBasicCredentials from fastapi.security import HTTPBasic, HTTPBasicCredentials
from fastapi.testclient import TestClient from fastapi.testclient import TestClient
from requests.auth import HTTPBasicAuth
app = FastAPI() app = FastAPI()
@ -48,8 +47,7 @@ def test_openapi_schema():
def test_security_http_basic(): def test_security_http_basic():
auth = HTTPBasicAuth(username="john", password="secret") response = client.get("/users/me", auth=("john", "secret"))
response = client.get("/users/me", auth=auth)
assert response.status_code == 200, response.text assert response.status_code == 200, response.text
assert response.json() == {"username": "john", "password": "secret"} assert response.json() == {"username": "john", "password": "secret"}

4
tests/test_security_http_basic_realm_description.py

@ -3,7 +3,6 @@ from base64 import b64encode
from fastapi import FastAPI, Security from fastapi import FastAPI, Security
from fastapi.security import HTTPBasic, HTTPBasicCredentials from fastapi.security import HTTPBasic, HTTPBasicCredentials
from fastapi.testclient import TestClient from fastapi.testclient import TestClient
from requests.auth import HTTPBasicAuth
app = FastAPI() app = FastAPI()
@ -54,8 +53,7 @@ def test_openapi_schema():
def test_security_http_basic(): def test_security_http_basic():
auth = HTTPBasicAuth(username="john", password="secret") response = client.get("/users/me", auth=("john", "secret"))
response = client.get("/users/me", auth=auth)
assert response.status_code == 200, response.text assert response.status_code == 200, response.text
assert response.json() == {"username": "john", "password": "secret"} assert response.json() == {"username": "john", "password": "secret"}

8
tests/test_tuples.py

@ -252,16 +252,14 @@ def test_tuple_with_model_invalid():
def test_tuple_form_valid(): def test_tuple_form_valid():
response = client.post("/tuple-form/", data=[("values", "1"), ("values", "2")]) response = client.post("/tuple-form/", data={"values": ("1", "2")})
assert response.status_code == 200, response.text assert response.status_code == 200, response.text
assert response.json() == [1, 2] assert response.json() == [1, 2]
def test_tuple_form_invalid(): def test_tuple_form_invalid():
response = client.post( response = client.post("/tuple-form/", data={"values": ("1", "2", "3")})
"/tuple-form/", data=[("values", "1"), ("values", "2"), ("values", "3")]
)
assert response.status_code == 422, response.text assert response.status_code == 422, response.text
response = client.post("/tuple-form/", data=[("values", "1")]) response = client.post("/tuple-form/", data={"values": ("1")})
assert response.status_code == 422, response.text assert response.status_code == 422, response.text

2
tests/test_tutorial/test_advanced_middleware/test_tutorial001.py

@ -9,6 +9,6 @@ def test_middleware():
assert response.status_code == 200, response.text assert response.status_code == 200, response.text
client = TestClient(app) client = TestClient(app)
response = client.get("/", allow_redirects=False) response = client.get("/", follow_redirects=False)
assert response.status_code == 307, response.text assert response.status_code == 307, response.text
assert response.headers["location"] == "https://testserver/" assert response.headers["location"] == "https://testserver/"

16
tests/test_tutorial/test_body/test_tutorial001.py

@ -176,7 +176,7 @@ def test_post_broken_body():
response = client.post( response = client.post(
"/items/", "/items/",
headers={"content-type": "application/json"}, headers={"content-type": "application/json"},
data="{some broken json}", content="{some broken json}",
) )
assert response.status_code == 422, response.text assert response.status_code == 422, response.text
assert response.json() == { assert response.json() == {
@ -214,7 +214,7 @@ def test_post_form_for_json():
def test_explicit_content_type(): def test_explicit_content_type():
response = client.post( response = client.post(
"/items/", "/items/",
data='{"name": "Foo", "price": 50.5}', content='{"name": "Foo", "price": 50.5}',
headers={"Content-Type": "application/json"}, headers={"Content-Type": "application/json"},
) )
assert response.status_code == 200, response.text assert response.status_code == 200, response.text
@ -223,7 +223,7 @@ def test_explicit_content_type():
def test_geo_json(): def test_geo_json():
response = client.post( response = client.post(
"/items/", "/items/",
data='{"name": "Foo", "price": 50.5}', content='{"name": "Foo", "price": 50.5}',
headers={"Content-Type": "application/geo+json"}, headers={"Content-Type": "application/geo+json"},
) )
assert response.status_code == 200, response.text assert response.status_code == 200, response.text
@ -232,7 +232,7 @@ def test_geo_json():
def test_no_content_type_is_json(): def test_no_content_type_is_json():
response = client.post( response = client.post(
"/items/", "/items/",
data='{"name": "Foo", "price": 50.5}', content='{"name": "Foo", "price": 50.5}',
) )
assert response.status_code == 200, response.text assert response.status_code == 200, response.text
assert response.json() == { assert response.json() == {
@ -255,17 +255,19 @@ def test_wrong_headers():
] ]
} }
response = client.post("/items/", data=data, headers={"Content-Type": "text/plain"}) response = client.post(
"/items/", content=data, headers={"Content-Type": "text/plain"}
)
assert response.status_code == 422, response.text assert response.status_code == 422, response.text
assert response.json() == invalid_dict assert response.json() == invalid_dict
response = client.post( response = client.post(
"/items/", data=data, headers={"Content-Type": "application/geo+json-seq"} "/items/", content=data, headers={"Content-Type": "application/geo+json-seq"}
) )
assert response.status_code == 422, response.text assert response.status_code == 422, response.text
assert response.json() == invalid_dict assert response.json() == invalid_dict
response = client.post( response = client.post(
"/items/", data=data, headers={"Content-Type": "application/not-really-json"} "/items/", content=data, headers={"Content-Type": "application/not-really-json"}
) )
assert response.status_code == 422, response.text assert response.status_code == 422, response.text
assert response.json() == invalid_dict assert response.json() == invalid_dict

16
tests/test_tutorial/test_body/test_tutorial001_py310.py

@ -185,7 +185,7 @@ def test_post_broken_body(client: TestClient):
response = client.post( response = client.post(
"/items/", "/items/",
headers={"content-type": "application/json"}, headers={"content-type": "application/json"},
data="{some broken json}", content="{some broken json}",
) )
assert response.status_code == 422, response.text assert response.status_code == 422, response.text
assert response.json() == { assert response.json() == {
@ -225,7 +225,7 @@ def test_post_form_for_json(client: TestClient):
def test_explicit_content_type(client: TestClient): def test_explicit_content_type(client: TestClient):
response = client.post( response = client.post(
"/items/", "/items/",
data='{"name": "Foo", "price": 50.5}', content='{"name": "Foo", "price": 50.5}',
headers={"Content-Type": "application/json"}, headers={"Content-Type": "application/json"},
) )
assert response.status_code == 200, response.text assert response.status_code == 200, response.text
@ -235,7 +235,7 @@ def test_explicit_content_type(client: TestClient):
def test_geo_json(client: TestClient): def test_geo_json(client: TestClient):
response = client.post( response = client.post(
"/items/", "/items/",
data='{"name": "Foo", "price": 50.5}', content='{"name": "Foo", "price": 50.5}',
headers={"Content-Type": "application/geo+json"}, headers={"Content-Type": "application/geo+json"},
) )
assert response.status_code == 200, response.text assert response.status_code == 200, response.text
@ -245,7 +245,7 @@ def test_geo_json(client: TestClient):
def test_no_content_type_is_json(client: TestClient): def test_no_content_type_is_json(client: TestClient):
response = client.post( response = client.post(
"/items/", "/items/",
data='{"name": "Foo", "price": 50.5}', content='{"name": "Foo", "price": 50.5}',
) )
assert response.status_code == 200, response.text assert response.status_code == 200, response.text
assert response.json() == { assert response.json() == {
@ -269,17 +269,19 @@ def test_wrong_headers(client: TestClient):
] ]
} }
response = client.post("/items/", data=data, headers={"Content-Type": "text/plain"}) response = client.post(
"/items/", content=data, headers={"Content-Type": "text/plain"}
)
assert response.status_code == 422, response.text assert response.status_code == 422, response.text
assert response.json() == invalid_dict assert response.json() == invalid_dict
response = client.post( response = client.post(
"/items/", data=data, headers={"Content-Type": "application/geo+json-seq"} "/items/", content=data, headers={"Content-Type": "application/geo+json-seq"}
) )
assert response.status_code == 422, response.text assert response.status_code == 422, response.text
assert response.json() == invalid_dict assert response.json() == invalid_dict
response = client.post( response = client.post(
"/items/", data=data, headers={"Content-Type": "application/not-really-json"} "/items/", content=data, headers={"Content-Type": "application/not-really-json"}
) )
assert response.status_code == 422, response.text assert response.status_code == 422, response.text
assert response.json() == invalid_dict assert response.json() == invalid_dict

5
tests/test_tutorial/test_cookie_params/test_tutorial001.py

@ -3,8 +3,6 @@ from fastapi.testclient import TestClient
from docs_src.cookie_params.tutorial001 import app from docs_src.cookie_params.tutorial001 import app
client = TestClient(app)
openapi_schema = { openapi_schema = {
"openapi": "3.0.2", "openapi": "3.0.2",
"info": {"title": "FastAPI", "version": "0.1.0"}, "info": {"title": "FastAPI", "version": "0.1.0"},
@ -88,6 +86,7 @@ openapi_schema = {
], ],
) )
def test(path, cookies, expected_status, expected_response): def test(path, cookies, expected_status, expected_response):
response = client.get(path, cookies=cookies) client = TestClient(app, cookies=cookies)
response = client.get(path)
assert response.status_code == expected_status assert response.status_code == expected_status
assert response.json() == expected_response assert response.json() == expected_response

15
tests/test_tutorial/test_cookie_params/test_tutorial001_py310.py

@ -70,14 +70,6 @@ openapi_schema = {
} }
@pytest.fixture(name="client")
def get_client():
from docs_src.cookie_params.tutorial001_py310 import app
client = TestClient(app)
return client
@needs_py310 @needs_py310
@pytest.mark.parametrize( @pytest.mark.parametrize(
"path,cookies,expected_status,expected_response", "path,cookies,expected_status,expected_response",
@ -94,7 +86,10 @@ def get_client():
("/items", {"session": "cookiesession"}, 200, {"ads_id": None}), ("/items", {"session": "cookiesession"}, 200, {"ads_id": None}),
], ],
) )
def test(path, cookies, expected_status, expected_response, client: TestClient): def test(path, cookies, expected_status, expected_response):
response = client.get(path, cookies=cookies) from docs_src.cookie_params.tutorial001_py310 import app
client = TestClient(app, cookies=cookies)
response = client.get(path)
assert response.status_code == expected_status assert response.status_code == expected_status
assert response.json() == expected_response assert response.json() == expected_response

2
tests/test_tutorial/test_custom_request_and_route/test_tutorial001.py

@ -26,7 +26,7 @@ def test_gzip_request(compress):
data = gzip.compress(data) data = gzip.compress(data)
headers["Content-Encoding"] = "gzip" headers["Content-Encoding"] = "gzip"
headers["Content-Type"] = "application/json" headers["Content-Type"] = "application/json"
response = client.post("/sum", data=data, headers=headers) response = client.post("/sum", content=data, headers=headers)
assert response.json() == {"sum": n} assert response.json() == {"sum": n}

2
tests/test_tutorial/test_custom_response/test_tutorial006.py

@ -32,6 +32,6 @@ def test_openapi_schema():
def test_get(): def test_get():
response = client.get("/typer", allow_redirects=False) response = client.get("/typer", follow_redirects=False)
assert response.status_code == 307, response.text assert response.status_code == 307, response.text
assert response.headers["location"] == "https://typer.tiangolo.com" assert response.headers["location"] == "https://typer.tiangolo.com"

2
tests/test_tutorial/test_custom_response/test_tutorial006b.py

@ -27,6 +27,6 @@ def test_openapi_schema():
def test_redirect_response_class(): def test_redirect_response_class():
response = client.get("/fastapi", allow_redirects=False) response = client.get("/fastapi", follow_redirects=False)
assert response.status_code == 307 assert response.status_code == 307
assert response.headers["location"] == "https://fastapi.tiangolo.com" assert response.headers["location"] == "https://fastapi.tiangolo.com"

2
tests/test_tutorial/test_custom_response/test_tutorial006c.py

@ -27,6 +27,6 @@ def test_openapi_schema():
def test_redirect_status_code(): def test_redirect_status_code():
response = client.get("/pydantic", allow_redirects=False) response = client.get("/pydantic", follow_redirects=False)
assert response.status_code == 302 assert response.status_code == 302
assert response.headers["location"] == "https://pydantic-docs.helpmanual.io/" assert response.headers["location"] == "https://pydantic-docs.helpmanual.io/"

2
tests/test_tutorial/test_path_operation_advanced_configurations/test_tutorial006.py

@ -47,7 +47,7 @@ def test_openapi_schema():
def test_post(): def test_post():
response = client.post("/items/", data=b"this is actually not validated") response = client.post("/items/", content=b"this is actually not validated")
assert response.status_code == 200, response.text assert response.status_code == 200, response.text
assert response.json() == { assert response.json() == {
"size": 30, "size": 30,

6
tests/test_tutorial/test_path_operation_advanced_configurations/test_tutorial007.py

@ -58,7 +58,7 @@ def test_post():
- x-men - x-men
- x-avengers - x-avengers
""" """
response = client.post("/items/", data=yaml_data) response = client.post("/items/", content=yaml_data)
assert response.status_code == 200, response.text assert response.status_code == 200, response.text
assert response.json() == { assert response.json() == {
"name": "Deadpoolio", "name": "Deadpoolio",
@ -74,7 +74,7 @@ def test_post_broken_yaml():
x - x-men x - x-men
x - x-avengers x - x-avengers
""" """
response = client.post("/items/", data=yaml_data) response = client.post("/items/", content=yaml_data)
assert response.status_code == 422, response.text assert response.status_code == 422, response.text
assert response.json() == {"detail": "Invalid YAML"} assert response.json() == {"detail": "Invalid YAML"}
@ -88,7 +88,7 @@ def test_post_invalid():
- x-avengers - x-avengers
- sneaky: object - sneaky: object
""" """
response = client.post("/items/", data=yaml_data) response = client.post("/items/", content=yaml_data)
assert response.status_code == 422, response.text assert response.status_code == 422, response.text
assert response.json() == { assert response.json() == {
"detail": [ "detail": [

4
tests/test_tutorial/test_security/test_tutorial006.py

@ -1,7 +1,6 @@
from base64 import b64encode from base64 import b64encode
from fastapi.testclient import TestClient from fastapi.testclient import TestClient
from requests.auth import HTTPBasicAuth
from docs_src.security.tutorial006 import app from docs_src.security.tutorial006 import app
@ -38,8 +37,7 @@ def test_openapi_schema():
def test_security_http_basic(): def test_security_http_basic():
auth = HTTPBasicAuth(username="john", password="secret") response = client.get("/users/me", auth=("john", "secret"))
response = client.get("/users/me", auth=auth)
assert response.status_code == 200, response.text assert response.status_code == 200, response.text
assert response.json() == {"username": "john", "password": "secret"} assert response.json() == {"username": "john", "password": "secret"}

12
tests/test_tutorial/test_websockets/test_tutorial002.py

@ -4,20 +4,18 @@ from fastapi.websockets import WebSocketDisconnect
from docs_src.websockets.tutorial002 import app from docs_src.websockets.tutorial002 import app
client = TestClient(app)
def test_main(): def test_main():
client = TestClient(app)
response = client.get("/") response = client.get("/")
assert response.status_code == 200, response.text assert response.status_code == 200, response.text
assert b"<!DOCTYPE html>" in response.content assert b"<!DOCTYPE html>" in response.content
def test_websocket_with_cookie(): def test_websocket_with_cookie():
client = TestClient(app, cookies={"session": "fakesession"})
with pytest.raises(WebSocketDisconnect): with pytest.raises(WebSocketDisconnect):
with client.websocket_connect( with client.websocket_connect("/items/foo/ws") as websocket:
"/items/foo/ws", cookies={"session": "fakesession"}
) as websocket:
message = "Message one" message = "Message one"
websocket.send_text(message) websocket.send_text(message)
data = websocket.receive_text() data = websocket.receive_text()
@ -33,6 +31,7 @@ def test_websocket_with_cookie():
def test_websocket_with_header(): def test_websocket_with_header():
client = TestClient(app)
with pytest.raises(WebSocketDisconnect): with pytest.raises(WebSocketDisconnect):
with client.websocket_connect("/items/bar/ws?token=some-token") as websocket: with client.websocket_connect("/items/bar/ws?token=some-token") as websocket:
message = "Message one" message = "Message one"
@ -50,6 +49,7 @@ def test_websocket_with_header():
def test_websocket_with_header_and_query(): def test_websocket_with_header_and_query():
client = TestClient(app)
with pytest.raises(WebSocketDisconnect): with pytest.raises(WebSocketDisconnect):
with client.websocket_connect("/items/2/ws?q=3&token=some-token") as websocket: with client.websocket_connect("/items/2/ws?q=3&token=some-token") as websocket:
message = "Message one" message = "Message one"
@ -71,6 +71,7 @@ def test_websocket_with_header_and_query():
def test_websocket_no_credentials(): def test_websocket_no_credentials():
client = TestClient(app)
with pytest.raises(WebSocketDisconnect): with pytest.raises(WebSocketDisconnect):
with client.websocket_connect("/items/foo/ws"): with client.websocket_connect("/items/foo/ws"):
pytest.fail( pytest.fail(
@ -79,6 +80,7 @@ def test_websocket_no_credentials():
def test_websocket_invalid_data(): def test_websocket_invalid_data():
client = TestClient(app)
with pytest.raises(WebSocketDisconnect): with pytest.raises(WebSocketDisconnect):
with client.websocket_connect("/items/foo/ws?q=bar&token=some-token"): with client.websocket_connect("/items/foo/ws?q=bar&token=some-token"):
pytest.fail( pytest.fail(

2
tests/test_ws_router.py

@ -111,7 +111,7 @@ def test_router_ws_depends():
def test_router_ws_depends_with_override(): def test_router_ws_depends_with_override():
client = TestClient(app) client = TestClient(app)
app.dependency_overrides[ws_dependency] = lambda: "Override" app.dependency_overrides[ws_dependency] = lambda: "Override" # noqa: E731
with client.websocket_connect("/router-ws-depends/") as websocket: with client.websocket_connect("/router-ws-depends/") as websocket:
assert websocket.receive_text() == "Override" assert websocket.receive_text() == "Override"

Loading…
Cancel
Save