Compare commits

..

37 Commits

Author SHA1 Message Date
Sebastián Ramírez
8cc967a760 🔖 Release version 0.95.2 2023-05-16 15:39:43 +02:00
Sebastián Ramírez
6d235d1fe1 📝 Update release notes 2023-05-16 15:38:23 +02:00
github-actions
66259ddbb5 📝 Update release notes 2023-05-16 13:30:24 +00:00
Sebastián Ramírez
b4535abe8f ⬆️ Upgrade Starlette version to >=0.27.0 for a security release (#9541) 2023-05-16 13:29:40 +00:00
github-actions
d59c27d017 📝 Update release notes 2023-05-09 14:32:48 +00:00
Samuel Colvin
5100a98ccd 🐛 Fix flask.escape warning for internal tests (#9468)
* fix flask.escape warning

* 📝 Fix highlight in docs for WSGI

---------

Co-authored-by: Sebastián Ramírez <tiangolo@gmail.com>
2023-05-09 14:32:00 +00:00
github-actions
fe55402776 📝 Update release notes 2023-05-08 21:36:55 +00:00
Sebastián Ramírez
f00f0de9ca Refactor 2 tests, for consistency and simplification (#9504)
 Refactor tests, for consistency and simplification
2023-05-08 23:36:13 +02:00
github-actions
028e7cad67 📝 Update release notes 2023-05-08 21:08:08 +00:00
Sebastián Ramírez
50c1a928fb Refactor OpenAPI tests, prepare for Pydantic v2 (#9503)
*  Refactor OpenAPI tests, move inline, prepare for Pydantic v2 tests

*  Fix test module loading for conditional OpenAPI

* 🐛 Fix missing pytest marker

*  Fix test for coverage
2023-05-08 23:07:32 +02:00
Bighneswar Parida
724060df43 📝 Update Deta deployment tutorial for compatibility with Deta Space (#6004)
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Co-authored-by: Lemonyte <49930425+lemonyte@users.noreply.github.com>
Co-authored-by: xeust <maxeust@gmail.com>
Co-authored-by: Sebastián Ramírez <tiangolo@gmail.com>
2023-05-08 14:32:02 +02:00
github-actions
490bde7169 📝 Update release notes 2023-05-08 11:18:04 +00:00
github-actions
3f5cfdc3fe 📝 Update release notes 2023-05-08 11:17:05 +00:00
oandersonmagalhaes
f2d01d7a6a 🌐 Add Portuguese translation for docs/pt/docs/advanced/events.md (#9326)
Co-authored-by: Fernando Crozetta <fernando@czetta.com>
Co-authored-by: Lorhan Sohaky <16273730+LorhanSohaky@users.noreply.github.com>
2023-05-08 13:16:57 +02:00
Vladislav Kramorenko
42ca1cb42d 🌐 Add Russian translation for docs/ru/docs/deployment/manually.md (#9417)
Co-authored-by: Artem Golicyn <86262613+AGolicyn@users.noreply.github.com>
Co-authored-by: Sebastián Ramírez <tiangolo@gmail.com>
2023-05-08 11:16:31 +00:00
github-actions
bdb32bfe03 📝 Update release notes 2023-05-08 11:16:10 +00:00
Saleumsack KEOBOUALAY
ed1f93f803 🌐 Add setup for translations to Lao (#9396)
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2023-05-08 13:15:37 +02:00
github-actions
33fa1b0927 📝 Update release notes 2023-05-08 11:14:57 +00:00
Vladislav Kramorenko
928bb2e2ce 🌐 Add Russian translation for docs/ru/docs/tutorial/testing.md (#9403)
Add Russian translation for docs/ru/docs/tutorial/testing.md
2023-05-08 13:14:19 +02:00
github-actions
778b909cc1 📝 Update release notes 2023-05-08 11:11:35 +00:00
Vladislav Kramorenko
d56f02a986 🌐 Add Russian translation for docs/ru/docs/deployment/https.md (#9428)
Co-authored-by: Artem Golicyn <86262613+AGolicyn@users.noreply.github.com>
Co-authored-by: Sebastián Ramírez <tiangolo@gmail.com>
2023-05-08 13:11:00 +02:00
github-actions
60ef67a66b 📝 Update release notes 2023-05-02 06:28:23 +00:00
MariiaRomaniuk
055cf356ca ✏ Fix command to install requirements in Windows (#9445)
fix command to install requirements
2023-05-01 23:27:49 -07:00
github-actions
8ac8d70d52 📝 Update release notes 2023-04-25 19:12:33 +00:00
Axel
eb1b858c4f 🌐 Add French translation for docs/fr/docs/advanced/response-directly.md (#9415)
Co-authored-by: Sebastián Ramírez <tiangolo@gmail.com>
2023-04-25 12:12:00 -07:00
github-actions
0ef0aa55b0 📝 Update release notes 2023-04-25 19:08:41 +00:00
Evzen Ptacek
0e75981bd0 🌐 Initiate Czech translation setup (#9288)
Initiate Czech translation setup

Co-authored-by: Sebastián Ramírez <tiangolo@gmail.com>
2023-04-25 12:08:01 -07:00
github-actions
1bc156482f 📝 Update release notes 2023-04-25 18:46:57 +00:00
Lucas Balieiro
6485c14c3b ✏ Fix typo in Portuguese docs for docs/pt/docs/index.md (#9337) 2023-04-25 11:46:17 -07:00
github-actions
bd51832394 📝 Update release notes 2023-04-25 18:45:08 +00:00
Nadezhda Fedina
da21ec92cf 🌐 Add Russian translation for docs/ru/docs/tutorial/response-status-code.md (#9370)
* Add Russian translation for docs/ru/docs/tutorial/response-status-code.md

* 🎨 [pre-commit.ci] Auto format from pre-commit.com hooks

* Apply suggestions from code review

Co-authored-by: Vladislav Kramorenko <85196001+Xewus@users.noreply.github.com>

* Replace 'response code' with 'status code', make minor translation improvements

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Co-authored-by: Vladislav Kramorenko <85196001+Xewus@users.noreply.github.com>
2023-04-25 11:44:34 -07:00
github-actions
7f3e0fe8a7 📝 Update release notes 2023-04-25 18:28:42 +00:00
github-actions
bde0316227 📝 Update release notes 2023-04-25 18:27:12 +00:00
github-actions
a73570a832 📝 Update release notes 2023-04-25 18:26:51 +00:00
Sebastián Ramírez
46726aa1c4 💚 Disable setup-python pip cache in CI (#9438) 2023-04-25 18:26:32 +00:00
dependabot[bot]
a3d881ab67 ⬆ Bump pypa/gh-action-pypi-publish from 1.6.4 to 1.8.5 (#9346)
Bumps [pypa/gh-action-pypi-publish](https://github.com/pypa/gh-action-pypi-publish) from 1.6.4 to 1.8.5.
- [Release notes](https://github.com/pypa/gh-action-pypi-publish/releases)
- [Commits](https://github.com/pypa/gh-action-pypi-publish/compare/v1.6.4...v1.8.5)

---
updated-dependencies:
- dependency-name: pypa/gh-action-pypi-publish
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-04-25 11:26:11 -07:00
dependabot[bot]
07f691ba4b ⬆ Bump dawidd6/action-download-artifact from 2.26.0 to 2.27.0 (#9394)
Bumps [dawidd6/action-download-artifact](https://github.com/dawidd6/action-download-artifact) from 2.26.0 to 2.27.0.
- [Release notes](https://github.com/dawidd6/action-download-artifact/releases)
- [Commits](https://github.com/dawidd6/action-download-artifact/compare/v2.26.0...v2.27.0)

---
updated-dependencies:
- dependency-name: dawidd6/action-download-artifact
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-04-25 11:25:28 -07:00
333 changed files with 37296 additions and 33840 deletions

View File

@@ -16,7 +16,7 @@ jobs:
rm -rf ./site
mkdir ./site
- name: Download Artifact Docs
uses: dawidd6/action-download-artifact@v2.26.0
uses: dawidd6/action-download-artifact@v2.27.0
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
workflow: build-docs.yml

View File

@@ -18,7 +18,8 @@ jobs:
uses: actions/setup-python@v4
with:
python-version: "3.7"
cache: "pip"
# Issue ref: https://github.com/actions/setup-python/issues/436
# cache: "pip"
cache-dependency-path: pyproject.toml
- uses: actions/cache@v3
id: cache
@@ -31,7 +32,7 @@ jobs:
- name: Build distribution
run: python -m build
- name: Publish
uses: pypa/gh-action-pypi-publish@v1.6.4
uses: pypa/gh-action-pypi-publish@v1.8.5
with:
password: ${{ secrets.PYPI_API_TOKEN }}
- name: Dump GitHub context

View File

@@ -20,7 +20,7 @@ jobs:
- run: pip install smokeshow
- uses: dawidd6/action-download-artifact@v2.26.0
- uses: dawidd6/action-download-artifact@v2.27.0
with:
workflow: test.yml
commit: ${{ github.event.workflow_run.head_sha }}

View File

@@ -21,7 +21,8 @@ jobs:
uses: actions/setup-python@v4
with:
python-version: ${{ matrix.python-version }}
cache: "pip"
# Issue ref: https://github.com/actions/setup-python/issues/436
# cache: "pip"
cache-dependency-path: pyproject.toml
- uses: actions/cache@v3
id: cache
@@ -54,7 +55,8 @@ jobs:
- uses: actions/setup-python@v4
with:
python-version: '3.8'
cache: "pip"
# Issue ref: https://github.com/actions/setup-python/issues/436
# cache: "pip"
cache-dependency-path: pyproject.toml
- name: Get coverage files

View File

@@ -40,6 +40,7 @@ nav:
- Languages:
- en: /
- az: /az/
- cs: /cs/
- de: /de/
- em: /em/
- es: /es/
@@ -51,6 +52,7 @@ nav:
- it: /it/
- ja: /ja/
- ko: /ko/
- lo: /lo/
- nl: /nl/
- pl: /pl/
- pt: /pt/
@@ -104,6 +106,8 @@ extra:
name: en - English
- link: /az/
name: az
- link: /cs/
name: cs
- link: /de/
name: de
- link: /em/
@@ -126,6 +130,8 @@ extra:
name: ja - 日本語
- link: /ko/
name: ko - 한국어
- link: /lo/
name: lo - ພາສາລາວ
- link: /nl/
name: nl
- link: /pl/

473
docs/cs/docs/index.md Normal file
View File

@@ -0,0 +1,473 @@
{!../../../docs/missing-translation.md!}
<p align="center">
<a href="https://fastapi.tiangolo.com"><img src="https://fastapi.tiangolo.com/img/logo-margin/logo-teal.png" alt="FastAPI"></a>
</p>
<p align="center">
<em>FastAPI framework, high performance, easy to learn, fast to code, ready for production</em>
</p>
<p align="center">
<a href="https://github.com/tiangolo/fastapi/actions?query=workflow%3ATest+event%3Apush+branch%3Amaster" target="_blank">
<img src="https://github.com/tiangolo/fastapi/workflows/Test/badge.svg?event=push&branch=master" alt="Test">
</a>
<a href="https://coverage-badge.samuelcolvin.workers.dev/redirect/tiangolo/fastapi" target="_blank">
<img src="https://coverage-badge.samuelcolvin.workers.dev/tiangolo/fastapi.svg" alt="Coverage">
</a>
<a href="https://pypi.org/project/fastapi" target="_blank">
<img src="https://img.shields.io/pypi/v/fastapi?color=%2334D058&label=pypi%20package" alt="Package version">
</a>
<a href="https://pypi.org/project/fastapi" target="_blank">
<img src="https://img.shields.io/pypi/pyversions/fastapi.svg?color=%2334D058" alt="Supported Python versions">
</a>
</p>
---
**Documentation**: <a href="https://fastapi.tiangolo.com" target="_blank">https://fastapi.tiangolo.com</a>
**Source Code**: <a href="https://github.com/tiangolo/fastapi" target="_blank">https://github.com/tiangolo/fastapi</a>
---
FastAPI is a modern, fast (high-performance), web framework for building APIs with Python 3.7+ based on standard Python type hints.
The key features are:
* **Fast**: Very high performance, on par with **NodeJS** and **Go** (thanks to Starlette and Pydantic). [One of the fastest Python frameworks available](#performance).
* **Fast to code**: Increase the speed to develop features by about 200% to 300%. *
* **Fewer bugs**: Reduce about 40% of human (developer) induced errors. *
* **Intuitive**: Great editor support. <abbr title="also known as auto-complete, autocompletion, IntelliSense">Completion</abbr> everywhere. Less time debugging.
* **Easy**: Designed to be easy to use and learn. Less time reading docs.
* **Short**: Minimize code duplication. Multiple features from each parameter declaration. Fewer bugs.
* **Robust**: Get production-ready code. With automatic interactive documentation.
* **Standards-based**: Based on (and fully compatible with) the open standards for APIs: <a href="https://github.com/OAI/OpenAPI-Specification" class="external-link" target="_blank">OpenAPI</a> (previously known as Swagger) and <a href="https://json-schema.org/" class="external-link" target="_blank">JSON Schema</a>.
<small>* estimation based on tests on an internal development team, building production applications.</small>
## Sponsors
<!-- sponsors -->
{% if sponsors %}
{% for sponsor in sponsors.gold -%}
<a href="{{ sponsor.url }}" target="_blank" title="{{ sponsor.title }}"><img src="{{ sponsor.img }}" style="border-radius:15px"></a>
{% endfor -%}
{%- for sponsor in sponsors.silver -%}
<a href="{{ sponsor.url }}" target="_blank" title="{{ sponsor.title }}"><img src="{{ sponsor.img }}" style="border-radius:15px"></a>
{% endfor %}
{% endif %}
<!-- /sponsors -->
<a href="https://fastapi.tiangolo.com/fastapi-people/#sponsors" class="external-link" target="_blank">Other sponsors</a>
## Opinions
"_[...] I'm using **FastAPI** a ton these days. [...] I'm actually planning to use it for all of my team's **ML services at Microsoft**. Some of them are getting integrated into the core **Windows** product and some **Office** products._"
<div style="text-align: right; margin-right: 10%;">Kabir Khan - <strong>Microsoft</strong> <a href="https://github.com/tiangolo/fastapi/pull/26" target="_blank"><small>(ref)</small></a></div>
---
"_We adopted the **FastAPI** library to spawn a **REST** server that can be queried to obtain **predictions**. [for Ludwig]_"
<div style="text-align: right; margin-right: 10%;">Piero Molino, Yaroslav Dudin, and Sai Sumanth Miryala - <strong>Uber</strong> <a href="https://eng.uber.com/ludwig-v0-2/" target="_blank"><small>(ref)</small></a></div>
---
"_**Netflix** is pleased to announce the open-source release of our **crisis management** orchestration framework: **Dispatch**! [built with **FastAPI**]_"
<div style="text-align: right; margin-right: 10%;">Kevin Glisson, Marc Vilanova, Forest Monsen - <strong>Netflix</strong> <a href="https://netflixtechblog.com/introducing-dispatch-da4b8a2a8072" target="_blank"><small>(ref)</small></a></div>
---
"_Im over the moon excited about **FastAPI**. Its so fun!_"
<div style="text-align: right; margin-right: 10%;">Brian Okken - <strong><a href="https://pythonbytes.fm/episodes/show/123/time-to-right-the-py-wrongs?time_in_sec=855" target="_blank">Python Bytes</a> podcast host</strong> <a href="https://twitter.com/brianokken/status/1112220079972728832" target="_blank"><small>(ref)</small></a></div>
---
"_Honestly, what you've built looks super solid and polished. In many ways, it's what I wanted **Hug** to be - it's really inspiring to see someone build that._"
<div style="text-align: right; margin-right: 10%;">Timothy Crosley - <strong><a href="https://www.hug.rest/" target="_blank">Hug</a> creator</strong> <a href="https://news.ycombinator.com/item?id=19455465" target="_blank"><small>(ref)</small></a></div>
---
"_If you're looking to learn one **modern framework** for building REST APIs, check out **FastAPI** [...] It's fast, easy to use and easy to learn [...]_"
"_We've switched over to **FastAPI** for our **APIs** [...] I think you'll like it [...]_"
<div style="text-align: right; margin-right: 10%;">Ines Montani - Matthew Honnibal - <strong><a href="https://explosion.ai" target="_blank">Explosion AI</a> founders - <a href="https://spacy.io" target="_blank">spaCy</a> creators</strong> <a href="https://twitter.com/_inesmontani/status/1144173225322143744" target="_blank"><small>(ref)</small></a> - <a href="https://twitter.com/honnibal/status/1144031421859655680" target="_blank"><small>(ref)</small></a></div>
---
"_If anyone is looking to build a production Python API, I would highly recommend **FastAPI**. It is **beautifully designed**, **simple to use** and **highly scalable**, it has become a **key component** in our API first development strategy and is driving many automations and services such as our Virtual TAC Engineer._"
<div style="text-align: right; margin-right: 10%;">Deon Pillsbury - <strong>Cisco</strong> <a href="https://www.linkedin.com/posts/deonpillsbury_cisco-cx-python-activity-6963242628536487936-trAp/" target="_blank"><small>(ref)</small></a></div>
---
## **Typer**, the FastAPI of CLIs
<a href="https://typer.tiangolo.com" target="_blank"><img src="https://typer.tiangolo.com/img/logo-margin/logo-margin-vector.svg" style="width: 20%;"></a>
If you are building a <abbr title="Command Line Interface">CLI</abbr> app to be used in the terminal instead of a web API, check out <a href="https://typer.tiangolo.com/" class="external-link" target="_blank">**Typer**</a>.
**Typer** is FastAPI's little sibling. And it's intended to be the **FastAPI of CLIs**. ⌨️ 🚀
## Requirements
Python 3.7+
FastAPI stands on the shoulders of giants:
* <a href="https://www.starlette.io/" class="external-link" target="_blank">Starlette</a> for the web parts.
* <a href="https://pydantic-docs.helpmanual.io/" class="external-link" target="_blank">Pydantic</a> for the data parts.
## Installation
<div class="termy">
```console
$ pip install fastapi
---> 100%
```
</div>
You will also need an ASGI server, for production such as <a href="https://www.uvicorn.org" class="external-link" target="_blank">Uvicorn</a> or <a href="https://github.com/pgjones/hypercorn" class="external-link" target="_blank">Hypercorn</a>.
<div class="termy">
```console
$ pip install "uvicorn[standard]"
---> 100%
```
</div>
## Example
### Create it
* Create a file `main.py` with:
```Python
from typing import Union
from fastapi import FastAPI
app = FastAPI()
@app.get("/")
def read_root():
return {"Hello": "World"}
@app.get("/items/{item_id}")
def read_item(item_id: int, q: Union[str, None] = None):
return {"item_id": item_id, "q": q}
```
<details markdown="1">
<summary>Or use <code>async def</code>...</summary>
If your code uses `async` / `await`, use `async def`:
```Python hl_lines="9 14"
from typing import Union
from fastapi import FastAPI
app = FastAPI()
@app.get("/")
async def read_root():
return {"Hello": "World"}
@app.get("/items/{item_id}")
async def read_item(item_id: int, q: Union[str, None] = None):
return {"item_id": item_id, "q": q}
```
**Note**:
If you don't know, check the _"In a hurry?"_ section about <a href="https://fastapi.tiangolo.com/async/#in-a-hurry" target="_blank">`async` and `await` in the docs</a>.
</details>
### Run it
Run the server with:
<div class="termy">
```console
$ uvicorn main:app --reload
INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
INFO: Started reloader process [28720]
INFO: Started server process [28722]
INFO: Waiting for application startup.
INFO: Application startup complete.
```
</div>
<details markdown="1">
<summary>About the command <code>uvicorn main:app --reload</code>...</summary>
The command `uvicorn main:app` refers to:
* `main`: the file `main.py` (the Python "module").
* `app`: the object created inside of `main.py` with the line `app = FastAPI()`.
* `--reload`: make the server restart after code changes. Only do this for development.
</details>
### Check it
Open your browser at <a href="http://127.0.0.1:8000/items/5?q=somequery" class="external-link" target="_blank">http://127.0.0.1:8000/items/5?q=somequery</a>.
You will see the JSON response as:
```JSON
{"item_id": 5, "q": "somequery"}
```
You already created an API that:
* Receives HTTP requests in the _paths_ `/` and `/items/{item_id}`.
* Both _paths_ take `GET` <em>operations</em> (also known as HTTP _methods_).
* The _path_ `/items/{item_id}` has a _path parameter_ `item_id` that should be an `int`.
* The _path_ `/items/{item_id}` has an optional `str` _query parameter_ `q`.
### Interactive API docs
Now go to <a href="http://127.0.0.1:8000/docs" class="external-link" target="_blank">http://127.0.0.1:8000/docs</a>.
You will see the automatic interactive API documentation (provided by <a href="https://github.com/swagger-api/swagger-ui" class="external-link" target="_blank">Swagger UI</a>):
![Swagger UI](https://fastapi.tiangolo.com/img/index/index-01-swagger-ui-simple.png)
### Alternative API docs
And now, go to <a href="http://127.0.0.1:8000/redoc" class="external-link" target="_blank">http://127.0.0.1:8000/redoc</a>.
You will see the alternative automatic documentation (provided by <a href="https://github.com/Rebilly/ReDoc" class="external-link" target="_blank">ReDoc</a>):
![ReDoc](https://fastapi.tiangolo.com/img/index/index-02-redoc-simple.png)
## Example upgrade
Now modify the file `main.py` to receive a body from a `PUT` request.
Declare the body using standard Python types, thanks to Pydantic.
```Python hl_lines="4 9-12 25-27"
from typing import Union
from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI()
class Item(BaseModel):
name: str
price: float
is_offer: Union[bool, None] = None
@app.get("/")
def read_root():
return {"Hello": "World"}
@app.get("/items/{item_id}")
def read_item(item_id: int, q: Union[str, None] = None):
return {"item_id": item_id, "q": q}
@app.put("/items/{item_id}")
def update_item(item_id: int, item: Item):
return {"item_name": item.name, "item_id": item_id}
```
The server should reload automatically (because you added `--reload` to the `uvicorn` command above).
### Interactive API docs upgrade
Now go to <a href="http://127.0.0.1:8000/docs" class="external-link" target="_blank">http://127.0.0.1:8000/docs</a>.
* The interactive API documentation will be automatically updated, including the new body:
![Swagger UI](https://fastapi.tiangolo.com/img/index/index-03-swagger-02.png)
* Click on the button "Try it out", it allows you to fill the parameters and directly interact with the API:
![Swagger UI interaction](https://fastapi.tiangolo.com/img/index/index-04-swagger-03.png)
* Then click on the "Execute" button, the user interface will communicate with your API, send the parameters, get the results and show them on the screen:
![Swagger UI interaction](https://fastapi.tiangolo.com/img/index/index-05-swagger-04.png)
### Alternative API docs upgrade
And now, go to <a href="http://127.0.0.1:8000/redoc" class="external-link" target="_blank">http://127.0.0.1:8000/redoc</a>.
* The alternative documentation will also reflect the new query parameter and body:
![ReDoc](https://fastapi.tiangolo.com/img/index/index-06-redoc-02.png)
### Recap
In summary, you declare **once** the types of parameters, body, etc. as function parameters.
You do that with standard modern Python types.
You don't have to learn a new syntax, the methods or classes of a specific library, etc.
Just standard **Python 3.7+**.
For example, for an `int`:
```Python
item_id: int
```
or for a more complex `Item` model:
```Python
item: Item
```
...and with that single declaration you get:
* Editor support, including:
* Completion.
* Type checks.
* Validation of data:
* Automatic and clear errors when the data is invalid.
* Validation even for deeply nested JSON objects.
* <abbr title="also known as: serialization, parsing, marshalling">Conversion</abbr> of input data: coming from the network to Python data and types. Reading from:
* JSON.
* Path parameters.
* Query parameters.
* Cookies.
* Headers.
* Forms.
* Files.
* <abbr title="also known as: serialization, parsing, marshalling">Conversion</abbr> of output data: converting from Python data and types to network data (as JSON):
* Convert Python types (`str`, `int`, `float`, `bool`, `list`, etc).
* `datetime` objects.
* `UUID` objects.
* Database models.
* ...and many more.
* Automatic interactive API documentation, including 2 alternative user interfaces:
* Swagger UI.
* ReDoc.
---
Coming back to the previous code example, **FastAPI** will:
* Validate that there is an `item_id` in the path for `GET` and `PUT` requests.
* Validate that the `item_id` is of type `int` for `GET` and `PUT` requests.
* If it is not, the client will see a useful, clear error.
* Check if there is an optional query parameter named `q` (as in `http://127.0.0.1:8000/items/foo?q=somequery`) for `GET` requests.
* As the `q` parameter is declared with `= None`, it is optional.
* Without the `None` it would be required (as is the body in the case with `PUT`).
* For `PUT` requests to `/items/{item_id}`, Read the body as JSON:
* Check that it has a required attribute `name` that should be a `str`.
* Check that it has a required attribute `price` that has to be a `float`.
* Check that it has an optional attribute `is_offer`, that should be a `bool`, if present.
* All this would also work for deeply nested JSON objects.
* Convert from and to JSON automatically.
* Document everything with OpenAPI, that can be used by:
* Interactive documentation systems.
* Automatic client code generation systems, for many languages.
* Provide 2 interactive documentation web interfaces directly.
---
We just scratched the surface, but you already get the idea of how it all works.
Try changing the line with:
```Python
return {"item_name": item.name, "item_id": item_id}
```
...from:
```Python
... "item_name": item.name ...
```
...to:
```Python
... "item_price": item.price ...
```
...and see how your editor will auto-complete the attributes and know their types:
![editor support](https://fastapi.tiangolo.com/img/vscode-completion.png)
For a more complete example including more features, see the <a href="https://fastapi.tiangolo.com/tutorial/">Tutorial - User Guide</a>.
**Spoiler alert**: the tutorial - user guide includes:
* Declaration of **parameters** from other different places as: **headers**, **cookies**, **form fields** and **files**.
* How to set **validation constraints** as `maximum_length` or `regex`.
* A very powerful and easy to use **<abbr title="also known as components, resources, providers, services, injectables">Dependency Injection</abbr>** system.
* Security and authentication, including support for **OAuth2** with **JWT tokens** and **HTTP Basic** auth.
* More advanced (but equally easy) techniques for declaring **deeply nested JSON models** (thanks to Pydantic).
* **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:
* **WebSockets**
* extremely easy tests based on HTTPX and `pytest`
* **CORS**
* **Cookie Sessions**
* ...and more.
## Performance
Independent TechEmpower benchmarks show **FastAPI** applications running under Uvicorn as <a href="https://www.techempower.com/benchmarks/#section=test&runid=7464e520-0dc2-473d-bd34-dbdfd7e85911&hw=ph&test=query&l=zijzen-7" class="external-link" target="_blank">one of the fastest Python frameworks available</a>, only below Starlette and Uvicorn themselves (used internally by FastAPI). (*)
To understand more about it, see the section <a href="https://fastapi.tiangolo.com/benchmarks/" class="internal-link" target="_blank">Benchmarks</a>.
## Optional Dependencies
Used by Pydantic:
* <a href="https://github.com/esnme/ultrajson" target="_blank"><code>ujson</code></a> - for faster JSON <abbr title="converting the string that comes from an HTTP request into Python data">"parsing"</abbr>.
* <a href="https://github.com/JoshData/python-email-validator" target="_blank"><code>email_validator</code></a> - for email validation.
Used by Starlette:
* <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://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://pyyaml.org/wiki/PyYAMLDocumentation" target="_blank"><code>pyyaml</code></a> - Required for Starlette's `SchemaGenerator` support (you probably don't need it with FastAPI).
* <a href="https://github.com/esnme/ultrajson" target="_blank"><code>ujson</code></a> - Required if you want to use `UJSONResponse`.
Used by FastAPI / Starlette:
* <a href="https://www.uvicorn.org" target="_blank"><code>uvicorn</code></a> - for the server that loads and serves your application.
* <a href="https://github.com/ijl/orjson" target="_blank"><code>orjson</code></a> - Required if you want to use `ORJSONResponse`.
You can install all of these with `pip install "fastapi[all]"`.
## License
This project is licensed under the terms of the MIT license.

154
docs/cs/mkdocs.yml Normal file
View File

@@ -0,0 +1,154 @@
site_name: FastAPI
site_description: FastAPI framework, high performance, easy to learn, fast to code, ready for production
site_url: https://fastapi.tiangolo.com/cs/
theme:
name: material
custom_dir: overrides
palette:
- media: '(prefers-color-scheme: light)'
scheme: default
primary: teal
accent: amber
toggle:
icon: material/lightbulb
name: Switch to light mode
- media: '(prefers-color-scheme: dark)'
scheme: slate
primary: teal
accent: amber
toggle:
icon: material/lightbulb-outline
name: Switch to dark mode
features:
- search.suggest
- search.highlight
- content.tabs.link
icon:
repo: fontawesome/brands/github-alt
logo: https://fastapi.tiangolo.com/img/icon-white.svg
favicon: https://fastapi.tiangolo.com/img/favicon.png
language: cs
repo_name: tiangolo/fastapi
repo_url: https://github.com/tiangolo/fastapi
edit_uri: ''
plugins:
- search
- markdownextradata:
data: data
nav:
- FastAPI: index.md
- Languages:
- en: /
- az: /az/
- cs: /cs/
- de: /de/
- es: /es/
- fa: /fa/
- fr: /fr/
- he: /he/
- hy: /hy/
- id: /id/
- it: /it/
- ja: /ja/
- ko: /ko/
- nl: /nl/
- pl: /pl/
- pt: /pt/
- ru: /ru/
- sq: /sq/
- sv: /sv/
- ta: /ta/
- tr: /tr/
- uk: /uk/
- zh: /zh/
markdown_extensions:
- toc:
permalink: true
- markdown.extensions.codehilite:
guess_lang: false
- mdx_include:
base_path: docs
- admonition
- codehilite
- extra
- pymdownx.superfences:
custom_fences:
- name: mermaid
class: mermaid
format: !!python/name:pymdownx.superfences.fence_code_format ''
- pymdownx.tabbed:
alternate_style: true
- attr_list
- md_in_html
extra:
analytics:
provider: google
property: G-YNEVN69SC3
social:
- icon: fontawesome/brands/github-alt
link: https://github.com/tiangolo/fastapi
- icon: fontawesome/brands/discord
link: https://discord.gg/VQjSZaeJmf
- icon: fontawesome/brands/twitter
link: https://twitter.com/fastapi
- icon: fontawesome/brands/linkedin
link: https://www.linkedin.com/in/tiangolo
- icon: fontawesome/brands/dev
link: https://dev.to/tiangolo
- icon: fontawesome/brands/medium
link: https://medium.com/@tiangolo
- icon: fontawesome/solid/globe
link: https://tiangolo.com
alternate:
- link: /
name: en - English
- link: /az/
name: az
- link: /cs/
name: cs
- link: /de/
name: de
- link: /es/
name: es - español
- link: /fa/
name: fa
- link: /fr/
name: fr - français
- link: /he/
name: he
- link: /hy/
name: hy
- link: /id/
name: id
- link: /it/
name: it - italiano
- link: /ja/
name: ja - 日本語
- link: /ko/
name: ko - 한국어
- link: /nl/
name: nl
- link: /pl/
name: pl
- link: /pt/
name: pt - português
- link: /ru/
name: ru - русский язык
- link: /sq/
name: sq - shqip
- link: /sv/
name: sv - svenska
- link: /ta/
name: ta - தமிழ்
- link: /tr/
name: tr - Türkçe
- link: /uk/
name: uk - українська мова
- link: /zh/
name: zh - 汉语
extra_css:
- https://fastapi.tiangolo.com/css/termynal.css
- https://fastapi.tiangolo.com/css/custom.css
extra_javascript:
- https://fastapi.tiangolo.com/js/termynal.js
- https://fastapi.tiangolo.com/js/custom.js

0
docs/cs/overrides/.gitignore vendored Normal file
View File

View File

@@ -40,6 +40,7 @@ nav:
- Languages:
- en: /
- az: /az/
- cs: /cs/
- de: /de/
- em: /em/
- es: /es/
@@ -51,6 +52,7 @@ nav:
- it: /it/
- ja: /ja/
- ko: /ko/
- lo: /lo/
- nl: /nl/
- pl: /pl/
- pt: /pt/
@@ -105,6 +107,8 @@ extra:
name: en - English
- link: /az/
name: az
- link: /cs/
name: cs
- link: /de/
name: de
- link: /em/
@@ -127,6 +131,8 @@ extra:
name: ja - 日本語
- link: /ko/
name: ko - 한국어
- link: /lo/
name: lo - ພາສາລາວ
- link: /nl/
name: nl
- link: /pl/

View File

@@ -51,6 +51,7 @@ nav:
- it: /it/
- ja: /ja/
- ko: /ko/
- lo: /lo/
- nl: /nl/
- pl: /pl/
- pt: /pt/
@@ -233,6 +234,8 @@ extra:
name: ja - 日本語
- link: /ko/
name: ko - 한국어
- link: /lo/
name: lo - ພາສາລາວ
- link: /nl/
name: nl
- link: /pl/

View File

@@ -12,7 +12,7 @@ Then wrap the WSGI (e.g. Flask) app with the middleware.
And then mount that under a path.
```Python hl_lines="2-3 22"
```Python hl_lines="2-3 23"
{!../../../docs_src/wsgi/tutorial001.py!}
```

View File

@@ -108,7 +108,7 @@ After activating the environment as described above:
<div class="termy">
```console
$ pip install -e ."[dev,doc,test]"
$ pip install -e ".[dev,doc,test]"
---> 100%
```

View File

@@ -1,15 +1,22 @@
# Deploy FastAPI on Deta
# Deploy FastAPI on Deta Space
In this section you will learn how to easily deploy a **FastAPI** application on <a href="https://www.deta.sh/?ref=fastapi" class="external-link" target="_blank">Deta</a> using the free plan. 🎁
In this section you will learn how to easily deploy a **FastAPI** application on <a href="https://deta.space?ref=fastapi" class="external-link" target="_blank">Deta Space</a>, for free. 🎁
It will take you about **10 minutes**.
It will take you about **10 minutes** to deploy an API that you can use. After that, you can optionally release it to anyone.
Let's dive in.
!!! info
<a href="https://www.deta.sh/?ref=fastapi" class="external-link" target="_blank">Deta</a> is a **FastAPI** sponsor. 🎉
<a href="https://deta.space?ref=fastapi" class="external-link" target="_blank">Deta</a> is a **FastAPI** sponsor. 🎉
## A basic **FastAPI** app
## A simple **FastAPI** app
* Create a directory for your app, for example, `./fastapideta/` and enter into it.
* To start, create an empty directory with the name of your app, for example `./fastapi-deta/`, and then navigate into it.
```console
$ mkdir fastapi-deta
$ cd fastapi-deta
```
### FastAPI code
@@ -37,14 +44,12 @@ Now, in the same directory create a file `requirements.txt` with:
```text
fastapi
uvicorn[standard]
```
!!! tip
You don't need to install Uvicorn to deploy on Deta, although you would probably want to install it locally to test your app.
### Directory structure
You will now have one directory `./fastapideta/` with two files:
You will now have a directory `./fastapi-deta/` with two files:
```
.
@@ -52,22 +57,23 @@ You will now have one directory `./fastapideta/` with two files:
└── requirements.txt
```
## Create a free Deta account
## Create a free **Deta Space** account
Now create a <a href="https://www.deta.sh/?ref=fastapi" class="external-link" target="_blank">free account on Deta</a>, you just need an email and password.
Next, create a free account on <a href="https://deta.space/signup?dev_mode=true&ref=fastapi" class="external-link" target="_blank">Deta Space</a>, you just need an email and password.
You don't even need a credit card, but make sure **Developer Mode** is enabled when you sign up.
You don't even need a credit card.
## Install the CLI
Once you have your account, install the Deta <abbr title="Command Line Interface application">CLI</abbr>:
Once you have your account, install the Deta Space <abbr title="Command Line Interface application">CLI</abbr>:
=== "Linux, macOS"
<div class="termy">
```console
$ curl -fsSL https://get.deta.dev/cli.sh | sh
$ curl -fsSL https://get.deta.dev/space-cli.sh | sh
```
</div>
@@ -77,7 +83,7 @@ Once you have your account, install the Deta <abbr title="Command Line Interface
<div class="termy">
```console
$ iwr https://get.deta.dev/cli.ps1 -useb | iex
$ iwr https://get.deta.dev/space-cli.ps1 -useb | iex
```
</div>
@@ -89,95 +95,144 @@ In a new terminal, confirm that it was correctly installed with:
<div class="termy">
```console
$ deta --help
$ space --help
Deta command line interface for managing deta micros.
Complete documentation available at https://docs.deta.sh
Complete documentation available at https://deta.space/docs
Usage:
deta [flags]
deta [command]
space [flags]
space [command]
Available Commands:
auth Change auth settings for a deta micro
help Help about any command
link link code to project
login login to space
new create new project
push push code for project
release create release for a project
validate validate spacefile in dir
version Space CLI version
...
```
</div>
!!! tip
If you have problems installing the CLI, check the <a href="https://docs.deta.sh/docs/micros/getting_started?ref=fastapi" class="external-link" target="_blank">official Deta docs</a>.
If you have problems installing the CLI, check the official <a href="https://deta.space/docs/en/basics/cli?ref=fastapi" class="external-link" target="_blank">Deta Space Documentation</a>.
## Login with the CLI
Now login to Deta from the CLI with:
In order to authenticate your CLI with Deta Space, you will need an access token.
To obtain this token, open your <a href="https://deta.space/login?ref=fastapi" class="external-link" target="_blank">Deta Space Canvas</a>, open the **Teletype** (command bar at the bottom of the Canvas), and then click on **Settings**. From there, select **Generate Token** and copy the resulting token.
<img src="/img/deployment/deta/image03.png">
Now run `space login` from the Space CLI. Upon pasting the token into the CLI prompt and pressing enter, you should see a confirmation message.
<div class="termy">
```console
$ deta login
$ space login
Please, log in from the web page. Waiting..
Logged in successfully.
To authenticate the Space CLI with your Space account, generate a new access token in your Space settings and paste it below:
# Enter access token (41 chars) >$ *****************************************
👍 Login Successful!
```
</div>
This will open a web browser and authenticate automatically.
## Create a new project in Space
## Deploy with Deta
Now that you've authenticated with the Space CLI, use it to create a new <a href="https://deta.space/docs/en/basics/projects" class="external-link" target="_blank">Space Project</a>:
Next, deploy your application with the Deta CLI:
```console
$ space new
# What is your project's name? >$ fastapi-deta
```
The Space CLI will ask you to name the project, we will call ours `fastapi-deta`.
Then, it will try to automatically detect which framework or language you are using, showing you what it finds. In our case it will identify the Python app with the following message, prompting you to confirm:
```console
⚙️ No Spacefile found, trying to auto-detect configuration ...
👇 Deta detected the following configuration:
Micros:
name: fastapi-deta
L src: .
L engine: python3.9
# Do you want to bootstrap "fastapi-deta" with this configuration? (y/n)$ y
```
After you confirm, your project will be created in Deta Space inside a special app called <a href="https://deta.space/docs/en/basics/projects#projects-in-builder?ref=fastapi" class="external-link" target="_blank">Builder</a>. Builder is a toolbox that helps you to create and manage your apps in Deta Space.
The CLI will also create a `Spacefile` locally in the `fastapi-deta` directory. The <a href="https://deta.space/docs/en/reference/spacefile?ref=fastapi" class="external-link" target="_blank">Spacefile</a> is a configuration file which tells Deta Space how to run your app. The `Spacefile` for your app will be as follows:
```yaml
v: 0
micros:
- name: fastapi-deta
src: .
engine: python3.9
```
It is a `yaml` file, and you can use it to add features like scheduled tasks or modify how your app functions, which we'll do later. To learn more, read <a href="https://deta.space/docs/en/reference/spacefile" class="external-link" target="_blank">the `Spacefile` documentation</a>.
!!! tip
The Space CLI will also create a hidden `.space` folder in your local directory to link your local environment with Deta Space. This folder should not be included in your version control and will automatically be added to your `.gitignore` file, if you have initialized a Git repository.
## Define the run command in the Spacefile
The `run` command in the Spacefile tells Space what command should be executed to start your app. In this case it would be `uvicorn main:app`.
```diff
v: 0
micros:
- name: fastapi-deta
src: .
engine: python3.9
+ run: uvicorn main:app
```
## Deploy to Deta Space
To get your FastAPI live in the cloud, use one more CLI command:
<div class="termy">
```console
$ deta new
Successfully created a new micro
// Notice the "endpoint" 🔍
{
"name": "fastapideta",
"runtime": "python3.7",
"endpoint": "https://qltnci.deta.dev",
"visor": "enabled",
"http_auth": "enabled"
}
Adding dependencies...
$ space push
---> 100%
build complete... created revision: satyr-jvjk
Successfully installed fastapi-0.61.1 pydantic-1.7.2 starlette-0.13.6
Successfully pushed your code and created a new Revision!
Updating your development instance with the latest Revision, it will be available on your Canvas shortly.
```
</div>
You will see a JSON message similar to:
```JSON hl_lines="4"
{
"name": "fastapideta",
"runtime": "python3.7",
"endpoint": "https://qltnci.deta.dev",
"visor": "enabled",
"http_auth": "enabled"
}
```
This command will package your code, upload all the necessary files to Deta Space, and run a remote build of your app, resulting in a **revision**. Whenever you run `space push` successfully, a live instance of your API is automatically updated with the latest revision.
!!! tip
Your deployment will have a different `"endpoint"` URL.
You can manage your <a href="https://deta.space/docs/en/basics/revisions#whats-a-revision" class="external-link" target="_blank">revisions</a> by opening your project in the Builder app. The live copy of your API will be visible under the **Develop** tab in Builder.
## Check it
Now open your browser in your `endpoint` URL. In the example above it was `https://qltnci.deta.dev`, but yours will be different.
The live instance of your API will also be added automatically to your Canvas (the dashboard) on Deta Space.
You will see the JSON response from your FastAPI app:
<img src="/img/deployment/deta/image04.png">
Click on the new app called `fastapi-deta`, and it will open your API in a new browser tab on a URL like `https://fastapi-deta-gj7ka8.deta.app/`.
You will get a JSON response from your FastAPI app:
```JSON
{
@@ -185,74 +240,152 @@ You will see the JSON response from your FastAPI app:
}
```
And now go to the `/docs` for your API, in the example above it would be `https://qltnci.deta.dev/docs`.
And now you can head over to the `/docs` of your API. For this example, it would be `https://fastapi-deta-gj7ka8.deta.app/docs`.
It will show your docs like:
<img src="/img/deployment/deta/image01.png">
<img src="/img/deployment/deta/image05.png">
## Enable public access
By default, Deta will handle authentication using cookies for your account.
Deta will handle authentication for your account using cookies. By default, every app or API that you `push` or install to your Space is personal - it's only accessible to you.
But once you are ready, you can make it public with:
But you can also make your API public using the `Spacefile` from earlier.
With a `public_routes` parameter, you can specify which paths of your API should be available to the public.
Set your `public_routes` to `"*"` to open every route of your API to the public:
```yaml
v: 0
micros:
- name: fastapi-deta
src: .
engine: python3.9
public_routes:
- "/*"
```
Then run `space push` again to update your live API on Deta Space.
Once it deploys, you can share your URL with anyone and they will be able to access your API. 🚀
## HTTPS
Congrats! You deployed your FastAPI app to Deta Space! 🎉 🍰
Also, notice that Deta Space correctly handles HTTPS for you, so you don't have to take care of that and can be sure that your users will have a secure encrypted connection. ✅ 🔒
## Create a release
Space also allows you to publish your API. When you publish it, anyone else can install their own copy of your API, in their own Data Space cloud.
To do so, run `space release` in the Space CLI to create an **unlisted release**:
<div class="termy">
```console
$ deta auth disable
$ space release
Successfully disabled http auth
# Do you want to use the latest revision (buzzard-hczt)? (y/n)$ y
~ Creating a Release with the latest Revision
---> 100%
creating release...
publishing release in edge locations..
completed...
released: fastapi-deta-exp-msbu
https://deta.space/discovery/r/5kjhgyxewkdmtotx
Lift off -- successfully created a new Release!
Your Release is available globally on 5 Deta Edges
Anyone can install their own copy of your app.
```
</div>
Now you can share that URL with anyone and they will be able to access your API. 🚀
This command publishes your revision as a release and gives you a link. Anyone you give this link to can install your API.
## HTTPS
Congrats! You deployed your FastAPI app to Deta! 🎉 🍰
You can also make your app publicly discoverable by creating a **listed release** with `space release --listed` in the Space CLI:
Also, notice that Deta correctly handles HTTPS for you, so you don't have to take care of that and can be sure that your clients will have a secure encrypted connection. ✅ 🔒
<div class="termy">
## Check the Visor
```console
$ space release --listed
From your docs UI (they will be in a URL like `https://qltnci.deta.dev/docs`) send a request to your *path operation* `/items/{item_id}`.
# Do you want to use the latest revision (buzzard-hczt)? (y/n)$ y
For example with ID `5`.
~ Creating a listed Release with the latest Revision ...
Now go to <a href="https://web.deta.sh/" class="external-link" target="_blank">https://web.deta.sh</a>.
creating release...
publishing release in edge locations..
completed...
released: fastapi-deta-exp-msbu
https://deta.space/discovery/@user/fastapi-deta
You will see there's a section to the left called <abbr title="it comes from Micro(server)">"Micros"</abbr> with each of your apps.
Lift off -- successfully created a new Release!
Your Release is available globally on 5 Deta Edges
Anyone can install their own copy of your app.
Listed on Discovery for others to find!
```
</div>
You will see a tab with "Details", and also a tab "Visor", go to the tab "Visor".
This will allow anyone to find and install your app via <a href="https://deta.space/discovery?ref=fastapi" class="external-link" target="_blank">Deta Discovery</a>. Read more about <a href="https://deta.space/docs/en/basics/releases?ref=fastapi" class="external-link" target="_blank">releasing your app in the docs</a>.
In there you can inspect the recent requests sent to your app.
## Check runtime logs
You can also edit them and re-play them.
Deta Space also lets you inspect the logs of every app you build or install.
<img src="/img/deployment/deta/image02.png">
Add some logging functionality to your app by adding a `print` statement to your `main.py` file.
```py
from fastapi import FastAPI
app = FastAPI()
@app.get("/")
def read_root():
return {"Hello": "World"}
@app.get("/items/{item_id}")
def read_item(item_id: int):
print(item_id)
return {"item_id": item_id}
```
The code within the `read_item` function includes a print statement that will output the `item_id` that is included in the URL. Send a request to your _path operation_ `/items/{item_id}` from the docs UI (which will have a URL like `https://fastapi-deta-gj7ka8.deta.app/docs`), using an ID like `5` as an example.
Now go to your <a href="https://deta.space?ref=fastapi" class="external-link" target="_blank">Space's Canvas</a>. Click on the context menu (`...`) of your live app instance, and then click on **View Logs**. Here you can view your app's logs, sorted by time.
<img src="/img/deployment/deta/image06.png">
## Learn more
At some point, you will probably want to store some data for your app in a way that persists through time. For that you can use <a href="https://docs.deta.sh/docs/base/py_tutorial?ref=fastapi" class="external-link" target="_blank">Deta Base</a>, it also has a generous **free tier**.
At some point, you will probably want to store some data for your app in a way that persists through time. For that you can use <a href="https://deta.space/docs/en/basics/data#deta-base?ref=fastapi" class="external-link" target="_blank">Deta Base</a> and <a href="https://deta.space/docs/en/basics/data#deta-drive?ref=fastapi" class="external-link" target="_blank">Deta Drive</a>, both of which have a generous **free tier**.
You can also read more in the <a href="https://deta.space/docs/?ref=fastapi" class="external-link" target="_blank">Deta Space Documentation</a>.
!!! tip
If you have any Deta related questions, comments, or feedback, head to the <a href="https://go.deta.dev/discord" class="external-link" target="_blank">Deta Discord server</a>.
You can also read more in the <a href="https://docs.deta.sh?ref=fastapi" class="external-link" target="_blank">Deta Docs</a>.
## Deployment Concepts
Coming back to the concepts we discussed in [Deployments Concepts](./concepts.md){.internal-link target=_blank}, here's how each of them would be handled with Deta:
Coming back to the concepts we discussed in [Deployments Concepts](./concepts.md){.internal-link target=_blank}, here's how each of them would be handled with Deta Space:
* **HTTPS**: Handled by Deta, they will give you a subdomain and handle HTTPS automatically.
* **Running on startup**: Handled by Deta, as part of their service.
* **Restarts**: Handled by Deta, as part of their service.
* **Replication**: Handled by Deta, as part of their service.
* **Memory**: Limit predefined by Deta, you could contact them to increase it.
* **Previous steps before starting**: Not directly supported, you could make it work with their Cron system or additional scripts.
- **HTTPS**: Handled by Deta Space, they will give you a subdomain and handle HTTPS automatically.
- **Running on startup**: Handled by Deta Space, as part of their service.
- **Restarts**: Handled by Deta Space, as part of their service.
- **Replication**: Handled by Deta Space, as part of their service.
- **Authentication**: Handled by Deta Space, as part of their service.
- **Memory**: Limit predefined by Deta Space, you could contact them to increase it.
- **Previous steps before starting**: Can be configured using the <a href="https://deta.space/docs/en/reference/spacefile?ref=fastapi" class="external-link" target="_blank">`Spacefile`</a>.
!!! note
Deta is designed to make it easy (and free) to deploy simple applications quickly.
Deta Space is designed to make it easy and free to build cloud applications for yourself. Then you can optionally share them with anyone.
It can simplify several use cases, but at the same time, it doesn't support others, like using external databases (apart from Deta's own NoSQL database system), custom virtual machines, etc.
You can read more details in the <a href="https://docs.deta.sh/docs/micros/about/" class="external-link" target="_blank">Deta docs</a> to see if it's the right choice for you.
You can read more details in the <a href="https://deta.space/docs/en/basics/micros?ref=fastapi" class="external-link" target="_blank">Deta Space Documentation</a> to see if it's the right choice for you.

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 52 KiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 49 KiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 103 KiB

View File

@@ -3,6 +3,32 @@
## Latest Changes
## 0.95.2
* ⬆️ Upgrade Starlette version to `>=0.27.0` for a security release. PR [#9541](https://github.com/tiangolo/fastapi/pull/9541) by [@tiangolo](https://github.com/tiangolo). Details on [Starlette's security advisory](https://github.com/encode/starlette/security/advisories/GHSA-v5gw-mw7f-84px).
### Translations
* 🌐 Add Portuguese translation for `docs/pt/docs/advanced/events.md`. PR [#9326](https://github.com/tiangolo/fastapi/pull/9326) by [@oandersonmagalhaes](https://github.com/oandersonmagalhaes).
* 🌐 Add Russian translation for `docs/ru/docs/deployment/manually.md`. PR [#9417](https://github.com/tiangolo/fastapi/pull/9417) by [@Xewus](https://github.com/Xewus).
* 🌐 Add setup for translations to Lao. PR [#9396](https://github.com/tiangolo/fastapi/pull/9396) by [@TheBrown](https://github.com/TheBrown).
* 🌐 Add Russian translation for `docs/ru/docs/tutorial/testing.md`. PR [#9403](https://github.com/tiangolo/fastapi/pull/9403) by [@Xewus](https://github.com/Xewus).
* 🌐 Add Russian translation for `docs/ru/docs/deployment/https.md`. PR [#9428](https://github.com/tiangolo/fastapi/pull/9428) by [@Xewus](https://github.com/Xewus).
* ✏ Fix command to install requirements in Windows. PR [#9445](https://github.com/tiangolo/fastapi/pull/9445) by [@MariiaRomanuik](https://github.com/MariiaRomanuik).
* 🌐 Add French translation for `docs/fr/docs/advanced/response-directly.md`. PR [#9415](https://github.com/tiangolo/fastapi/pull/9415) by [@axel584](https://github.com/axel584).
* 🌐 Initiate Czech translation setup. PR [#9288](https://github.com/tiangolo/fastapi/pull/9288) by [@3p1463k](https://github.com/3p1463k).
* ✏ Fix typo in Portuguese docs for `docs/pt/docs/index.md`. PR [#9337](https://github.com/tiangolo/fastapi/pull/9337) by [@lucasbalieiro](https://github.com/lucasbalieiro).
* 🌐 Add Russian translation for `docs/ru/docs/tutorial/response-status-code.md`. PR [#9370](https://github.com/tiangolo/fastapi/pull/9370) by [@nadia3373](https://github.com/nadia3373).
### Internal
* 🐛 Fix `flask.escape` warning for internal tests. PR [#9468](https://github.com/tiangolo/fastapi/pull/9468) by [@samuelcolvin](https://github.com/samuelcolvin).
* ✅ Refactor 2 tests, for consistency and simplification. PR [#9504](https://github.com/tiangolo/fastapi/pull/9504) by [@tiangolo](https://github.com/tiangolo).
* ✅ Refactor OpenAPI tests, prepare for Pydantic v2. PR [#9503](https://github.com/tiangolo/fastapi/pull/9503) by [@tiangolo](https://github.com/tiangolo).
* ⬆ Bump dawidd6/action-download-artifact from 2.26.0 to 2.27.0. PR [#9394](https://github.com/tiangolo/fastapi/pull/9394) by [@dependabot[bot]](https://github.com/apps/dependabot).
* 💚 Disable setup-python pip cache in CI. PR [#9438](https://github.com/tiangolo/fastapi/pull/9438) by [@tiangolo](https://github.com/tiangolo).
* ⬆ Bump pypa/gh-action-pypi-publish from 1.6.4 to 1.8.5. PR [#9346](https://github.com/tiangolo/fastapi/pull/9346) by [@dependabot[bot]](https://github.com/apps/dependabot).
## 0.95.1
### Fixes

View File

@@ -40,6 +40,7 @@ nav:
- Languages:
- en: /
- az: /az/
- cs: /cs/
- de: /de/
- em: /em/
- es: /es/
@@ -51,6 +52,7 @@ nav:
- it: /it/
- ja: /ja/
- ko: /ko/
- lo: /lo/
- nl: /nl/
- pl: /pl/
- pt: /pt/
@@ -211,6 +213,8 @@ extra:
name: en - English
- link: /az/
name: az
- link: /cs/
name: cs
- link: /de/
name: de
- link: /em/
@@ -233,6 +237,8 @@ extra:
name: ja - 日本語
- link: /ko/
name: ko - 한국어
- link: /lo/
name: lo - ພາສາລາວ
- link: /nl/
name: nl
- link: /pl/

View File

@@ -40,6 +40,7 @@ nav:
- Languages:
- en: /
- az: /az/
- cs: /cs/
- de: /de/
- em: /em/
- es: /es/
@@ -51,6 +52,7 @@ nav:
- it: /it/
- ja: /ja/
- ko: /ko/
- lo: /lo/
- nl: /nl/
- pl: /pl/
- pt: /pt/
@@ -114,6 +116,8 @@ extra:
name: en - English
- link: /az/
name: az
- link: /cs/
name: cs
- link: /de/
name: de
- link: /em/
@@ -136,6 +140,8 @@ extra:
name: ja - 日本語
- link: /ko/
name: ko - 한국어
- link: /lo/
name: lo - ພາສາລາວ
- link: /nl/
name: nl
- link: /pl/

View File

@@ -40,6 +40,7 @@ nav:
- Languages:
- en: /
- az: /az/
- cs: /cs/
- de: /de/
- em: /em/
- es: /es/
@@ -51,6 +52,7 @@ nav:
- it: /it/
- ja: /ja/
- ko: /ko/
- lo: /lo/
- nl: /nl/
- pl: /pl/
- pt: /pt/
@@ -104,6 +106,8 @@ extra:
name: en - English
- link: /az/
name: az
- link: /cs/
name: cs
- link: /de/
name: de
- link: /em/
@@ -126,6 +130,8 @@ extra:
name: ja - 日本語
- link: /ko/
name: ko - 한국어
- link: /lo/
name: lo - ພາສາລາວ
- link: /nl/
name: nl
- link: /pl/

View File

@@ -0,0 +1,63 @@
# Renvoyer directement une réponse
Lorsque vous créez une *opération de chemins* **FastAPI**, vous pouvez normalement retourner n'importe quelle donnée : un `dict`, une `list`, un modèle Pydantic, un modèle de base de données, etc.
Par défaut, **FastAPI** convertirait automatiquement cette valeur de retour en JSON en utilisant le `jsonable_encoder` expliqué dans [JSON Compatible Encoder](../tutorial/encoder.md){.internal-link target=_blank}.
Ensuite, en arrière-plan, il mettra ces données JSON-compatible (par exemple un `dict`) à l'intérieur d'un `JSONResponse` qui sera utilisé pour envoyer la réponse au client.
Mais vous pouvez retourner une `JSONResponse` directement à partir de vos *opérations de chemin*.
Cela peut être utile, par exemple, pour retourner des en-têtes personnalisés ou des cookies.
## Renvoyer une `Response`
En fait, vous pouvez retourner n'importe quelle `Response` ou n'importe quelle sous-classe de celle-ci.
!!! Note
`JSONResponse` est elle-même une sous-classe de `Response`.
Et quand vous retournez une `Response`, **FastAPI** la transmet directement.
Elle ne fera aucune conversion de données avec les modèles Pydantic, elle ne convertira pas le contenu en un type quelconque.
Cela vous donne beaucoup de flexibilité. Vous pouvez retourner n'importe quel type de données, surcharger n'importe quelle déclaration ou validation de données.
## Utiliser le `jsonable_encoder` dans une `Response`
Parce que **FastAPI** n'apporte aucune modification à une `Response` que vous retournez, vous devez vous assurer que son contenu est prêt à être utilisé (sérialisable).
Par exemple, vous ne pouvez pas mettre un modèle Pydantic dans une `JSONResponse` sans d'abord le convertir en un `dict` avec tous les types de données (comme `datetime`, `UUID`, etc.) convertis en types compatibles avec JSON.
Pour ces cas, vous pouvez spécifier un appel à `jsonable_encoder` pour convertir vos données avant de les passer à une réponse :
```Python hl_lines="6-7 21-22"
{!../../../docs_src/response_directly/tutorial001.py!}
```
!!! note "Détails techniques"
Vous pouvez aussi utiliser `from starlette.responses import JSONResponse`.
**FastAPI** fournit le même objet `starlette.responses` que `fastapi.responses` juste par commodité pour le développeur. Mais la plupart des réponses disponibles proviennent directement de Starlette.
## Renvoyer une `Response` personnalisée
L'exemple ci-dessus montre toutes les parties dont vous avez besoin, mais il n'est pas encore très utile, car vous auriez pu retourner l'`item` directement, et **FastAPI** l'aurait mis dans une `JSONResponse` pour vous, en le convertissant en `dict`, etc. Tout cela par défaut.
Maintenant, voyons comment vous pourriez utiliser cela pour retourner une réponse personnalisée.
Disons que vous voulez retourner une réponse <a href="https://en.wikipedia.org/wiki/XML" class="external-link" target="_blank">XML</a>.
Vous pouvez mettre votre contenu XML dans une chaîne de caractères, la placer dans une `Response`, et la retourner :
```Python hl_lines="1 18"
{!../../../docs_src/response_directly/tutorial002.py!}
```
## Notes
Lorsque vous renvoyez une `Response` directement, ses données ne sont pas validées, converties (sérialisées), ni documentées automatiquement.
Mais vous pouvez toujours les documenter comme décrit dans [Additional Responses in OpenAPI](additional-responses.md){.internal-link target=_blank}.
Vous pouvez voir dans les sections suivantes comment utiliser/déclarer ces `Response`s personnalisées tout en conservant la conversion automatique des données, la documentation, etc.

View File

@@ -40,6 +40,7 @@ nav:
- Languages:
- en: /
- az: /az/
- cs: /cs/
- de: /de/
- em: /em/
- es: /es/
@@ -51,6 +52,7 @@ nav:
- it: /it/
- ja: /ja/
- ko: /ko/
- lo: /lo/
- nl: /nl/
- pl: /pl/
- pt: /pt/
@@ -75,6 +77,7 @@ nav:
- advanced/index.md
- advanced/path-operation-advanced-configuration.md
- advanced/additional-status-codes.md
- advanced/response-directly.md
- advanced/additional-responses.md
- async.md
- Déploiement:
@@ -132,6 +135,8 @@ extra:
name: en - English
- link: /az/
name: az
- link: /cs/
name: cs
- link: /de/
name: de
- link: /em/
@@ -154,6 +159,8 @@ extra:
name: ja - 日本語
- link: /ko/
name: ko - 한국어
- link: /lo/
name: lo - ພາສາລາວ
- link: /nl/
name: nl
- link: /pl/

View File

@@ -40,6 +40,7 @@ nav:
- Languages:
- en: /
- az: /az/
- cs: /cs/
- de: /de/
- em: /em/
- es: /es/
@@ -51,6 +52,7 @@ nav:
- it: /it/
- ja: /ja/
- ko: /ko/
- lo: /lo/
- nl: /nl/
- pl: /pl/
- pt: /pt/
@@ -104,6 +106,8 @@ extra:
name: en - English
- link: /az/
name: az
- link: /cs/
name: cs
- link: /de/
name: de
- link: /em/
@@ -126,6 +130,8 @@ extra:
name: ja - 日本語
- link: /ko/
name: ko - 한국어
- link: /lo/
name: lo - ພາສາລາວ
- link: /nl/
name: nl
- link: /pl/

View File

@@ -40,6 +40,7 @@ nav:
- Languages:
- en: /
- az: /az/
- cs: /cs/
- de: /de/
- em: /em/
- es: /es/
@@ -51,6 +52,7 @@ nav:
- it: /it/
- ja: /ja/
- ko: /ko/
- lo: /lo/
- nl: /nl/
- pl: /pl/
- pt: /pt/
@@ -104,6 +106,8 @@ extra:
name: en - English
- link: /az/
name: az
- link: /cs/
name: cs
- link: /de/
name: de
- link: /em/
@@ -126,6 +130,8 @@ extra:
name: ja - 日本語
- link: /ko/
name: ko - 한국어
- link: /lo/
name: lo - ພາສາລາວ
- link: /nl/
name: nl
- link: /pl/

View File

@@ -40,6 +40,7 @@ nav:
- Languages:
- en: /
- az: /az/
- cs: /cs/
- de: /de/
- em: /em/
- es: /es/
@@ -51,6 +52,7 @@ nav:
- it: /it/
- ja: /ja/
- ko: /ko/
- lo: /lo/
- nl: /nl/
- pl: /pl/
- pt: /pt/
@@ -104,6 +106,8 @@ extra:
name: en - English
- link: /az/
name: az
- link: /cs/
name: cs
- link: /de/
name: de
- link: /em/
@@ -126,6 +130,8 @@ extra:
name: ja - 日本語
- link: /ko/
name: ko - 한국어
- link: /lo/
name: lo - ພາສາລາວ
- link: /nl/
name: nl
- link: /pl/

View File

@@ -40,6 +40,7 @@ nav:
- Languages:
- en: /
- az: /az/
- cs: /cs/
- de: /de/
- em: /em/
- es: /es/
@@ -51,6 +52,7 @@ nav:
- it: /it/
- ja: /ja/
- ko: /ko/
- lo: /lo/
- nl: /nl/
- pl: /pl/
- pt: /pt/
@@ -104,6 +106,8 @@ extra:
name: en - English
- link: /az/
name: az
- link: /cs/
name: cs
- link: /de/
name: de
- link: /em/
@@ -126,6 +130,8 @@ extra:
name: ja - 日本語
- link: /ko/
name: ko - 한국어
- link: /lo/
name: lo - ພາສາລາວ
- link: /nl/
name: nl
- link: /pl/

View File

@@ -40,6 +40,7 @@ nav:
- Languages:
- en: /
- az: /az/
- cs: /cs/
- de: /de/
- em: /em/
- es: /es/
@@ -51,6 +52,7 @@ nav:
- it: /it/
- ja: /ja/
- ko: /ko/
- lo: /lo/
- nl: /nl/
- pl: /pl/
- pt: /pt/
@@ -148,6 +150,8 @@ extra:
name: en - English
- link: /az/
name: az
- link: /cs/
name: cs
- link: /de/
name: de
- link: /em/
@@ -170,6 +174,8 @@ extra:
name: ja - 日本語
- link: /ko/
name: ko - 한국어
- link: /lo/
name: lo - ພາສາລາວ
- link: /nl/
name: nl
- link: /pl/

View File

@@ -40,6 +40,7 @@ nav:
- Languages:
- en: /
- az: /az/
- cs: /cs/
- de: /de/
- em: /em/
- es: /es/
@@ -51,6 +52,7 @@ nav:
- it: /it/
- ja: /ja/
- ko: /ko/
- lo: /lo/
- nl: /nl/
- pl: /pl/
- pt: /pt/
@@ -118,6 +120,8 @@ extra:
name: en - English
- link: /az/
name: az
- link: /cs/
name: cs
- link: /de/
name: de
- link: /em/
@@ -140,6 +144,8 @@ extra:
name: ja - 日本語
- link: /ko/
name: ko - 한국어
- link: /lo/
name: lo - ພາສາລາວ
- link: /nl/
name: nl
- link: /pl/

469
docs/lo/docs/index.md Normal file
View File

@@ -0,0 +1,469 @@
<p align="center">
<a href="https://fastapi.tiangolo.com"><img src="https://fastapi.tiangolo.com/img/logo-margin/logo-teal.png" alt="FastAPI"></a>
</p>
<p align="center">
<em>FastAPI framework, high performance, easy to learn, fast to code, ready for production</em>
</p>
<p align="center">
<a href="https://github.com/tiangolo/fastapi/actions?query=workflow%3ATest+event%3Apush+branch%3Amaster" target="_blank">
<img src="https://github.com/tiangolo/fastapi/workflows/Test/badge.svg?event=push&branch=master" alt="Test">
</a>
<a href="https://coverage-badge.samuelcolvin.workers.dev/redirect/tiangolo/fastapi" target="_blank">
<img src="https://coverage-badge.samuelcolvin.workers.dev/tiangolo/fastapi.svg" alt="Coverage">
</a>
<a href="https://pypi.org/project/fastapi" target="_blank">
<img src="https://img.shields.io/pypi/v/fastapi?color=%2334D058&label=pypi%20package" alt="Package version">
</a>
<a href="https://pypi.org/project/fastapi" target="_blank">
<img src="https://img.shields.io/pypi/pyversions/fastapi.svg?color=%2334D058" alt="Supported Python versions">
</a>
</p>
---
**Documentation**: <a href="https://fastapi.tiangolo.com" target="_blank">https://fastapi.tiangolo.com</a>
**Source Code**: <a href="https://github.com/tiangolo/fastapi" target="_blank">https://github.com/tiangolo/fastapi</a>
---
FastAPI is a modern, fast (high-performance), web framework for building APIs with Python 3.7+ based on standard Python type hints.
The key features are:
* **Fast**: Very high performance, on par with **NodeJS** and **Go** (thanks to Starlette and Pydantic). [One of the fastest Python frameworks available](#performance).
* **Fast to code**: Increase the speed to develop features by about 200% to 300%. *
* **Fewer bugs**: Reduce about 40% of human (developer) induced errors. *
* **Intuitive**: Great editor support. <abbr title="also known as auto-complete, autocompletion, IntelliSense">Completion</abbr> everywhere. Less time debugging.
* **Easy**: Designed to be easy to use and learn. Less time reading docs.
* **Short**: Minimize code duplication. Multiple features from each parameter declaration. Fewer bugs.
* **Robust**: Get production-ready code. With automatic interactive documentation.
* **Standards-based**: Based on (and fully compatible with) the open standards for APIs: <a href="https://github.com/OAI/OpenAPI-Specification" class="external-link" target="_blank">OpenAPI</a> (previously known as Swagger) and <a href="https://json-schema.org/" class="external-link" target="_blank">JSON Schema</a>.
<small>* estimation based on tests on an internal development team, building production applications.</small>
## Sponsors
<!-- sponsors -->
{% if sponsors %}
{% for sponsor in sponsors.gold -%}
<a href="{{ sponsor.url }}" target="_blank" title="{{ sponsor.title }}"><img src="{{ sponsor.img }}" style="border-radius:15px"></a>
{% endfor -%}
{%- for sponsor in sponsors.silver -%}
<a href="{{ sponsor.url }}" target="_blank" title="{{ sponsor.title }}"><img src="{{ sponsor.img }}" style="border-radius:15px"></a>
{% endfor %}
{% endif %}
<!-- /sponsors -->
<a href="https://fastapi.tiangolo.com/fastapi-people/#sponsors" class="external-link" target="_blank">Other sponsors</a>
## Opinions
"_[...] I'm using **FastAPI** a ton these days. [...] I'm actually planning to use it for all of my team's **ML services at Microsoft**. Some of them are getting integrated into the core **Windows** product and some **Office** products._"
<div style="text-align: right; margin-right: 10%;">Kabir Khan - <strong>Microsoft</strong> <a href="https://github.com/tiangolo/fastapi/pull/26" target="_blank"><small>(ref)</small></a></div>
---
"_We adopted the **FastAPI** library to spawn a **REST** server that can be queried to obtain **predictions**. [for Ludwig]_"
<div style="text-align: right; margin-right: 10%;">Piero Molino, Yaroslav Dudin, and Sai Sumanth Miryala - <strong>Uber</strong> <a href="https://eng.uber.com/ludwig-v0-2/" target="_blank"><small>(ref)</small></a></div>
---
"_**Netflix** is pleased to announce the open-source release of our **crisis management** orchestration framework: **Dispatch**! [built with **FastAPI**]_"
<div style="text-align: right; margin-right: 10%;">Kevin Glisson, Marc Vilanova, Forest Monsen - <strong>Netflix</strong> <a href="https://netflixtechblog.com/introducing-dispatch-da4b8a2a8072" target="_blank"><small>(ref)</small></a></div>
---
"_Im over the moon excited about **FastAPI**. Its so fun!_"
<div style="text-align: right; margin-right: 10%;">Brian Okken - <strong><a href="https://pythonbytes.fm/episodes/show/123/time-to-right-the-py-wrongs?time_in_sec=855" target="_blank">Python Bytes</a> podcast host</strong> <a href="https://twitter.com/brianokken/status/1112220079972728832" target="_blank"><small>(ref)</small></a></div>
---
"_Honestly, what you've built looks super solid and polished. In many ways, it's what I wanted **Hug** to be - it's really inspiring to see someone build that._"
<div style="text-align: right; margin-right: 10%;">Timothy Crosley - <strong><a href="https://www.hug.rest/" target="_blank">Hug</a> creator</strong> <a href="https://news.ycombinator.com/item?id=19455465" target="_blank"><small>(ref)</small></a></div>
---
"_If you're looking to learn one **modern framework** for building REST APIs, check out **FastAPI** [...] It's fast, easy to use and easy to learn [...]_"
"_We've switched over to **FastAPI** for our **APIs** [...] I think you'll like it [...]_"
<div style="text-align: right; margin-right: 10%;">Ines Montani - Matthew Honnibal - <strong><a href="https://explosion.ai" target="_blank">Explosion AI</a> founders - <a href="https://spacy.io" target="_blank">spaCy</a> creators</strong> <a href="https://twitter.com/_inesmontani/status/1144173225322143744" target="_blank"><small>(ref)</small></a> - <a href="https://twitter.com/honnibal/status/1144031421859655680" target="_blank"><small>(ref)</small></a></div>
---
"_If anyone is looking to build a production Python API, I would highly recommend **FastAPI**. It is **beautifully designed**, **simple to use** and **highly scalable**, it has become a **key component** in our API first development strategy and is driving many automations and services such as our Virtual TAC Engineer._"
<div style="text-align: right; margin-right: 10%;">Deon Pillsbury - <strong>Cisco</strong> <a href="https://www.linkedin.com/posts/deonpillsbury_cisco-cx-python-activity-6963242628536487936-trAp/" target="_blank"><small>(ref)</small></a></div>
---
## **Typer**, the FastAPI of CLIs
<a href="https://typer.tiangolo.com" target="_blank"><img src="https://typer.tiangolo.com/img/logo-margin/logo-margin-vector.svg" style="width: 20%;"></a>
If you are building a <abbr title="Command Line Interface">CLI</abbr> app to be used in the terminal instead of a web API, check out <a href="https://typer.tiangolo.com/" class="external-link" target="_blank">**Typer**</a>.
**Typer** is FastAPI's little sibling. And it's intended to be the **FastAPI of CLIs**. ⌨️ 🚀
## Requirements
Python 3.7+
FastAPI stands on the shoulders of giants:
* <a href="https://www.starlette.io/" class="external-link" target="_blank">Starlette</a> for the web parts.
* <a href="https://pydantic-docs.helpmanual.io/" class="external-link" target="_blank">Pydantic</a> for the data parts.
## Installation
<div class="termy">
```console
$ pip install fastapi
---> 100%
```
</div>
You will also need an ASGI server, for production such as <a href="https://www.uvicorn.org" class="external-link" target="_blank">Uvicorn</a> or <a href="https://github.com/pgjones/hypercorn" class="external-link" target="_blank">Hypercorn</a>.
<div class="termy">
```console
$ pip install "uvicorn[standard]"
---> 100%
```
</div>
## Example
### Create it
* Create a file `main.py` with:
```Python
from typing import Union
from fastapi import FastAPI
app = FastAPI()
@app.get("/")
def read_root():
return {"Hello": "World"}
@app.get("/items/{item_id}")
def read_item(item_id: int, q: Union[str, None] = None):
return {"item_id": item_id, "q": q}
```
<details markdown="1">
<summary>Or use <code>async def</code>...</summary>
If your code uses `async` / `await`, use `async def`:
```Python hl_lines="9 14"
from typing import Union
from fastapi import FastAPI
app = FastAPI()
@app.get("/")
async def read_root():
return {"Hello": "World"}
@app.get("/items/{item_id}")
async def read_item(item_id: int, q: Union[str, None] = None):
return {"item_id": item_id, "q": q}
```
**Note**:
If you don't know, check the _"In a hurry?"_ section about <a href="https://fastapi.tiangolo.com/async/#in-a-hurry" target="_blank">`async` and `await` in the docs</a>.
</details>
### Run it
Run the server with:
<div class="termy">
```console
$ uvicorn main:app --reload
INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
INFO: Started reloader process [28720]
INFO: Started server process [28722]
INFO: Waiting for application startup.
INFO: Application startup complete.
```
</div>
<details markdown="1">
<summary>About the command <code>uvicorn main:app --reload</code>...</summary>
The command `uvicorn main:app` refers to:
* `main`: the file `main.py` (the Python "module").
* `app`: the object created inside of `main.py` with the line `app = FastAPI()`.
* `--reload`: make the server restart after code changes. Only do this for development.
</details>
### Check it
Open your browser at <a href="http://127.0.0.1:8000/items/5?q=somequery" class="external-link" target="_blank">http://127.0.0.1:8000/items/5?q=somequery</a>.
You will see the JSON response as:
```JSON
{"item_id": 5, "q": "somequery"}
```
You already created an API that:
* Receives HTTP requests in the _paths_ `/` and `/items/{item_id}`.
* Both _paths_ take `GET` <em>operations</em> (also known as HTTP _methods_).
* The _path_ `/items/{item_id}` has a _path parameter_ `item_id` that should be an `int`.
* The _path_ `/items/{item_id}` has an optional `str` _query parameter_ `q`.
### Interactive API docs
Now go to <a href="http://127.0.0.1:8000/docs" class="external-link" target="_blank">http://127.0.0.1:8000/docs</a>.
You will see the automatic interactive API documentation (provided by <a href="https://github.com/swagger-api/swagger-ui" class="external-link" target="_blank">Swagger UI</a>):
![Swagger UI](https://fastapi.tiangolo.com/img/index/index-01-swagger-ui-simple.png)
### Alternative API docs
And now, go to <a href="http://127.0.0.1:8000/redoc" class="external-link" target="_blank">http://127.0.0.1:8000/redoc</a>.
You will see the alternative automatic documentation (provided by <a href="https://github.com/Rebilly/ReDoc" class="external-link" target="_blank">ReDoc</a>):
![ReDoc](https://fastapi.tiangolo.com/img/index/index-02-redoc-simple.png)
## Example upgrade
Now modify the file `main.py` to receive a body from a `PUT` request.
Declare the body using standard Python types, thanks to Pydantic.
```Python hl_lines="4 9-12 25-27"
from typing import Union
from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI()
class Item(BaseModel):
name: str
price: float
is_offer: Union[bool, None] = None
@app.get("/")
def read_root():
return {"Hello": "World"}
@app.get("/items/{item_id}")
def read_item(item_id: int, q: Union[str, None] = None):
return {"item_id": item_id, "q": q}
@app.put("/items/{item_id}")
def update_item(item_id: int, item: Item):
return {"item_name": item.name, "item_id": item_id}
```
The server should reload automatically (because you added `--reload` to the `uvicorn` command above).
### Interactive API docs upgrade
Now go to <a href="http://127.0.0.1:8000/docs" class="external-link" target="_blank">http://127.0.0.1:8000/docs</a>.
* The interactive API documentation will be automatically updated, including the new body:
![Swagger UI](https://fastapi.tiangolo.com/img/index/index-03-swagger-02.png)
* Click on the button "Try it out", it allows you to fill the parameters and directly interact with the API:
![Swagger UI interaction](https://fastapi.tiangolo.com/img/index/index-04-swagger-03.png)
* Then click on the "Execute" button, the user interface will communicate with your API, send the parameters, get the results and show them on the screen:
![Swagger UI interaction](https://fastapi.tiangolo.com/img/index/index-05-swagger-04.png)
### Alternative API docs upgrade
And now, go to <a href="http://127.0.0.1:8000/redoc" class="external-link" target="_blank">http://127.0.0.1:8000/redoc</a>.
* The alternative documentation will also reflect the new query parameter and body:
![ReDoc](https://fastapi.tiangolo.com/img/index/index-06-redoc-02.png)
### Recap
In summary, you declare **once** the types of parameters, body, etc. as function parameters.
You do that with standard modern Python types.
You don't have to learn a new syntax, the methods or classes of a specific library, etc.
Just standard **Python 3.7+**.
For example, for an `int`:
```Python
item_id: int
```
or for a more complex `Item` model:
```Python
item: Item
```
...and with that single declaration you get:
* Editor support, including:
* Completion.
* Type checks.
* Validation of data:
* Automatic and clear errors when the data is invalid.
* Validation even for deeply nested JSON objects.
* <abbr title="also known as: serialization, parsing, marshalling">Conversion</abbr> of input data: coming from the network to Python data and types. Reading from:
* JSON.
* Path parameters.
* Query parameters.
* Cookies.
* Headers.
* Forms.
* Files.
* <abbr title="also known as: serialization, parsing, marshalling">Conversion</abbr> of output data: converting from Python data and types to network data (as JSON):
* Convert Python types (`str`, `int`, `float`, `bool`, `list`, etc).
* `datetime` objects.
* `UUID` objects.
* Database models.
* ...and many more.
* Automatic interactive API documentation, including 2 alternative user interfaces:
* Swagger UI.
* ReDoc.
---
Coming back to the previous code example, **FastAPI** will:
* Validate that there is an `item_id` in the path for `GET` and `PUT` requests.
* Validate that the `item_id` is of type `int` for `GET` and `PUT` requests.
* If it is not, the client will see a useful, clear error.
* Check if there is an optional query parameter named `q` (as in `http://127.0.0.1:8000/items/foo?q=somequery`) for `GET` requests.
* As the `q` parameter is declared with `= None`, it is optional.
* Without the `None` it would be required (as is the body in the case with `PUT`).
* For `PUT` requests to `/items/{item_id}`, Read the body as JSON:
* Check that it has a required attribute `name` that should be a `str`.
* Check that it has a required attribute `price` that has to be a `float`.
* Check that it has an optional attribute `is_offer`, that should be a `bool`, if present.
* All this would also work for deeply nested JSON objects.
* Convert from and to JSON automatically.
* Document everything with OpenAPI, that can be used by:
* Interactive documentation systems.
* Automatic client code generation systems, for many languages.
* Provide 2 interactive documentation web interfaces directly.
---
We just scratched the surface, but you already get the idea of how it all works.
Try changing the line with:
```Python
return {"item_name": item.name, "item_id": item_id}
```
...from:
```Python
... "item_name": item.name ...
```
...to:
```Python
... "item_price": item.price ...
```
...and see how your editor will auto-complete the attributes and know their types:
![editor support](https://fastapi.tiangolo.com/img/vscode-completion.png)
For a more complete example including more features, see the <a href="https://fastapi.tiangolo.com/tutorial/">Tutorial - User Guide</a>.
**Spoiler alert**: the tutorial - user guide includes:
* Declaration of **parameters** from other different places as: **headers**, **cookies**, **form fields** and **files**.
* How to set **validation constraints** as `maximum_length` or `regex`.
* A very powerful and easy to use **<abbr title="also known as components, resources, providers, services, injectables">Dependency Injection</abbr>** system.
* Security and authentication, including support for **OAuth2** with **JWT tokens** and **HTTP Basic** auth.
* More advanced (but equally easy) techniques for declaring **deeply nested JSON models** (thanks to Pydantic).
* **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:
* **WebSockets**
* extremely easy tests based on HTTPX and `pytest`
* **CORS**
* **Cookie Sessions**
* ...and more.
## Performance
Independent TechEmpower benchmarks show **FastAPI** applications running under Uvicorn as <a href="https://www.techempower.com/benchmarks/#section=test&runid=7464e520-0dc2-473d-bd34-dbdfd7e85911&hw=ph&test=query&l=zijzen-7" class="external-link" target="_blank">one of the fastest Python frameworks available</a>, only below Starlette and Uvicorn themselves (used internally by FastAPI). (*)
To understand more about it, see the section <a href="https://fastapi.tiangolo.com/benchmarks/" class="internal-link" target="_blank">Benchmarks</a>.
## Optional Dependencies
Used by Pydantic:
* <a href="https://github.com/esnme/ultrajson" target="_blank"><code>ujson</code></a> - for faster JSON <abbr title="converting the string that comes from an HTTP request into Python data">"parsing"</abbr>.
* <a href="https://github.com/JoshData/python-email-validator" target="_blank"><code>email_validator</code></a> - for email validation.
Used by Starlette:
* <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://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://pyyaml.org/wiki/PyYAMLDocumentation" target="_blank"><code>pyyaml</code></a> - Required for Starlette's `SchemaGenerator` support (you probably don't need it with FastAPI).
* <a href="https://github.com/esnme/ultrajson" target="_blank"><code>ujson</code></a> - Required if you want to use `UJSONResponse`.
Used by FastAPI / Starlette:
* <a href="https://www.uvicorn.org" target="_blank"><code>uvicorn</code></a> - for the server that loads and serves your application.
* <a href="https://github.com/ijl/orjson" target="_blank"><code>orjson</code></a> - Required if you want to use `ORJSONResponse`.
You can install all of these with `pip install "fastapi[all]"`.
## License
This project is licensed under the terms of the MIT license.

157
docs/lo/mkdocs.yml Normal file
View File

@@ -0,0 +1,157 @@
site_name: FastAPI
site_description: FastAPI framework, high performance, easy to learn, fast to code, ready for production
site_url: https://fastapi.tiangolo.com/lo/
theme:
name: material
custom_dir: overrides
palette:
- media: '(prefers-color-scheme: light)'
scheme: default
primary: teal
accent: amber
toggle:
icon: material/lightbulb
name: Switch to light mode
- media: '(prefers-color-scheme: dark)'
scheme: slate
primary: teal
accent: amber
toggle:
icon: material/lightbulb-outline
name: Switch to dark mode
features:
- search.suggest
- search.highlight
- content.tabs.link
icon:
repo: fontawesome/brands/github-alt
logo: https://fastapi.tiangolo.com/img/icon-white.svg
favicon: https://fastapi.tiangolo.com/img/favicon.png
language: en
repo_name: tiangolo/fastapi
repo_url: https://github.com/tiangolo/fastapi
edit_uri: ''
plugins:
- search
- markdownextradata:
data: data
nav:
- FastAPI: index.md
- Languages:
- en: /
- az: /az/
- de: /de/
- em: /em/
- es: /es/
- fa: /fa/
- fr: /fr/
- he: /he/
- hy: /hy/
- id: /id/
- it: /it/
- ja: /ja/
- ko: /ko/
- lo: /lo/
- nl: /nl/
- pl: /pl/
- pt: /pt/
- ru: /ru/
- sq: /sq/
- sv: /sv/
- ta: /ta/
- tr: /tr/
- uk: /uk/
- zh: /zh/
markdown_extensions:
- toc:
permalink: true
- markdown.extensions.codehilite:
guess_lang: false
- mdx_include:
base_path: docs
- admonition
- codehilite
- extra
- pymdownx.superfences:
custom_fences:
- name: mermaid
class: mermaid
format: !!python/name:pymdownx.superfences.fence_code_format ''
- pymdownx.tabbed:
alternate_style: true
- attr_list
- md_in_html
extra:
analytics:
provider: google
property: G-YNEVN69SC3
social:
- icon: fontawesome/brands/github-alt
link: https://github.com/tiangolo/fastapi
- icon: fontawesome/brands/discord
link: https://discord.gg/VQjSZaeJmf
- icon: fontawesome/brands/twitter
link: https://twitter.com/fastapi
- icon: fontawesome/brands/linkedin
link: https://www.linkedin.com/in/tiangolo
- icon: fontawesome/brands/dev
link: https://dev.to/tiangolo
- icon: fontawesome/brands/medium
link: https://medium.com/@tiangolo
- icon: fontawesome/solid/globe
link: https://tiangolo.com
alternate:
- link: /
name: en - English
- link: /az/
name: az
- link: /de/
name: de
- link: /em/
name: 😉
- link: /es/
name: es - español
- link: /fa/
name: fa
- link: /fr/
name: fr - français
- link: /he/
name: he
- link: /hy/
name: hy
- link: /id/
name: id
- link: /it/
name: it - italiano
- link: /ja/
name: ja - 日本語
- link: /ko/
name: ko - 한국어
- link: /lo/
name: lo - ພາສາລາວ
- link: /nl/
name: nl
- link: /pl/
name: pl
- link: /pt/
name: pt - português
- link: /ru/
name: ru - русский язык
- link: /sq/
name: sq - shqip
- link: /sv/
name: sv - svenska
- link: /ta/
name: ta - தமிழ்
- link: /tr/
name: tr - Türkçe
- link: /uk/
name: uk - українська мова
- link: /zh/
name: zh - 汉语
extra_css:
- https://fastapi.tiangolo.com/css/termynal.css
- https://fastapi.tiangolo.com/css/custom.css
extra_javascript:
- https://fastapi.tiangolo.com/js/termynal.js
- https://fastapi.tiangolo.com/js/custom.js

0
docs/lo/overrides/.gitignore vendored Normal file
View File

View File

@@ -40,6 +40,7 @@ nav:
- Languages:
- en: /
- az: /az/
- cs: /cs/
- de: /de/
- em: /em/
- es: /es/
@@ -51,6 +52,7 @@ nav:
- it: /it/
- ja: /ja/
- ko: /ko/
- lo: /lo/
- nl: /nl/
- pl: /pl/
- pt: /pt/
@@ -104,6 +106,8 @@ extra:
name: en - English
- link: /az/
name: az
- link: /cs/
name: cs
- link: /de/
name: de
- link: /em/
@@ -126,6 +130,8 @@ extra:
name: ja - 日本語
- link: /ko/
name: ko - 한국어
- link: /lo/
name: lo - ພາສາລາວ
- link: /nl/
name: nl
- link: /pl/

View File

@@ -40,6 +40,7 @@ nav:
- Languages:
- en: /
- az: /az/
- cs: /cs/
- de: /de/
- em: /em/
- es: /es/
@@ -51,6 +52,7 @@ nav:
- it: /it/
- ja: /ja/
- ko: /ko/
- lo: /lo/
- nl: /nl/
- pl: /pl/
- pt: /pt/
@@ -107,6 +109,8 @@ extra:
name: en - English
- link: /az/
name: az
- link: /cs/
name: cs
- link: /de/
name: de
- link: /em/
@@ -129,6 +133,8 @@ extra:
name: ja - 日本語
- link: /ko/
name: ko - 한국어
- link: /lo/
name: lo - ພາສາລາວ
- link: /nl/
name: nl
- link: /pl/

View File

@@ -0,0 +1,163 @@
# Eventos de vida útil
Você pode definir a lógica (código) que poderia ser executada antes da aplicação **inicializar**. Isso significa que esse código será executado **uma vez**, **antes** da aplicação **começar a receber requisições**.
Do mesmo modo, você pode definir a lógica (código) que será executada quando a aplicação estiver sendo **encerrada**. Nesse caso, este código será executado **uma vez**, **depois** de ter possivelmente tratado **várias requisições**.
Por conta desse código ser executado antes da aplicação **começar** a receber requisições, e logo após **terminar** de lidar com as requisições, ele cobre toda a **vida útil** (_lifespan_) da aplicação (o termo "vida útil" será importante em um segundo 😉).
Pode ser muito útil para configurar **recursos** que você precisa usar por toda aplicação, e que são **compartilhados** entre as requisições, e/ou que você precisa **limpar** depois. Por exemplo, o pool de uma conexão com o banco de dados ou carregamento de um modelo compartilhado de aprendizado de máquina (_machine learning_).
## Caso de uso
Vamos iniciar com um exemplo de **caso de uso** e então ver como resolvê-lo com isso.
Vamos imaginar que você tem alguns **modelos de _machine learning_** que deseja usar para lidar com as requisições. 🤖
Os mesmos modelos são compartilhados entre as requisições, então não é um modelo por requisição, ou um por usuário ou algo parecido.
Vamos imaginar que o carregamento do modelo pode **demorar bastante tempo**, porque ele tem que ler muitos **dados do disco**. Então você não quer fazer isso a cada requisição.
Você poderia carregá-lo no nível mais alto do módulo/arquivo, mas isso também poderia significaria **carregar o modelo** mesmo se você estiver executando um simples teste automatizado, então esse teste poderia ser **lento** porque teria que esperar o carregamento do modelo antes de ser capaz de executar uma parte independente do código.
Isso é que nós iremos resolver, vamos carregar o modelo antes das requisições serem manuseadas, mas apenas um pouco antes da aplicação começar a receber requisições, não enquanto o código estiver sendo carregado.
## Vida útil (_Lifespan_)
Você pode definir essa lógica de *inicialização* e *encerramento* usando os parâmetros de `lifespan` da aplicação `FastAPI`, e um "gerenciador de contexto" (te mostrarei o que é isso a seguir).
Vamos iniciar com um exemplo e ver isso detalhadamente.
Nós criamos uma função assíncrona chamada `lifespan()` com `yield` como este:
```Python hl_lines="16 19"
{!../../../docs_src/events/tutorial003.py!}
```
Aqui nós estamos simulando a *inicialização* custosa do carregamento do modelo colocando a (falsa) função de modelo no dicionário com modelos de _machine learning_ antes do `yield`. Este código será executado **antes** da aplicação **começar a receber requisições**, durante a *inicialização*.
E então, logo após o `yield`, descarregaremos o modelo. Esse código será executado **após** a aplicação **terminar de lidar com as requisições**, pouco antes do *encerramento*. Isso poderia, por exemplo, liberar recursos como memória ou GPU.
!!! tip "Dica"
O `shutdown` aconteceria quando você estivesse **encerrando** a aplicação.
Talvez você precise inicializar uma nova versão, ou apenas cansou de executá-la. 🤷
### Função _lifespan_
A primeira coisa a notar, é que estamos definindo uma função assíncrona com `yield`. Isso é muito semelhante à Dependências com `yield`.
```Python hl_lines="14-19"
{!../../../docs_src/events/tutorial003.py!}
```
A primeira parte da função, antes do `yield`, será executada **antes** da aplicação inicializar.
E a parte posterior do `yield` irá executar **após** a aplicação ser encerrada.
### Gerenciador de Contexto Assíncrono
Se você verificar, a função está decorada com um `@asynccontextmanager`.
Que converte a função em algo chamado de "**Gerenciador de Contexto Assíncrono**".
```Python hl_lines="1 13"
{!../../../docs_src/events/tutorial003.py!}
```
Um **gerenciador de contexto** em Python é algo que você pode usar em uma declaração `with`, por exemplo, `open()` pode ser usado como um gerenciador de contexto:
```Python
with open("file.txt") as file:
file.read()
```
Nas versões mais recentes de Python, há também um **gerenciador de contexto assíncrono**. Você o usaria com `async with`:
```Python
async with lifespan(app):
await do_stuff()
```
Quando você cria um gerenciador de contexto ou um gerenciador de contexto assíncrono como mencionado acima, o que ele faz é que, antes de entrar no bloco `with`, ele irá executar o código anterior ao `yield`, e depois de sair do bloco `with`, ele irá executar o código depois do `yield`.
No nosso exemplo de código acima, nós não usamos ele diretamente, mas nós passamos para o FastAPI para ele usá-lo.
O parâmetro `lifespan` da aplicação `FastAPI` usa um **Gerenciador de Contexto Assíncrono**, então nós podemos passar nosso novo gerenciador de contexto assíncrono do `lifespan` para ele.
```Python hl_lines="22"
{!../../../docs_src/events/tutorial003.py!}
```
## Eventos alternativos (deprecados)
!!! warning "Aviso"
A maneira recomendada para lidar com a *inicialização* e o *encerramento* é usando o parâmetro `lifespan` da aplicação `FastAPI` como descrito acima.
Você provavelmente pode pular essa parte.
Existe uma forma alternativa para definir a execução dessa lógica durante *inicialização* e durante *encerramento*.
Você pode definir manipuladores de eventos (funções) que precisam ser executadas antes da aplicação inicializar, ou quando a aplicação estiver encerrando.
Essas funções podem ser declaradas com `async def` ou `def` normal.
### Evento `startup`
Para adicionar uma função que deve rodar antes da aplicação iniciar, declare-a com o evento `"startup"`:
```Python hl_lines="8"
{!../../../docs_src/events/tutorial001.py!}
```
Nesse caso, a função de manipulação de evento `startup` irá inicializar os itens do "banco de dados" (só um `dict`) com alguns valores.
Você pode adicionar mais que uma função de manipulação de evento.
E sua aplicação não irá começar a receber requisições até que todos os manipuladores de eventos de `startup` sejam concluídos.
### Evento `shutdown`
Para adicionar uma função que deve ser executada quando a aplicação estiver encerrando, declare ela com o evento `"shutdown"`:
```Python hl_lines="6"
{!../../../docs_src/events/tutorial002.py!}
```
Aqui, a função de manipulação de evento `shutdown` irá escrever uma linha de texto `"Application shutdown"` no arquivo `log.txt`.
!!! info "Informação"
Na função `open()`, o `mode="a"` significa "acrescentar", então, a linha irá ser adicionada depois de qualquer coisa que esteja naquele arquivo, sem sobrescrever o conteúdo anterior.
!!! tip "Dica"
Perceba que nesse caso nós estamos usando a função padrão do Python `open()` que interage com um arquivo.
Então, isso envolve I/O (input/output), que exige "esperar" que coisas sejam escritas em disco.
Mas `open()` não usa `async` e `await`.
Então, nós declaramos uma função de manipulação de evento com o padrão `def` ao invés de `async def`.
### `startup` e `shutdown` juntos
Há uma grande chance que a lógica para sua *inicialização* e *encerramento* esteja conectada, você pode querer iniciar alguma coisa e então finalizá-la, adquirir um recurso e então liberá-lo, etc.
Fazendo isso em funções separadas que não compartilham lógica ou variáveis entre elas é mais difícil já que você precisa armazenar os valores em variáveis globais ou truques parecidos.
Por causa disso, agora é recomendado em vez disso usar o `lifespan` como explicado acima.
## Detalhes técnicos
Só um detalhe técnico para nerds curiosos. 🤓
Por baixo, na especificação técnica ASGI, essa é a parte do <a href="https://asgi.readthedocs.io/en/latest/specs/lifespan.html" class="external-link" target="_blank">Protocolo Lifespan</a>, e define eventos chamados `startup` e `shutdown`.
!!! info "Informação"
Você pode ler mais sobre o manipulador `lifespan` do Starlette na <a href="https://www.starlette.io/lifespan/" class="external-link" target="_blank">Documentação do Lifespan Starlette</a>.
Incluindo como manipular estado do lifespan que pode ser usado em outras áreas do seu código.
## Sub Aplicações
🚨 Tenha em mente que esses eventos de lifespan (de inicialização e desligamento) irão somente ser executados para a aplicação principal, não para [Sub Aplicações - Montagem](./sub-applications.md){.internal-link target=_blank}.

View File

@@ -292,7 +292,7 @@ Agora vá para <a href="http://127.0.0.1:8000/docs" class="external-link" target
![Swagger UI](https://fastapi.tiangolo.com/img/index/index-03-swagger-02.png)
* Clique no botão "Try it out", ele permiirá que você preencha os parâmetros e interaja diretamente com a API:
* Clique no botão "Try it out", ele permitirá que você preencha os parâmetros e interaja diretamente com a API:
![Swagger UI interaction](https://fastapi.tiangolo.com/img/index/index-04-swagger-03.png)

View File

@@ -40,6 +40,7 @@ nav:
- Languages:
- en: /
- az: /az/
- cs: /cs/
- de: /de/
- em: /em/
- es: /es/
@@ -51,6 +52,7 @@ nav:
- it: /it/
- ja: /ja/
- ko: /ko/
- lo: /lo/
- nl: /nl/
- pl: /pl/
- pt: /pt/
@@ -90,6 +92,7 @@ nav:
- tutorial/static-files.md
- Guia de Usuário Avançado:
- advanced/index.md
- advanced/events.md
- Implantação:
- deployment/index.md
- deployment/versions.md
@@ -144,6 +147,8 @@ extra:
name: en - English
- link: /az/
name: az
- link: /cs/
name: cs
- link: /de/
name: de
- link: /em/
@@ -166,6 +171,8 @@ extra:
name: ja - 日本語
- link: /ko/
name: ko - 한국어
- link: /lo/
name: lo - ພາສາລາວ
- link: /nl/
name: nl
- link: /pl/

View File

@@ -0,0 +1,198 @@
# Об HTTPS
Обычно представляется, что HTTPS это некая опция, которая либо "включена", либо нет.
Но всё несколько сложнее.
!!! tip "Заметка"
Если Вы торопитесь или Вам не интересно, можете перейти на следующую страницу этого пошагового руководства по размещению приложений на серверах с использованием различных технологий.
Чтобы **изучить основы HTTPS** для клиента, перейдите по ссылке <a href="https://howhttps.works/" class="external-link" target="_blank">https://howhttps.works/</a>.
Здесь же представлены некоторые концепции, которые **разработчик** должен иметь в виду при размышлениях об HTTPS:
* Протокол HTTPS предполагает, что **серверу** нужно **располагать "сертификатами"** сгенерированными **третьей стороной**.
* На самом деле эти сертификаты **приобретены** у третьей стороны, а не "сгенерированы".
* У сертификатов есть **срок годности**.
* Срок годности **истекает**.
* По истечении срока годности их нужно **обновить**, то есть **снова получить** у третьей стороны.
* Шифрование соединения происходит **на уровне протокола TCP**.
* Протокол TCP находится на один уровень **ниже протокола HTTP**.
* Поэтому **проверка сертификатов и шифрование** происходит **до HTTP**.
* **TCP не знает о "доменах"**, но знает об IP-адресах.
* Информация о **запрашиваемом домене** извлекается из запроса **на уровне HTTP**.
* **Сертификаты HTTPS** "сертифицируют" **конкретный домен**, но проверка сертификатов и шифрование данных происходит на уровне протокола TCP, то есть **до того**, как станет известен домен-получатель данных.
* **По умолчанию** это означает, что у Вас может быть **только один сертификат HTTPS на один IP-адрес**.
* Не важно, насколько большой у Вас сервер и насколько маленькие приложения на нём могут быть.
* Однако, у этой проблемы есть **решение**.
* Существует **расширение** протокола **TLS** (который работает на уровне TCP, то есть до HTTP) называемое **<a href="https://en.wikipedia.org/wiki/Server_Name_Indication" class="external-link" target="_blank"><abbr title="Указание имени сервера">SNI</abbr></a>**.
* Расширение SNI позволяет одному серверу (с **одним IP-адресом**) иметь **несколько сертификатов HTTPS** и обслуживать **множество HTTPS-доменов/приложений**.
* Чтобы эта конструкция работала, **один** её компонент (программа) запущенный на сервере и слушающий **публичный IP-адрес**, должен иметь **все сертификаты HTTPS** для этого сервера.
* **После** установления защищённого соединения, протоколом передачи данных **остаётся HTTP**.
* Но данные теперь **зашифрованы**, несмотря на то, что они передаются по **протоколу HTTP**.
Обычной практикой является иметь **одну программу/HTTP-сервер** запущенную на сервере (машине, хосте и т.д.) и **ответственную за всю работу с HTTPS**:
* получение **зашифрованных HTTPS-запросов**
* отправка **расшифрованных HTTP запросов** в соответствующее HTTP-приложение, работающее на том же сервере (в нашем случае, это приложение **FastAPI**)
* получние **HTTP-ответа** от приложения
* **шифрование ответа** используя подходящий **сертификат HTTPS**
* отправка зашифрованного **HTTPS-ответа клиенту**.
Такой сервер часто называют **<a href="https://en.wikipedia.org/wiki/TLS_termination_proxy" class="external-link" target="_blank">Прокси-сервер завершения работы TLS</a>** или просто "прокси-сервер".
Вот некоторые варианты, которые Вы можете использовать в качестве такого прокси-сервера:
* Traefik (может обновлять сертификаты)
* Caddy (может обновлять сертификаты)
* Nginx
* HAProxy
## Let's Encrypt (центр сертификации)
До появления Let's Encrypt **сертификаты HTTPS** приходилось покупать у третьих сторон.
Процесс получения такого сертификата был трудоёмким, требовал предоставления подтверждающих документов и сертификаты стоили дорого.
Но затем консорциумом Linux Foundation был создан проект **<a href="https://letsencrypt.org/" class="external-link" target="_blank">Let's Encrypt</a>**.
Он автоматически предоставляет **бесплатные сертификаты HTTPS**. Эти сертификаты используют все стандартные криптографические способы шифрования. Они имеют небольшой срок годности (около 3 месяцев), благодаря чему они даже **более безопасны**.
При запросе на получение сертификата, он автоматически генерируется и домен проверяется на безопасность. Это позволяет обновлять сертификаты автоматически.
Суть идеи в автоматическом получении и обновлении этих сертификатов, чтобы все могли пользоваться **безопасным HTTPS. Бесплатно. В любое время.**
## HTTPS для разработчиков
Ниже, шаг за шагом, с заострением внимания на идеях, важных для разработчика, описано, как может выглядеть HTTPS API.
### Имя домена
Чаще всего, всё начинается с **приобретения имени домена**. Затем нужно настроить DNS-сервер (вероятно у того же провайдера, который выдал Вам домен).
Далее, возможно, Вы получаете "облачный" сервер (виртуальную машину) или что-то типа этого, у которого есть <abbr title="Не изменяемый">постоянный</abbr> **публичный IP-адрес**.
На DNS-сервере (серверах) Вам следует настроить соответствующую ресурсную запись ("`запись A`"), указав, что **Ваш домен** связан с публичным **IP-адресом Вашего сервера**.
Обычно эту запись достаточно указать один раз, при первоначальной настройке всего сервера.
!!! tip "Заметка"
Уровни протоколов, работающих с именами доменов, намного ниже HTTPS, но об этом следует упомянуть здесь, так как всё зависит от доменов и IP-адресов.
### DNS
Теперь давайте сфокусируемся на работе с HTTPS.
Всё начинается с того, что браузер спрашивает у **DNS-серверов**, какой **IP-адрес связан с доменом**, для примера возьмём домен `someapp.example.com`.
DNS-сервера присылают браузеру определённый **IP-адрес**, тот самый публичный IP-адрес Вашего сервера, который Вы указали в ресурсной "записи А" при настройке.
<img src="/img/deployment/https/https01.svg">
### Рукопожатие TLS
В дальнейшем браузер будет взаимодействовать с этим IP-адресом через **port 443** (общепринятый номер порта для HTTPS).
Первым шагом будет установление соединения между клиентом (браузером) и сервером и выбор криптографического ключа (для шифрования).
<img src="/img/deployment/https/https02.svg">
Эта часть клиент-серверного взаимодействия устанавливает TLS-соединение и называется **TLS-рукопожатием**.
### TLS с расширением SNI
На сервере **только один процесс** может прослушивать определённый **порт** определённого **IP-адреса**. На сервере могут быть и другие процессы, слушающие другие порты этого же IP-адреса, но никакой процесс не может быть привязан к уже занятой комбинации IP-адрес:порт. Эта комбинация называется "сокет".
По умолчанию TLS (HTTPS) использует порт `443`. Потому этот же порт будем использовать и мы.
И раз уж только один процесс может слушать этот порт, то это будет процесс **прокси-сервера завершения работы TLS**.
Прокси-сервер завершения работы TLS будет иметь доступ к одному или нескольким **TLS-сертификатам** (сертификаты HTTPS).
Используя **расширение SNI** упомянутое выше, прокси-сервер из имеющихся сертификатов TLS (HTTPS) выберет тот, который соответствует имени домена, указанному в запросе от клиента.
То есть будет выбран сертификат для домена `someapp.example.com`.
<img src="/img/deployment/https/https03.svg">
Клиент уже **доверяет** тому, кто выдал этот TLS-сертификат (в нашем случае - Let's Encrypt, но мы ещё обсудим это), потому может **проверить**, действителен ли полученный от сервера сертификат.
Затем, используя этот сертификат, клиент и прокси-сервер **выбирают способ шифрования** данных для устанавливаемого **TCP-соединения**. На этом операция **TLS-рукопожатия** завершена.
В дальнейшем клиент и сервер будут взаимодействовать по **зашифрованному TCP-соединению**, как предлагается в протоколе TLS. И на основе этого TCP-соедениния будет создано **HTTP-соединение**.
Таким образом, **HTTPS** это тот же **HTTP**, но внутри **безопасного TLS-соединения** вместо чистого (незашифрованного) TCP-соединения.
!!! tip "Заметка"
Обратите внимание, что шифрование происходит на **уровне TCP**, а не на более высоком уровне HTTP.
### HTTPS-запрос
Теперь, когда между клиентом и сервером (в нашем случае, браузером и прокси-сервером) создано **зашифрованное TCP-соединение**, они могут начать **обмен данными по протоколу HTTP**.
Так клиент отправляет **HTTPS-запрос**. То есть обычный HTTP-запрос, но через зашифрованное TLS-содинение.
<img src="/img/deployment/https/https04.svg">
### Расшифровка запроса
Прокси-сервер, используя согласованный с клиентом ключ, расшифрует полученный **зашифрованный запрос** и передаст **обычный (незашифрованный) HTTP-запрос** процессу, запускающему приложение (например, процессу Uvicorn запускающему приложение FastAPI).
<img src="/img/deployment/https/https05.svg">
### HTTP-ответ
Приложение обработает запрос и вернёт **обычный (незашифрованный) HTTP-ответ** прокси-серверу.
<img src="/img/deployment/https/https06.svg">
### HTTPS-ответ
Пркоси-сервер **зашифрует ответ** используя ранее согласованный с клиентом способ шифрования (которые содержатся в сертификате для домена `someapp.example.com`) и отправит его браузеру.
Наконец, браузер проверит ответ, в том числе, что тот зашифрован с нужным ключом, **расшифрует его** и обработает.
<img src="/img/deployment/https/https07.svg">
Клиент (браузер) знает, что ответ пришёл от правильного сервера, так как использует методы шифрования, согласованные ими раннее через **HTTPS-сертификат**.
### Множество приложений
На одном и том же сервере (или серверах) можно разместить **множество приложений**, например, другие программы с API или базы данных.
Напомню, что только один процесс (например, прокси-сервер) может прослушивать определённый порт определённого IP-адреса.
Но другие процессы и приложения тоже могут работать на этом же сервере (серверах), если они не пытаются использовать уже занятую **комбинацию IP-адреса и порта** (сокет).
<img src="/img/deployment/https/https08.svg">
Таким образом, сервер завершения TLS может обрабатывать HTTPS-запросы и использовать сертификаты для **множества доменов** или приложений и передавать запросы правильным адресатам (другим приложениям).
### Обновление сертификата
В недалёком будущем любой сертификат станет **просроченным** (примерно через три месяца после получения).
Когда это произойдёт, можно запустить другую программу, которая подключится к Let's Encrypt и обновит сертификат(ы). Существуют прокси-серверы, которые могут сделать это действие самостоятельно.
<img src="/img/deployment/https/https.svg">
**TLS-сертификаты** не привязаны к IP-адресу, но **связаны с именем домена**.
Так что при обновлении сертификатов программа должна **подтвердить** центру сертификации (Let's Encrypt), что обновление запросил **"владелец", который контролирует этот домен**.
Есть несколько путей осуществления этого. Самые популярные из них:
* **Изменение записей DNS**.
* Для такого варианта Ваша программа обновления должна уметь работать с API DNS-провайдера, обслуживающего Ваши DNS-записи. Не у всех провайдеров есть такой API, так что этот способ не всегда применим.
* **Запуск в качестве программы-сервера** (как минимум, на время обновления сертификатов) на публичном IP-адресе домена.
* Как уже не раз упоминалось, только один процесс может прослушивать определённый порт определённого IP-адреса.
* Это одна из причин использования прокси-сервера ещё и в качестве программы обновления сертификатов.
* В случае, если обновлением сертификатов занимается другая программа, Вам понадобится остановить прокси-сервер, запустить программу обновления сертификатов на сокете, предназначенном для прокси-сервера, настроить прокси-сервер на работу с новыми сертификатами и перезапустить его. Эта схема далека от идеальной, так как Ваши приложения будут недоступны на время отключения прокси-сервера.
Весь этот процесс обновления, одновременный с обслуживанием запросов, является одной из основных причин, по которой желательно иметь **отдельную систему для работы с HTTPS** в виде прокси-сервера завершения TLS, а не просто использовать сертификаты TLS непосредственно с сервером приложений (например, Uvicorn).
## Резюме
Наличие **HTTPS** очень важно и довольно **критично** в большинстве случаев. Однако, Вам, как разработчику, не нужно тратить много сил на это, достаточно **понимать эти концепции** и принципы их работы.
Но узнав базовые основы **HTTPS** Вы можете легко совмещать разные инструменты, которые помогут Вам в дальнейшей разработке.
В следующих главах я покажу Вам несколько примеров, как настраивать **HTTPS** для приложений **FastAPI**. 🔒

View File

@@ -0,0 +1,150 @@
# Запуск сервера вручную - Uvicorn
Для запуска приложения **FastAPI** на удалённой серверной машине Вам необходим программный сервер, поддерживающий протокол ASGI, такой как **Uvicorn**.
Существует три наиболее распространённые альтернативы:
* <a href="https://www.uvicorn.org/" class="external-link" target="_blank">Uvicorn</a>: высокопроизводительный ASGI сервер.
* <a href="https://pgjones.gitlab.io/hypercorn/" class="external-link" target="_blank">Hypercorn</a>: ASGI сервер, помимо прочего поддерживающий HTTP/2 и Trio.
* <a href="https://github.com/django/daphne" class="external-link" target="_blank">Daphne</a>: ASGI сервер, созданный для Django Channels.
## Сервер как машина и сервер как программа
В этих терминах есть некоторые различия и Вам следует запомнить их. 💡
Слово "**сервер**" чаще всего используется в двух контекстах:
- удалённый или расположенный в "облаке" компьютер (физическая или виртуальная машина).
- программа, запущенная на таком компьютере (например, Uvicorn).
Просто запомните, если Вам встретился термин "сервер", то обычно он подразумевает что-то из этих двух смыслов.
Когда имеют в виду именно удалённый компьютер, часто говорят просто **сервер**, но ещё его называют **машина**, **ВМ** (виртуальная машина), **нода**. Все эти термины обозначают одно и то же - удалённый компьютер, обычно под управлением Linux, на котором Вы запускаете программы.
## Установка программного сервера
Вы можете установить сервер, совместимый с протоколом ASGI, так:
=== "Uvicorn"
* <a href="https://www.uvicorn.org/" class="external-link" target="_blank">Uvicorn</a>, молниесный ASGI сервер, основанный на библиотеках uvloop и httptools.
<div class="termy">
```console
$ pip install "uvicorn[standard]"
---> 100%
```
</div>
!!! tip "Подсказка"
С опцией `standard`, Uvicorn будет установливаться и использоваться с некоторыми дополнительными рекомендованными зависимостями.
В них входит `uvloop`, высокопроизводительная замена `asyncio`, которая значительно ускоряет работу асинхронных программ.
=== "Hypercorn"
* <a href="https://gitlab.com/pgjones/hypercorn" class="external-link" target="_blank">Hypercorn</a>, ASGI сервер, поддерживающий протокол HTTP/2.
<div class="termy">
```console
$ pip install hypercorn
---> 100%
```
</div>
...или какой-либо другой ASGI сервер.
## Запуск серверной программы
Затем запустите Ваше приложение так же, как было указано в руководстве ранее, но без опции `--reload`:
=== "Uvicorn"
<div class="termy">
```console
$ uvicorn main:app --host 0.0.0.0 --port 80
<span style="color: green;">INFO</span>: Uvicorn running on http://0.0.0.0:80 (Press CTRL+C to quit)
```
</div>
=== "Hypercorn"
<div class="termy">
```console
$ hypercorn main:app --bind 0.0.0.0:80
Running on 0.0.0.0:8080 over http (CTRL + C to quit)
```
</div>
!!! warning "Предупреждение"
Не забудьте удалить опцию `--reload`, если ранее пользовались ею.
Включение опции `--reload` требует дополнительных ресурсов, влияет на стабильность работы приложения и может повлечь прочие неприятности.
Она сильно помогает во время **разработки**, но **не следует** использовать её при **реальной работе** приложения.
## Hypercorn с Trio
Starlette и **FastAPI** основаны на <a href="https://anyio.readthedocs.io/en/stable/" class="external-link" target="_blank">AnyIO</a>, которая делает их совместимыми как с <a href="https://docs.python.org/3/library/asyncio-task.html" class="external-link" target="_blank">asyncio</a> - стандартной библиотекой Python, так и с <a href="https://trio.readthedocs.io/en/stable/" class="external-link" target="_blank">Trio</a>.
Тем не менее Uvicorn совместим только с asyncio и обычно используется совместно с <a href="https://github.com/MagicStack/uvloop" class="external-link" target="_blank">`uvloop`</a>, высокопроизводительной заменой `asyncio`.
Но если Вы хотите использовать **Trio** напрямую, то можете воспользоваться **Hypercorn**, так как они совместимы. ✨
### Установка Hypercorn с Trio
Для начала, Вам нужно установить Hypercorn с поддержкой Trio:
<div class="termy">
```console
$ pip install "hypercorn[trio]"
---> 100%
```
</div>
### Запуск с Trio
Далее запустите Hypercorn с опцией `--worker-class` и аргументом `trio`:
<div class="termy">
```console
$ hypercorn main:app --worker-class trio
```
</div>
Hypercorn, в свою очередь, запустит Ваше приложение использующее Trio.
Таким образом, Вы сможете использовать Trio в своём приложении. Но лучше использовать AnyIO, для сохранения совместимости и с Trio, и с asyncio. 🎉
## Концепции развёртывания
В вышеприведённых примерах серверные программы (например Uvicorn) запускали только **один процесс**, принимающий входящие запросы с любого IP (на это указывал аргумент `0.0.0.0`) на определённый порт (в примерах мы указывали порт `80`).
Это основная идея. Но возможно, Вы озаботитесь добавлением дополнительных возможностей, таких как:
* Использование более безопасного протокола HTTPS
* Настройки запуска приложения
* Перезагрузка приложения
* Запуск нескольких экземпляров приложения
* Управление памятью
* Использование перечисленных функций перед запуском приложения.
Я поведаю Вам больше о каждой из этих концепций в следующих главах, с конкретными примерами стратегий работы с ними. 🚀

View File

@@ -0,0 +1,89 @@
# HTTP коды статуса ответа
Вы можете задать HTTP код статуса ответа с помощью параметра `status_code` подобно тому, как вы определяете схему ответа в любой из *операций пути*:
* `@app.get()`
* `@app.post()`
* `@app.put()`
* `@app.delete()`
* и других.
```Python hl_lines="6"
{!../../../docs_src/response_status_code/tutorial001.py!}
```
!!! note "Примечание"
Обратите внимание, что `status_code` является атрибутом метода-декоратора (`get`, `post` и т.д.), а не *функции-обработчика пути* в отличие от всех остальных параметров и тела запроса.
Параметр `status_code` принимает число, обозначающее HTTP код статуса ответа.
!!! info "Информация"
В качестве значения параметра `status_code` также может использоваться `IntEnum`, например, из библиотеки <a href="https://docs.python.org/3/library/http.html#http.HTTPStatus" class="external-link" target="_blank">`http.HTTPStatus`</a> в Python.
Это позволит:
* Возвращать указанный код статуса в ответе.
* Документировать его как код статуса ответа в OpenAPI схеме (а значит, и в пользовательском интерфейсе):
<img src="/img/tutorial/response-status-code/image01.png">
!!! note "Примечание"
Некоторые коды статуса ответа (см. следующий раздел) указывают на то, что ответ не имеет тела.
FastAPI знает об этом и создаст документацию OpenAPI, в которой будет указано, что тело ответа отсутствует.
## Об HTTP кодах статуса ответа
!!! note "Примечание"
Если вы уже знаете, что представляют собой HTTP коды статуса ответа, можете перейти к следующему разделу.
В протоколе HTTP числовой код состояния из 3 цифр отправляется как часть ответа.
У кодов статуса есть названия, чтобы упростить их распознавание, но важны именно числовые значения.
Кратко о значениях кодов:
* `1XX` статус-коды информационного типа. Они редко используются разработчиками напрямую. Ответы с этими кодами не могут иметь тела.
* **`2XX`** статус-коды, сообщающие об успешной обработке запроса. Они используются чаще всего.
* `200` это код статуса ответа по умолчанию, который означает, что все прошло "OK".
* Другим примером может быть статус `201`, "Created". Он обычно используется после создания новой записи в базе данных.
* Особый случай `204`, "No Content". Этот статус ответа используется, когда нет содержимого для возврата клиенту, и поэтому ответ не должен иметь тела.
* **`3XX`** статус-коды, сообщающие о перенаправлениях. Ответы с этими кодами статуса могут иметь или не иметь тело, за исключением ответов со статусом `304`, "Not Modified", у которых не должно быть тела.
* **`4XX`** статус-коды, сообщающие о клиентской ошибке. Это ещё одна наиболее часто используемая категория.
* Пример код `404` для статуса "Not Found".
* Для общих ошибок со стороны клиента можно просто использовать код `400`.
* `5XX` – статус-коды, сообщающие о серверной ошибке. Они почти никогда не используются разработчиками напрямую. Когда что-то идет не так в какой-то части кода вашего приложения или на сервере, он автоматически вернёт один из 5XX кодов.
!!! tip "Подсказка"
Чтобы узнать больше о HTTP кодах статуса и о том, для чего каждый из них предназначен, ознакомьтесь с <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Status" class="external-link" target="_blank">документацией <abbr title="Mozilla Developer Network">MDN</abbr> об HTTP кодах статуса ответа</a>.
## Краткие обозначения для запоминания названий кодов
Рассмотрим предыдущий пример еще раз:
```Python hl_lines="6"
{!../../../docs_src/response_status_code/tutorial001.py!}
```
`201` это код статуса "Создано".
Но вам не обязательно запоминать, что означает каждый из этих кодов.
Для удобства вы можете использовать переменные из `fastapi.status`.
```Python hl_lines="1 6"
{!../../../docs_src/response_status_code/tutorial002.py!}
```
Они содержат те же числовые значения, но позволяют использовать подсказки редактора для выбора кода статуса:
<img src="/img/tutorial/response-status-code/image02.png">
!!! note "Технические детали"
Вы также можете использовать `from starlette import status` вместо `from fastapi import status`.
**FastAPI** позволяет использовать как `starlette.status`, так и `fastapi.status` исключительно для удобства разработчиков. Но поставляется fastapi.status непосредственно из Starlette.
## Изменение кода статуса по умолчанию
Позже, в [Руководстве для продвинутых пользователей](../advanced/response-change-status-code.md){.internal-link target=_blank}, вы узнаете, как возвращать HTTP коды статуса, отличные от используемого здесь кода статуса по умолчанию.

View File

@@ -0,0 +1,212 @@
# Тестирование
Благодаря <a href="https://www.starlette.io/testclient/" class="external-link" target="_blank">Starlette</a>, тестировать приложения **FastAPI** легко и приятно.
Тестирование основано на библиотеке <a href="https://www.python-httpx.org" class="external-link" target="_blank">HTTPX</a>, которая в свою очередь основана на библиотеке Requests, так что все действия знакомы и интуитивно понятны.
Используя эти инструменты, Вы можете напрямую задействовать <a href="https://docs.pytest.org/" class="external-link" target="_blank">pytest</a> с **FastAPI**.
## Использование класса `TestClient`
!!! info "Информация"
Для использования класса `TestClient` необходимо установить библиотеку <a href="https://www.python-httpx.org" class="external-link" target="_blank">`httpx`</a>.
Например, так: `pip install httpx`.
Импортируйте `TestClient`.
Создайте объект `TestClient`, передав ему в качестве параметра Ваше приложение **FastAPI**.
Создайте функцию, название которой должно начинаться с `test_` (это стандарт из соглашений `pytest`).
Используйте объект `TestClient` так же, как Вы используете `httpx`.
Напишите простое утверждение с `assert` дабы проверить истинность Python-выражения (это тоже стандарт `pytest`).
```Python hl_lines="2 12 15-18"
{!../../../docs_src/app_testing/tutorial001.py!}
```
!!! tip "Подсказка"
Обратите внимание, что тестирующая функция является обычной `def`, а не асинхронной `async def`.
И вызов клиента также осуществляется без `await`.
Это позволяет вам использовать `pytest` без лишних усложнений.
!!! note "Технические детали"
Также можно написать `from starlette.testclient import TestClient`.
**FastAPI** предоставляет тот же самый `starlette.testclient` как `fastapi.testclient`. Это всего лишь небольшое удобство для Вас, как разработчика.
!!! tip "Подсказка"
Если для тестирования Вам, помимо запросов к приложению FastAPI, необходимо вызывать асинхронные функции (например, для подключения к базе данных с помощью асинхронного драйвера), то ознакомьтесь со страницей [Асинхронное тестирование](../advanced/async-tests.md){.internal-link target=_blank} в расширенном руководстве.
## Разделение тестов и приложения
В реальном приложении Вы, вероятно, разместите тесты в отдельном файле.
Кроме того, Ваше приложение **FastAPI** может состоять из нескольких файлов, модулей и т.п.
### Файл приложения **FastAPI**
Допустим, структура файлов Вашего приложения похожа на ту, что описана на странице [Более крупные приложения](./bigger-applications.md){.internal-link target=_blank}:
```
.
├── app
│   ├── __init__.py
│   └── main.py
```
Здесь файл `main.py` является "точкой входа" в Ваше приложение и содержит инициализацию Вашего приложения **FastAPI**:
```Python
{!../../../docs_src/app_testing/main.py!}
```
### Файл тестов
Также у Вас может быть файл `test_main.py` содержащий тесты. Можно разместить тестовый файл и файл приложения в одной директории (в директориях для Python-кода желательно размещать и файл `__init__.py`):
``` hl_lines="5"
.
├── app
│   ├── __init__.py
│   ├── main.py
│   └── test_main.py
```
Так как оба файла находятся в одной директории, для импорта объекта приложения из файла `main` в файл `test_main` Вы можете использовать относительный импорт:
```Python hl_lines="3"
{!../../../docs_src/app_testing/test_main.py!}
```
...и писать дальше тесты, как и раньше.
## Тестирование: расширенный пример
Теперь давайте расширим наш пример и добавим деталей, чтоб посмотреть, как тестировать различные части приложения.
### Расширенный файл приложения **FastAPI**
Мы продолжим работу с той же файловой структурой, что и ранее:
```
.
├── app
│   ├── __init__.py
│   ├── main.py
│   └── test_main.py
```
Предположим, что в файле `main.py` с приложением **FastAPI** есть несколько **операций пути**.
В нём описана операция `GET`, которая может вернуть ошибку.
Ещё есть операция `POST` и она тоже может вернуть ошибку.
Обе *операции пути* требуют наличия в запросе заголовка `X-Token`.
=== "Python 3.10+"
```Python
{!> ../../../docs_src/app_testing/app_b_an_py310/main.py!}
```
=== "Python 3.9+"
```Python
{!> ../../../docs_src/app_testing/app_b_an_py39/main.py!}
```
=== "Python 3.6+"
```Python
{!> ../../../docs_src/app_testing/app_b_an/main.py!}
```
=== "Python 3.10+ без Annotated"
!!! tip "Подсказка"
По возможности используйте версию с `Annotated`.
```Python
{!> ../../../docs_src/app_testing/app_b_py310/main.py!}
```
=== "Python 3.6+ без Annotated"
!!! tip "Подсказка"
По возможности используйте версию с `Annotated`.
```Python
{!> ../../../docs_src/app_testing/app_b/main.py!}
```
### Расширенный файл тестов
Теперь обновим файл `test_main.py`, добавив в него тестов:
```Python
{!> ../../../docs_src/app_testing/app_b/test_main.py!}
```
Если Вы не знаете, как передать информацию в запросе, можете воспользоваться поисковиком (погуглить) и задать вопрос: "Как передать информацию в запросе с помощью `httpx`", можно даже спросить: "Как передать информацию в запросе с помощью `requests`", поскольку дизайн HTTPX основан на дизайне Requests.
Затем Вы просто применяете найденные ответы в тестах.
Например:
* Передаёте *path*-параметры или *query*-параметры, вписав их непосредственно в строку URL.
* Передаёте JSON в теле запроса, передав Python-объект (например: `dict`) через именованный параметр `json`.
* Если же Вам необходимо отправить *форму с данными* вместо JSON, то используйте параметр `data` вместо `json`.
* Для передачи *заголовков*, передайте объект `dict` через параметр `headers`.
* Для передачи *cookies* также передайте `dict`, но через параметр `cookies`.
Для получения дополнительной информации о передаче данных на бэкенд с помощью `httpx` или `TestClient` ознакомьтесь с <a href="https://www.python-httpx.org" class="external-link" target="_blank">документацией HTTPX</a>.
!!! info "Информация"
Обратите внимание, что `TestClient` принимает данные, которые можно конвертировать в JSON, но не модели Pydantic.
Если в Ваших тестах есть модели Pydantic и Вы хотите отправить их в тестируемое приложение, то можете использовать функцию `jsonable_encoder`, описанную на странице [Кодировщик совместимый с JSON](encoder.md){.internal-link target=_blank}.
## Запуск тестов
Далее Вам нужно установить `pytest`:
<div class="termy">
```console
$ pip install pytest
---> 100%
```
</div>
Он автоматически найдёт все файлы и тесты, выполнит их и предоставит Вам отчёт о результатах тестирования.
Запустите тесты командой `pytest` и увидите результат:
<div class="termy">
```console
$ pytest
================ test session starts ================
platform linux -- Python 3.6.9, pytest-5.3.5, py-1.8.1, pluggy-0.13.1
rootdir: /home/user/code/superawesome-cli/app
plugins: forked-1.1.3, xdist-1.31.0, cov-2.8.1
collected 6 items
---> 100%
test_main.py <span style="color: green; white-space: pre;">...... [100%]</span>
<span style="color: green;">================= 1 passed in 0.03s =================</span>
```
</div>

View File

@@ -40,6 +40,7 @@ nav:
- Languages:
- en: /
- az: /az/
- cs: /cs/
- de: /de/
- em: /em/
- es: /es/
@@ -51,6 +52,7 @@ nav:
- it: /it/
- ja: /ja/
- ko: /ko/
- lo: /lo/
- nl: /nl/
- pl: /pl/
- pt: /pt/
@@ -70,10 +72,14 @@ nav:
- tutorial/background-tasks.md
- tutorial/extra-data-types.md
- tutorial/cookie-params.md
- tutorial/testing.md
- tutorial/response-status-code.md
- async.md
- Развёртывание:
- deployment/index.md
- deployment/versions.md
- deployment/https.md
- deployment/manually.md
- project-generation.md
- alternatives.md
- history-design-future.md
@@ -124,6 +130,8 @@ extra:
name: en - English
- link: /az/
name: az
- link: /cs/
name: cs
- link: /de/
name: de
- link: /em/
@@ -146,6 +154,8 @@ extra:
name: ja - 日本語
- link: /ko/
name: ko - 한국어
- link: /lo/
name: lo - ພາສາລາວ
- link: /nl/
name: nl
- link: /pl/

View File

@@ -40,6 +40,7 @@ nav:
- Languages:
- en: /
- az: /az/
- cs: /cs/
- de: /de/
- em: /em/
- es: /es/
@@ -51,6 +52,7 @@ nav:
- it: /it/
- ja: /ja/
- ko: /ko/
- lo: /lo/
- nl: /nl/
- pl: /pl/
- pt: /pt/
@@ -104,6 +106,8 @@ extra:
name: en - English
- link: /az/
name: az
- link: /cs/
name: cs
- link: /de/
name: de
- link: /em/
@@ -126,6 +130,8 @@ extra:
name: ja - 日本語
- link: /ko/
name: ko - 한국어
- link: /lo/
name: lo - ພາສາລາວ
- link: /nl/
name: nl
- link: /pl/

View File

@@ -40,6 +40,7 @@ nav:
- Languages:
- en: /
- az: /az/
- cs: /cs/
- de: /de/
- em: /em/
- es: /es/
@@ -51,6 +52,7 @@ nav:
- it: /it/
- ja: /ja/
- ko: /ko/
- lo: /lo/
- nl: /nl/
- pl: /pl/
- pt: /pt/
@@ -104,6 +106,8 @@ extra:
name: en - English
- link: /az/
name: az
- link: /cs/
name: cs
- link: /de/
name: de
- link: /em/
@@ -126,6 +130,8 @@ extra:
name: ja - 日本語
- link: /ko/
name: ko - 한국어
- link: /lo/
name: lo - ພາສາລາວ
- link: /nl/
name: nl
- link: /pl/

View File

@@ -40,6 +40,7 @@ nav:
- Languages:
- en: /
- az: /az/
- cs: /cs/
- de: /de/
- em: /em/
- es: /es/
@@ -51,6 +52,7 @@ nav:
- it: /it/
- ja: /ja/
- ko: /ko/
- lo: /lo/
- nl: /nl/
- pl: /pl/
- pt: /pt/
@@ -104,6 +106,8 @@ extra:
name: en - English
- link: /az/
name: az
- link: /cs/
name: cs
- link: /de/
name: de
- link: /em/
@@ -126,6 +130,8 @@ extra:
name: ja - 日本語
- link: /ko/
name: ko - 한국어
- link: /lo/
name: lo - ພາສາລາວ
- link: /nl/
name: nl
- link: /pl/

View File

@@ -40,6 +40,7 @@ nav:
- Languages:
- en: /
- az: /az/
- cs: /cs/
- de: /de/
- em: /em/
- es: /es/
@@ -51,6 +52,7 @@ nav:
- it: /it/
- ja: /ja/
- ko: /ko/
- lo: /lo/
- nl: /nl/
- pl: /pl/
- pt: /pt/
@@ -109,6 +111,8 @@ extra:
name: en - English
- link: /az/
name: az
- link: /cs/
name: cs
- link: /de/
name: de
- link: /em/
@@ -131,6 +135,8 @@ extra:
name: ja - 日本語
- link: /ko/
name: ko - 한국어
- link: /lo/
name: lo - ພາສາລາວ
- link: /nl/
name: nl
- link: /pl/

View File

@@ -40,6 +40,7 @@ nav:
- Languages:
- en: /
- az: /az/
- cs: /cs/
- de: /de/
- em: /em/
- es: /es/
@@ -51,6 +52,7 @@ nav:
- it: /it/
- ja: /ja/
- ko: /ko/
- lo: /lo/
- nl: /nl/
- pl: /pl/
- pt: /pt/
@@ -104,6 +106,8 @@ extra:
name: en - English
- link: /az/
name: az
- link: /cs/
name: cs
- link: /de/
name: de
- link: /em/
@@ -126,6 +130,8 @@ extra:
name: ja - 日本語
- link: /ko/
name: ko - 한국어
- link: /lo/
name: lo - ພາສາລາວ
- link: /nl/
name: nl
- link: /pl/

View File

@@ -40,6 +40,7 @@ nav:
- Languages:
- en: /
- az: /az/
- cs: /cs/
- de: /de/
- em: /em/
- es: /es/
@@ -51,6 +52,7 @@ nav:
- it: /it/
- ja: /ja/
- ko: /ko/
- lo: /lo/
- nl: /nl/
- pl: /pl/
- pt: /pt/
@@ -161,6 +163,8 @@ extra:
name: en - English
- link: /az/
name: az
- link: /cs/
name: cs
- link: /de/
name: de
- link: /em/
@@ -183,6 +187,8 @@ extra:
name: ja - 日本語
- link: /ko/
name: ko - 한국어
- link: /lo/
name: lo - ພາສາລາວ
- link: /nl/
name: nl
- link: /pl/

View File

@@ -1,6 +1,7 @@
from fastapi import FastAPI
from fastapi.middleware.wsgi import WSGIMiddleware
from flask import Flask, escape, request
from flask import Flask, request
from markupsafe import escape
flask_app = Flask(__name__)

View File

@@ -1,6 +1,6 @@
"""FastAPI framework, high performance, easy to learn, fast to code, ready for production"""
__version__ = "0.95.1"
__version__ = "0.95.2"
from starlette import status as status

View File

@@ -41,7 +41,7 @@ classifiers = [
"Topic :: Internet :: WWW/HTTP",
]
dependencies = [
"starlette>=0.26.1,<0.27.0",
"starlette>=0.27.0,<0.28.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"]

View File

@@ -19,92 +19,91 @@ def foo(items: Items):
client = TestClient(app)
openapi_schema = {
"openapi": "3.0.2",
"info": {"title": "FastAPI", "version": "0.1.0"},
"paths": {
"/foo": {
"post": {
"responses": {
"200": {
"description": "Successful Response",
"content": {"application/json": {"schema": {}}},
},
"422": {
"description": "Validation Error",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/HTTPValidationError"
}
}
},
},
},
"summary": "Foo",
"operationId": "foo_foo_post",
"requestBody": {
"content": {
"application/json": {
"schema": {"$ref": "#/components/schemas/Items"}
}
},
"required": True,
},
}
}
},
"components": {
"schemas": {
"Items": {
"title": "Items",
"required": ["items"],
"type": "object",
"properties": {
"items": {
"title": "Items",
"type": "object",
"additionalProperties": {"type": "integer"},
}
},
},
"ValidationError": {
"title": "ValidationError",
"required": ["loc", "msg", "type"],
"type": "object",
"properties": {
"loc": {
"title": "Location",
"type": "array",
"items": {"anyOf": [{"type": "string"}, {"type": "integer"}]},
},
"msg": {"title": "Message", "type": "string"},
"type": {"title": "Error Type", "type": "string"},
},
},
"HTTPValidationError": {
"title": "HTTPValidationError",
"type": "object",
"properties": {
"detail": {
"title": "Detail",
"type": "array",
"items": {"$ref": "#/components/schemas/ValidationError"},
}
},
},
}
},
}
def test_additional_properties_schema():
response = client.get("/openapi.json")
assert response.status_code == 200, response.text
assert response.json() == openapi_schema
def test_additional_properties_post():
response = client.post("/foo", json={"items": {"foo": 1, "bar": 2}})
assert response.status_code == 200, response.text
assert response.json() == {"foo": 1, "bar": 2}
def test_openapi_schema():
response = client.get("/openapi.json")
assert response.status_code == 200, response.text
assert response.json() == {
"openapi": "3.0.2",
"info": {"title": "FastAPI", "version": "0.1.0"},
"paths": {
"/foo": {
"post": {
"responses": {
"200": {
"description": "Successful Response",
"content": {"application/json": {"schema": {}}},
},
"422": {
"description": "Validation Error",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/HTTPValidationError"
}
}
},
},
},
"summary": "Foo",
"operationId": "foo_foo_post",
"requestBody": {
"content": {
"application/json": {
"schema": {"$ref": "#/components/schemas/Items"}
}
},
"required": True,
},
}
}
},
"components": {
"schemas": {
"Items": {
"title": "Items",
"required": ["items"],
"type": "object",
"properties": {
"items": {
"title": "Items",
"type": "object",
"additionalProperties": {"type": "integer"},
}
},
},
"ValidationError": {
"title": "ValidationError",
"required": ["loc", "msg", "type"],
"type": "object",
"properties": {
"loc": {
"title": "Location",
"type": "array",
"items": {
"anyOf": [{"type": "string"}, {"type": "integer"}]
},
},
"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"},
}
},
},
}
},
}

View File

@@ -17,36 +17,33 @@ router.include_router(sub_router, prefix="/items")
app.include_router(router)
openapi_schema = {
"openapi": "3.0.2",
"info": {"title": "FastAPI", "version": "0.1.0"},
"paths": {
"/items/": {
"get": {
"responses": {
"200": {
"description": "Successful Response",
"content": {"application/json": {"schema": {}}},
}
},
"summary": "Read Item",
"operationId": "read_item_items__get",
}
}
},
}
client = TestClient(app)
def test_openapi_schema():
response = client.get("/openapi.json")
assert response.status_code == 200, response.text
assert response.json() == openapi_schema
def test_path_operation():
response = client.get("/items/")
assert response.status_code == 200, response.text
assert response.json() == {"id": "foo"}
def test_openapi_schema():
response = client.get("/openapi.json")
assert response.status_code == 200, response.text
assert response.json() == {
"openapi": "3.0.2",
"info": {"title": "FastAPI", "version": "0.1.0"},
"paths": {
"/items/": {
"get": {
"responses": {
"200": {
"description": "Successful Response",
"content": {"application/json": {"schema": {}}},
}
},
"summary": "Read Item",
"operationId": "read_item_items__get",
}
}
},
}

View File

@@ -25,114 +25,116 @@ def main_route(callback_url: HttpUrl):
pass # pragma: no cover
openapi_schema = {
"openapi": "3.0.2",
"info": {"title": "FastAPI", "version": "0.1.0"},
"paths": {
"/": {
"post": {
"summary": "Main Route",
"operationId": "main_route__post",
"parameters": [
{
"required": True,
"schema": {
"title": "Callback Url",
"maxLength": 2083,
"minLength": 1,
"type": "string",
"format": "uri",
},
"name": "callback_url",
"in": "query",
}
],
"responses": {
"200": {
"description": "Successful Response",
"content": {"application/json": {"schema": {}}},
},
"422": {
"description": "Validation Error",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/HTTPValidationError"
}
}
},
},
},
"callbacks": {
"callback_route": {
"{$callback_url}/callback/": {
"get": {
"summary": "Callback Route",
"operationId": "callback_route__callback_url__callback__get",
"responses": {
"400": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/CustomModel"
}
}
},
"description": "Bad Request",
},
"200": {
"description": "Successful Response",
"content": {"application/json": {"schema": {}}},
},
},
}
}
}
},
}
}
},
"components": {
"schemas": {
"CustomModel": {
"title": "CustomModel",
"required": ["a"],
"type": "object",
"properties": {"a": {"title": "A", "type": "integer"}},
},
"HTTPValidationError": {
"title": "HTTPValidationError",
"type": "object",
"properties": {
"detail": {
"title": "Detail",
"type": "array",
"items": {"$ref": "#/components/schemas/ValidationError"},
}
},
},
"ValidationError": {
"title": "ValidationError",
"required": ["loc", "msg", "type"],
"type": "object",
"properties": {
"loc": {
"title": "Location",
"type": "array",
"items": {"anyOf": [{"type": "string"}, {"type": "integer"}]},
},
"msg": {"title": "Message", "type": "string"},
"type": {"title": "Error Type", "type": "string"},
},
},
}
},
}
client = TestClient(app)
def test_openapi_schema():
response = client.get("/openapi.json")
assert response.status_code == 200, response.text
assert response.json() == openapi_schema
assert response.json() == {
"openapi": "3.0.2",
"info": {"title": "FastAPI", "version": "0.1.0"},
"paths": {
"/": {
"post": {
"summary": "Main Route",
"operationId": "main_route__post",
"parameters": [
{
"required": True,
"schema": {
"title": "Callback Url",
"maxLength": 2083,
"minLength": 1,
"type": "string",
"format": "uri",
},
"name": "callback_url",
"in": "query",
}
],
"responses": {
"200": {
"description": "Successful Response",
"content": {"application/json": {"schema": {}}},
},
"422": {
"description": "Validation Error",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/HTTPValidationError"
}
}
},
},
},
"callbacks": {
"callback_route": {
"{$callback_url}/callback/": {
"get": {
"summary": "Callback Route",
"operationId": "callback_route__callback_url__callback__get",
"responses": {
"400": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/CustomModel"
}
}
},
"description": "Bad Request",
},
"200": {
"description": "Successful Response",
"content": {
"application/json": {"schema": {}}
},
},
},
}
}
}
},
}
}
},
"components": {
"schemas": {
"CustomModel": {
"title": "CustomModel",
"required": ["a"],
"type": "object",
"properties": {"a": {"title": "A", "type": "integer"}},
},
"HTTPValidationError": {
"title": "HTTPValidationError",
"type": "object",
"properties": {
"detail": {
"title": "Detail",
"type": "array",
"items": {"$ref": "#/components/schemas/ValidationError"},
}
},
},
"ValidationError": {
"title": "ValidationError",
"required": ["loc", "msg", "type"],
"type": "object",
"properties": {
"loc": {
"title": "Location",
"type": "array",
"items": {
"anyOf": [{"type": "string"}, {"type": "integer"}]
},
},
"msg": {"title": "Message", "type": "string"},
"type": {"title": "Error Type", "type": "string"},
},
},
}
},
}

View File

@@ -30,71 +30,70 @@ async def a(id):
pass # pragma: no cover
openapi_schema = {
"openapi": "3.0.2",
"info": {"title": "FastAPI", "version": "0.1.0"},
"paths": {
"/a/{id}": {
"get": {
"responses": {
"422": {
"description": "Error",
"content": {
"application/vnd.api+json": {
"schema": {"$ref": "#/components/schemas/JsonApiError"}
}
},
},
"200": {
"description": "Successful Response",
"content": {"application/vnd.api+json": {"schema": {}}},
},
},
"summary": "A",
"operationId": "a_a__id__get",
"parameters": [
{
"required": True,
"schema": {"title": "Id"},
"name": "id",
"in": "path",
}
],
}
}
},
"components": {
"schemas": {
"Error": {
"title": "Error",
"required": ["status", "title"],
"type": "object",
"properties": {
"status": {"title": "Status", "type": "string"},
"title": {"title": "Title", "type": "string"},
},
},
"JsonApiError": {
"title": "JsonApiError",
"required": ["errors"],
"type": "object",
"properties": {
"errors": {
"title": "Errors",
"type": "array",
"items": {"$ref": "#/components/schemas/Error"},
}
},
},
}
},
}
client = TestClient(app)
def test_openapi_schema():
response = client.get("/openapi.json")
assert response.status_code == 200, response.text
assert response.json() == openapi_schema
assert response.json() == {
"openapi": "3.0.2",
"info": {"title": "FastAPI", "version": "0.1.0"},
"paths": {
"/a/{id}": {
"get": {
"responses": {
"422": {
"description": "Error",
"content": {
"application/vnd.api+json": {
"schema": {
"$ref": "#/components/schemas/JsonApiError"
}
}
},
},
"200": {
"description": "Successful Response",
"content": {"application/vnd.api+json": {"schema": {}}},
},
},
"summary": "A",
"operationId": "a_a__id__get",
"parameters": [
{
"required": True,
"schema": {"title": "Id"},
"name": "id",
"in": "path",
}
],
}
}
},
"components": {
"schemas": {
"Error": {
"title": "Error",
"required": ["status", "title"],
"type": "object",
"properties": {
"status": {"title": "Status", "type": "string"},
"title": {"title": "Title", "type": "string"},
},
},
"JsonApiError": {
"title": "JsonApiError",
"required": ["errors"],
"type": "object",
"properties": {
"errors": {
"title": "Errors",
"type": "array",
"items": {"$ref": "#/components/schemas/Error"},
}
},
},
}
},
}

View File

@@ -9,77 +9,76 @@ async def a(id):
pass # pragma: no cover
openapi_schema = {
"openapi": "3.0.2",
"info": {"title": "FastAPI", "version": "0.1.0"},
"paths": {
"/a/{id}": {
"get": {
"responses": {
"422": {
"description": "Validation Error",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/HTTPValidationError"
}
}
},
},
"200": {
"description": "Successful Response",
"content": {"application/json": {"schema": {}}},
},
},
"summary": "A",
"operationId": "a_a__id__get",
"parameters": [
{
"required": True,
"schema": {"title": "Id"},
"name": "id",
"in": "path",
}
],
}
}
},
"components": {
"schemas": {
"ValidationError": {
"title": "ValidationError",
"required": ["loc", "msg", "type"],
"type": "object",
"properties": {
"loc": {
"title": "Location",
"type": "array",
"items": {"anyOf": [{"type": "string"}, {"type": "integer"}]},
},
"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"},
}
},
},
}
},
}
client = TestClient(app)
def test_openapi_schema():
response = client.get("/openapi.json")
assert response.status_code == 200, response.text
assert response.json() == openapi_schema
assert response.json() == {
"openapi": "3.0.2",
"info": {"title": "FastAPI", "version": "0.1.0"},
"paths": {
"/a/{id}": {
"get": {
"responses": {
"422": {
"description": "Validation Error",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/HTTPValidationError"
}
}
},
},
"200": {
"description": "Successful Response",
"content": {"application/json": {"schema": {}}},
},
},
"summary": "A",
"operationId": "a_a__id__get",
"parameters": [
{
"required": True,
"schema": {"title": "Id"},
"name": "id",
"in": "path",
}
],
}
}
},
"components": {
"schemas": {
"ValidationError": {
"title": "ValidationError",
"required": ["loc", "msg", "type"],
"type": "object",
"properties": {
"loc": {
"title": "Location",
"type": "array",
"items": {
"anyOf": [{"type": "string"}, {"type": "integer"}]
},
},
"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"},
}
},
},
}
},
}

View File

@@ -35,83 +35,82 @@ async def b():
pass # pragma: no cover
openapi_schema = {
"openapi": "3.0.2",
"info": {"title": "FastAPI", "version": "0.1.0"},
"paths": {
"/a": {
"get": {
"responses": {
"500": {
"description": "Error",
"content": {
"application/vnd.api+json": {
"schema": {"$ref": "#/components/schemas/JsonApiError"}
}
},
},
"200": {
"description": "Successful Response",
"content": {"application/vnd.api+json": {"schema": {}}},
},
},
"summary": "A",
"operationId": "a_a_get",
}
},
"/b": {
"get": {
"responses": {
"500": {
"description": "Error",
"content": {
"application/json": {
"schema": {"$ref": "#/components/schemas/Error"}
}
},
},
"200": {
"description": "Successful Response",
"content": {"application/json": {"schema": {}}},
},
},
"summary": "B",
"operationId": "b_b_get",
}
},
},
"components": {
"schemas": {
"Error": {
"title": "Error",
"required": ["status", "title"],
"type": "object",
"properties": {
"status": {"title": "Status", "type": "string"},
"title": {"title": "Title", "type": "string"},
},
},
"JsonApiError": {
"title": "JsonApiError",
"required": ["errors"],
"type": "object",
"properties": {
"errors": {
"title": "Errors",
"type": "array",
"items": {"$ref": "#/components/schemas/Error"},
}
},
},
}
},
}
client = TestClient(app)
def test_openapi_schema():
response = client.get("/openapi.json")
assert response.status_code == 200, response.text
assert response.json() == openapi_schema
assert response.json() == {
"openapi": "3.0.2",
"info": {"title": "FastAPI", "version": "0.1.0"},
"paths": {
"/a": {
"get": {
"responses": {
"500": {
"description": "Error",
"content": {
"application/vnd.api+json": {
"schema": {
"$ref": "#/components/schemas/JsonApiError"
}
}
},
},
"200": {
"description": "Successful Response",
"content": {"application/vnd.api+json": {"schema": {}}},
},
},
"summary": "A",
"operationId": "a_a_get",
}
},
"/b": {
"get": {
"responses": {
"500": {
"description": "Error",
"content": {
"application/json": {
"schema": {"$ref": "#/components/schemas/Error"}
}
},
},
"200": {
"description": "Successful Response",
"content": {"application/json": {"schema": {}}},
},
},
"summary": "B",
"operationId": "b_b_get",
}
},
},
"components": {
"schemas": {
"Error": {
"title": "Error",
"required": ["status", "title"],
"type": "object",
"properties": {
"status": {"title": "Status", "type": "string"},
"title": {"title": "Title", "type": "string"},
},
},
"JsonApiError": {
"title": "JsonApiError",
"required": ["errors"],
"type": "object",
"properties": {
"errors": {
"title": "Errors",
"type": "array",
"items": {"$ref": "#/components/schemas/Error"},
}
},
},
}
},
}

View File

@@ -53,103 +53,10 @@ async def d():
app.include_router(router)
openapi_schema = {
"openapi": "3.0.2",
"info": {"title": "FastAPI", "version": "0.1.0"},
"paths": {
"/a": {
"get": {
"responses": {
"501": {"description": "Error 1"},
"200": {
"description": "Successful Response",
"content": {"application/json": {"schema": {}}},
},
},
"summary": "A",
"operationId": "a_a_get",
}
},
"/b": {
"get": {
"responses": {
"502": {"description": "Error 2"},
"4XX": {"description": "Error with range, upper"},
"200": {
"description": "Successful Response",
"content": {"application/json": {"schema": {}}},
},
},
"summary": "B",
"operationId": "b_b_get",
}
},
"/c": {
"get": {
"responses": {
"400": {"description": "Error with str"},
"5XX": {"description": "Error with range, lower"},
"200": {
"description": "Successful Response",
"content": {"application/json": {"schema": {}}},
},
"default": {"description": "A default response"},
},
"summary": "C",
"operationId": "c_c_get",
}
},
"/d": {
"get": {
"responses": {
"400": {"description": "Error with str"},
"5XX": {
"description": "Server Error",
"content": {
"application/json": {
"schema": {"$ref": "#/components/schemas/ResponseModel"}
}
},
},
"200": {
"description": "Successful Response",
"content": {"application/json": {"schema": {}}},
},
"default": {
"description": "Default Response",
"content": {
"application/json": {
"schema": {"$ref": "#/components/schemas/ResponseModel"}
}
},
},
},
"summary": "D",
"operationId": "d_d_get",
}
},
},
"components": {
"schemas": {
"ResponseModel": {
"title": "ResponseModel",
"required": ["message"],
"type": "object",
"properties": {"message": {"title": "Message", "type": "string"}},
}
}
},
}
client = TestClient(app)
def test_openapi_schema():
response = client.get("/openapi.json")
assert response.status_code == 200, response.text
assert response.json() == openapi_schema
def test_a():
response = client.get("/a")
assert response.status_code == 200, response.text
@@ -172,3 +79,99 @@ def test_d():
response = client.get("/d")
assert response.status_code == 200, response.text
assert response.json() == "d"
def test_openapi_schema():
response = client.get("/openapi.json")
assert response.status_code == 200, response.text
assert response.json() == {
"openapi": "3.0.2",
"info": {"title": "FastAPI", "version": "0.1.0"},
"paths": {
"/a": {
"get": {
"responses": {
"501": {"description": "Error 1"},
"200": {
"description": "Successful Response",
"content": {"application/json": {"schema": {}}},
},
},
"summary": "A",
"operationId": "a_a_get",
}
},
"/b": {
"get": {
"responses": {
"502": {"description": "Error 2"},
"4XX": {"description": "Error with range, upper"},
"200": {
"description": "Successful Response",
"content": {"application/json": {"schema": {}}},
},
},
"summary": "B",
"operationId": "b_b_get",
}
},
"/c": {
"get": {
"responses": {
"400": {"description": "Error with str"},
"5XX": {"description": "Error with range, lower"},
"200": {
"description": "Successful Response",
"content": {"application/json": {"schema": {}}},
},
"default": {"description": "A default response"},
},
"summary": "C",
"operationId": "c_c_get",
}
},
"/d": {
"get": {
"responses": {
"400": {"description": "Error with str"},
"5XX": {
"description": "Server Error",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ResponseModel"
}
}
},
},
"200": {
"description": "Successful Response",
"content": {"application/json": {"schema": {}}},
},
"default": {
"description": "Default Response",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ResponseModel"
}
}
},
},
},
"summary": "D",
"operationId": "d_d_get",
}
},
},
"components": {
"schemas": {
"ResponseModel": {
"title": "ResponseModel",
"required": ["message"],
"type": "object",
"properties": {"message": {"title": "Message", "type": "string"}},
}
}
},
}

View File

@@ -28,161 +28,6 @@ async def unrelated(foo: Annotated[str, object()]):
client = TestClient(app)
openapi_schema = {
"openapi": "3.0.2",
"info": {"title": "FastAPI", "version": "0.1.0"},
"paths": {
"/default": {
"get": {
"summary": "Default",
"operationId": "default_default_get",
"parameters": [
{
"required": False,
"schema": {"title": "Foo", "type": "string", "default": "foo"},
"name": "foo",
"in": "query",
}
],
"responses": {
"200": {
"description": "Successful Response",
"content": {"application/json": {"schema": {}}},
},
"422": {
"description": "Validation Error",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/HTTPValidationError"
}
}
},
},
},
}
},
"/required": {
"get": {
"summary": "Required",
"operationId": "required_required_get",
"parameters": [
{
"required": True,
"schema": {"title": "Foo", "minLength": 1, "type": "string"},
"name": "foo",
"in": "query",
}
],
"responses": {
"200": {
"description": "Successful Response",
"content": {"application/json": {"schema": {}}},
},
"422": {
"description": "Validation Error",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/HTTPValidationError"
}
}
},
},
},
}
},
"/multiple": {
"get": {
"summary": "Multiple",
"operationId": "multiple_multiple_get",
"parameters": [
{
"required": True,
"schema": {"title": "Foo", "minLength": 1, "type": "string"},
"name": "foo",
"in": "query",
}
],
"responses": {
"200": {
"description": "Successful Response",
"content": {"application/json": {"schema": {}}},
},
"422": {
"description": "Validation Error",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/HTTPValidationError"
}
}
},
},
},
}
},
"/unrelated": {
"get": {
"summary": "Unrelated",
"operationId": "unrelated_unrelated_get",
"parameters": [
{
"required": True,
"schema": {"title": "Foo", "type": "string"},
"name": "foo",
"in": "query",
}
],
"responses": {
"200": {
"description": "Successful Response",
"content": {"application/json": {"schema": {}}},
},
"422": {
"description": "Validation Error",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/HTTPValidationError"
}
}
},
},
},
}
},
},
"components": {
"schemas": {
"HTTPValidationError": {
"title": "HTTPValidationError",
"type": "object",
"properties": {
"detail": {
"title": "Detail",
"type": "array",
"items": {"$ref": "#/components/schemas/ValidationError"},
}
},
},
"ValidationError": {
"title": "ValidationError",
"required": ["loc", "msg", "type"],
"type": "object",
"properties": {
"loc": {
"title": "Location",
"type": "array",
"items": {"anyOf": [{"type": "string"}, {"type": "integer"}]},
},
"msg": {"title": "Message", "type": "string"},
"type": {"title": "Error Type", "type": "string"},
},
},
}
},
}
foo_is_missing = {
"detail": [
{
@@ -217,7 +62,6 @@ foo_is_short = {
("/multiple?foo=", 422, foo_is_short),
("/unrelated?foo=bar", 200, {"foo": "bar"}),
("/unrelated", 422, foo_is_missing),
("/openapi.json", 200, openapi_schema),
],
)
def test_get(path, expected_status, expected_response):
@@ -227,11 +71,14 @@ def test_get(path, expected_status, expected_response):
def test_multiple_path():
app = FastAPI()
@app.get("/test1")
@app.get("/test2")
async def test(var: Annotated[str, Query()] = "bar"):
return {"foo": var}
client = TestClient(app)
response = client.get("/test1")
assert response.status_code == 200
assert response.json() == {"foo": "bar"}
@@ -265,3 +112,177 @@ def test_nested_router():
response = client.get("/nested/test")
assert response.status_code == 200
assert response.json() == {"foo": "bar"}
def test_openapi_schema():
response = client.get("/openapi.json")
assert response.status_code == 200
assert response.json() == {
"openapi": "3.0.2",
"info": {"title": "FastAPI", "version": "0.1.0"},
"paths": {
"/default": {
"get": {
"summary": "Default",
"operationId": "default_default_get",
"parameters": [
{
"required": False,
"schema": {
"title": "Foo",
"type": "string",
"default": "foo",
},
"name": "foo",
"in": "query",
}
],
"responses": {
"200": {
"description": "Successful Response",
"content": {"application/json": {"schema": {}}},
},
"422": {
"description": "Validation Error",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/HTTPValidationError"
}
}
},
},
},
}
},
"/required": {
"get": {
"summary": "Required",
"operationId": "required_required_get",
"parameters": [
{
"required": True,
"schema": {
"title": "Foo",
"minLength": 1,
"type": "string",
},
"name": "foo",
"in": "query",
}
],
"responses": {
"200": {
"description": "Successful Response",
"content": {"application/json": {"schema": {}}},
},
"422": {
"description": "Validation Error",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/HTTPValidationError"
}
}
},
},
},
}
},
"/multiple": {
"get": {
"summary": "Multiple",
"operationId": "multiple_multiple_get",
"parameters": [
{
"required": True,
"schema": {
"title": "Foo",
"minLength": 1,
"type": "string",
},
"name": "foo",
"in": "query",
}
],
"responses": {
"200": {
"description": "Successful Response",
"content": {"application/json": {"schema": {}}},
},
"422": {
"description": "Validation Error",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/HTTPValidationError"
}
}
},
},
},
}
},
"/unrelated": {
"get": {
"summary": "Unrelated",
"operationId": "unrelated_unrelated_get",
"parameters": [
{
"required": True,
"schema": {"title": "Foo", "type": "string"},
"name": "foo",
"in": "query",
}
],
"responses": {
"200": {
"description": "Successful Response",
"content": {"application/json": {"schema": {}}},
},
"422": {
"description": "Validation Error",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/HTTPValidationError"
}
}
},
},
},
}
},
},
"components": {
"schemas": {
"HTTPValidationError": {
"title": "HTTPValidationError",
"type": "object",
"properties": {
"detail": {
"title": "Detail",
"type": "array",
"items": {"$ref": "#/components/schemas/ValidationError"},
}
},
},
"ValidationError": {
"title": "ValidationError",
"required": ["loc", "msg", "type"],
"type": "object",
"properties": {
"loc": {
"title": "Location",
"type": "array",
"items": {
"anyOf": [{"type": "string"}, {"type": "integer"}]
},
},
"msg": {"title": "Message", "type": "string"},
"type": {"title": "Error Type", "type": "string"},
},
},
}
},
}

View File

File diff suppressed because it is too large Load Diff

View File

@@ -46,49 +46,6 @@ app.include_router(router=router_a, prefix="/a")
client = TestClient(app)
openapi_schema = {
"openapi": "3.0.2",
"info": {"title": "FastAPI", "version": "0.1.0"},
"paths": {
"/a/": {
"get": {
"responses": {
"200": {
"description": "Successful Response",
"content": {"application/json": {"schema": {}}},
}
},
"summary": "Get A",
"operationId": "get_a_a__get",
}
},
"/a/b/": {
"get": {
"responses": {
"200": {
"description": "Successful Response",
"content": {"application/json": {"schema": {}}},
}
},
"summary": "Get B",
"operationId": "get_b_a_b__get",
}
},
"/a/b/c/": {
"get": {
"responses": {
"200": {
"description": "Successful Response",
"content": {"application/json": {"schema": {}}},
}
},
"summary": "Get C",
"operationId": "get_c_a_b_c__get",
}
},
},
}
@pytest.mark.parametrize(
"path,expected_status,expected_response",
@@ -96,7 +53,6 @@ openapi_schema = {
("/a", 200, {"msg": "A"}),
("/a/b", 200, {"msg": "B"}),
("/a/b/c", 200, {"msg": "C"}),
("/openapi.json", 200, openapi_schema),
],
)
def test_get_path(path, expected_status, expected_response):
@@ -113,3 +69,50 @@ def test_route_classes():
assert getattr(routes["/a/"], "x_type") == "A" # noqa: B009
assert getattr(routes["/a/b/"], "x_type") == "B" # noqa: B009
assert getattr(routes["/a/b/c/"], "x_type") == "C" # noqa: B009
def test_openapi_schema():
response = client.get("/openapi.json")
assert response.status_code == 200, response.text
assert response.json() == {
"openapi": "3.0.2",
"info": {"title": "FastAPI", "version": "0.1.0"},
"paths": {
"/a/": {
"get": {
"responses": {
"200": {
"description": "Successful Response",
"content": {"application/json": {"schema": {}}},
}
},
"summary": "Get A",
"operationId": "get_a_a__get",
}
},
"/a/b/": {
"get": {
"responses": {
"200": {
"description": "Successful Response",
"content": {"application/json": {"schema": {}}},
}
},
"summary": "Get B",
"operationId": "get_b_a_b__get",
}
},
"/a/b/c/": {
"get": {
"responses": {
"200": {
"description": "Successful Response",
"content": {"application/json": {"schema": {}}},
}
},
"summary": "Get C",
"operationId": "get_c_a_b_c__get",
}
},
},
}

View File

@@ -44,156 +44,6 @@ async def no_duplicates_sub(
return [item, sub_items]
openapi_schema = {
"openapi": "3.0.2",
"info": {"title": "FastAPI", "version": "0.1.0"},
"paths": {
"/with-duplicates": {
"post": {
"summary": "With Duplicates",
"operationId": "with_duplicates_with_duplicates_post",
"requestBody": {
"content": {
"application/json": {
"schema": {"$ref": "#/components/schemas/Item"}
}
},
"required": True,
},
"responses": {
"200": {
"description": "Successful Response",
"content": {"application/json": {"schema": {}}},
},
"422": {
"description": "Validation Error",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/HTTPValidationError"
}
}
},
},
},
}
},
"/no-duplicates": {
"post": {
"summary": "No Duplicates",
"operationId": "no_duplicates_no_duplicates_post",
"requestBody": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Body_no_duplicates_no_duplicates_post"
}
}
},
"required": True,
},
"responses": {
"200": {
"description": "Successful Response",
"content": {"application/json": {"schema": {}}},
},
"422": {
"description": "Validation Error",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/HTTPValidationError"
}
}
},
},
},
}
},
"/with-duplicates-sub": {
"post": {
"summary": "No Duplicates Sub",
"operationId": "no_duplicates_sub_with_duplicates_sub_post",
"requestBody": {
"content": {
"application/json": {
"schema": {"$ref": "#/components/schemas/Item"}
}
},
"required": True,
},
"responses": {
"200": {
"description": "Successful Response",
"content": {"application/json": {"schema": {}}},
},
"422": {
"description": "Validation Error",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/HTTPValidationError"
}
}
},
},
},
}
},
},
"components": {
"schemas": {
"Body_no_duplicates_no_duplicates_post": {
"title": "Body_no_duplicates_no_duplicates_post",
"required": ["item", "item2"],
"type": "object",
"properties": {
"item": {"$ref": "#/components/schemas/Item"},
"item2": {"$ref": "#/components/schemas/Item"},
},
},
"HTTPValidationError": {
"title": "HTTPValidationError",
"type": "object",
"properties": {
"detail": {
"title": "Detail",
"type": "array",
"items": {"$ref": "#/components/schemas/ValidationError"},
}
},
},
"Item": {
"title": "Item",
"required": ["data"],
"type": "object",
"properties": {"data": {"title": "Data", "type": "string"}},
},
"ValidationError": {
"title": "ValidationError",
"required": ["loc", "msg", "type"],
"type": "object",
"properties": {
"loc": {
"title": "Location",
"type": "array",
"items": {"anyOf": [{"type": "string"}, {"type": "integer"}]},
},
"msg": {"title": "Message", "type": "string"},
"type": {"title": "Error Type", "type": "string"},
},
},
}
},
}
def test_openapi_schema():
response = client.get("/openapi.json")
assert response.status_code == 200, response.text
assert response.json() == openapi_schema
def test_no_duplicates_invalid():
response = client.post("/no-duplicates", json={"item": {"data": "myitem"}})
assert response.status_code == 422, response.text
@@ -230,3 +80,152 @@ def test_sub_duplicates():
{"data": "myitem"},
[{"data": "myitem"}, {"data": "myitem"}],
]
def test_openapi_schema():
response = client.get("/openapi.json")
assert response.status_code == 200, response.text
assert response.json() == {
"openapi": "3.0.2",
"info": {"title": "FastAPI", "version": "0.1.0"},
"paths": {
"/with-duplicates": {
"post": {
"summary": "With Duplicates",
"operationId": "with_duplicates_with_duplicates_post",
"requestBody": {
"content": {
"application/json": {
"schema": {"$ref": "#/components/schemas/Item"}
}
},
"required": True,
},
"responses": {
"200": {
"description": "Successful Response",
"content": {"application/json": {"schema": {}}},
},
"422": {
"description": "Validation Error",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/HTTPValidationError"
}
}
},
},
},
}
},
"/no-duplicates": {
"post": {
"summary": "No Duplicates",
"operationId": "no_duplicates_no_duplicates_post",
"requestBody": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Body_no_duplicates_no_duplicates_post"
}
}
},
"required": True,
},
"responses": {
"200": {
"description": "Successful Response",
"content": {"application/json": {"schema": {}}},
},
"422": {
"description": "Validation Error",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/HTTPValidationError"
}
}
},
},
},
}
},
"/with-duplicates-sub": {
"post": {
"summary": "No Duplicates Sub",
"operationId": "no_duplicates_sub_with_duplicates_sub_post",
"requestBody": {
"content": {
"application/json": {
"schema": {"$ref": "#/components/schemas/Item"}
}
},
"required": True,
},
"responses": {
"200": {
"description": "Successful Response",
"content": {"application/json": {"schema": {}}},
},
"422": {
"description": "Validation Error",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/HTTPValidationError"
}
}
},
},
},
}
},
},
"components": {
"schemas": {
"Body_no_duplicates_no_duplicates_post": {
"title": "Body_no_duplicates_no_duplicates_post",
"required": ["item", "item2"],
"type": "object",
"properties": {
"item": {"$ref": "#/components/schemas/Item"},
"item2": {"$ref": "#/components/schemas/Item"},
},
},
"HTTPValidationError": {
"title": "HTTPValidationError",
"type": "object",
"properties": {
"detail": {
"title": "Detail",
"type": "array",
"items": {"$ref": "#/components/schemas/ValidationError"},
}
},
},
"Item": {
"title": "Item",
"required": ["data"],
"type": "object",
"properties": {"data": {"title": "Data", "type": "string"}},
},
"ValidationError": {
"title": "ValidationError",
"required": ["loc", "msg", "type"],
"type": "object",
"properties": {
"loc": {
"title": "Location",
"type": "array",
"items": {
"anyOf": [{"type": "string"}, {"type": "integer"}]
},
},
"msg": {"title": "Message", "type": "string"},
"type": {"title": "Error Type", "type": "string"},
},
},
}
},
}

View File

@@ -11,34 +11,32 @@ def read_main(request: Request):
client = TestClient(app)
openapi_schema = {
"openapi": "3.0.2",
"info": {"title": "FastAPI", "version": "0.1.0"},
"paths": {
"/app": {
"get": {
"summary": "Read Main",
"operationId": "read_main_app_get",
"responses": {
"200": {
"description": "Successful Response",
"content": {"application/json": {"schema": {}}},
}
},
}
}
},
"servers": [{"url": "/api/v1"}],
}
def test_openapi():
response = client.get("/openapi.json")
assert response.status_code == 200
assert response.json() == openapi_schema
def test_main():
response = client.get("/app")
assert response.status_code == 200
assert response.json() == {"message": "Hello World", "root_path": "/api/v1"}
def test_openapi():
response = client.get("/openapi.json")
assert response.status_code == 200
assert response.json() == {
"openapi": "3.0.2",
"info": {"title": "FastAPI", "version": "0.1.0"},
"paths": {
"/app": {
"get": {
"summary": "Read Main",
"operationId": "read_main_app_get",
"responses": {
"200": {
"description": "Successful Response",
"content": {"application/json": {"schema": {}}},
}
},
}
}
},
"servers": [{"url": "/api/v1"}],
}

View File

@@ -23,60 +23,57 @@ def f():
return {"c": {}, "d": {"a": {}}}
openapi_schema = {
"openapi": "3.0.2",
"info": {"title": "FastAPI", "version": "0.1.0"},
"paths": {
"/": {
"get": {
"summary": "F",
"operationId": "f__get",
"responses": {
"200": {
"description": "Successful Response",
"content": {
"application/json": {
"schema": {"$ref": "#/components/schemas/Model3"}
}
},
}
},
}
}
},
"components": {
"schemas": {
"Model": {"title": "Model", "type": "object", "properties": {}},
"Model2": {
"title": "Model2",
"required": ["a"],
"type": "object",
"properties": {"a": {"$ref": "#/components/schemas/Model"}},
},
"Model3": {
"title": "Model3",
"required": ["c", "d"],
"type": "object",
"properties": {
"c": {"$ref": "#/components/schemas/Model"},
"d": {"$ref": "#/components/schemas/Model2"},
},
},
}
},
}
client = TestClient(app)
def test_openapi_schema():
response = client.get("/openapi.json")
assert response.status_code == 200, response.text
assert response.json() == openapi_schema
def test_get_api_route():
response = client.get("/")
assert response.status_code == 200, response.text
assert response.json() == {"c": {}, "d": {"a": {}}}
def test_openapi_schema():
response = client.get("/openapi.json")
assert response.status_code == 200, response.text
assert response.json() == {
"openapi": "3.0.2",
"info": {"title": "FastAPI", "version": "0.1.0"},
"paths": {
"/": {
"get": {
"summary": "F",
"operationId": "f__get",
"responses": {
"200": {
"description": "Successful Response",
"content": {
"application/json": {
"schema": {"$ref": "#/components/schemas/Model3"}
}
},
}
},
}
}
},
"components": {
"schemas": {
"Model": {"title": "Model", "type": "object", "properties": {}},
"Model2": {
"title": "Model2",
"required": ["a"],
"type": "object",
"properties": {"a": {"$ref": "#/components/schemas/Model"}},
},
"Model3": {
"title": "Model3",
"required": ["c", "d"],
"type": "object",
"properties": {
"c": {"$ref": "#/components/schemas/Model"},
"d": {"$ref": "#/components/schemas/Model2"},
},
},
}
},
}

View File

@@ -52,273 +52,6 @@ def trace_item(item_id: str):
client = TestClient(app)
openapi_schema = {
"openapi": "3.0.2",
"info": {"title": "FastAPI", "version": "0.1.0"},
"paths": {
"/items/{item_id}": {
"get": {
"responses": {
"200": {
"description": "Successful Response",
"content": {"application/json": {"schema": {}}},
},
"422": {
"description": "Validation Error",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/HTTPValidationError"
}
}
},
},
},
"summary": "Get Items",
"operationId": "get_items_items__item_id__get",
"parameters": [
{
"required": True,
"schema": {"title": "Item Id", "type": "string"},
"name": "item_id",
"in": "path",
}
],
},
"delete": {
"responses": {
"200": {
"description": "Successful Response",
"content": {"application/json": {"schema": {}}},
},
"422": {
"description": "Validation Error",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/HTTPValidationError"
}
}
},
},
},
"summary": "Delete Item",
"operationId": "delete_item_items__item_id__delete",
"parameters": [
{
"required": True,
"schema": {"title": "Item Id", "type": "string"},
"name": "item_id",
"in": "path",
}
],
"requestBody": {
"content": {
"application/json": {
"schema": {"$ref": "#/components/schemas/Item"}
}
},
"required": True,
},
},
"options": {
"responses": {
"200": {
"description": "Successful Response",
"content": {"application/json": {"schema": {}}},
},
"422": {
"description": "Validation Error",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/HTTPValidationError"
}
}
},
},
},
"summary": "Options Item",
"operationId": "options_item_items__item_id__options",
"parameters": [
{
"required": True,
"schema": {"title": "Item Id", "type": "string"},
"name": "item_id",
"in": "path",
}
],
},
"head": {
"responses": {
"200": {
"description": "Successful Response",
"content": {"application/json": {"schema": {}}},
},
"422": {
"description": "Validation Error",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/HTTPValidationError"
}
}
},
},
},
"summary": "Head Item",
"operationId": "head_item_items__item_id__head",
"parameters": [
{
"required": True,
"schema": {"title": "Item Id", "type": "string"},
"name": "item_id",
"in": "path",
}
],
},
"patch": {
"responses": {
"200": {
"description": "Successful Response",
"content": {"application/json": {"schema": {}}},
},
"422": {
"description": "Validation Error",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/HTTPValidationError"
}
}
},
},
},
"summary": "Patch Item",
"operationId": "patch_item_items__item_id__patch",
"parameters": [
{
"required": True,
"schema": {"title": "Item Id", "type": "string"},
"name": "item_id",
"in": "path",
}
],
"requestBody": {
"content": {
"application/json": {
"schema": {"$ref": "#/components/schemas/Item"}
}
},
"required": True,
},
},
"trace": {
"responses": {
"200": {
"description": "Successful Response",
"content": {"application/json": {"schema": {}}},
},
"422": {
"description": "Validation Error",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/HTTPValidationError"
}
}
},
},
},
"summary": "Trace Item",
"operationId": "trace_item_items__item_id__trace",
"parameters": [
{
"required": True,
"schema": {"title": "Item Id", "type": "string"},
"name": "item_id",
"in": "path",
}
],
},
},
"/items-not-decorated/{item_id}": {
"get": {
"responses": {
"200": {
"description": "Successful Response",
"content": {"application/json": {"schema": {}}},
},
"422": {
"description": "Validation Error",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/HTTPValidationError"
}
}
},
},
},
"summary": "Get Not Decorated",
"operationId": "get_not_decorated_items_not_decorated__item_id__get",
"parameters": [
{
"required": True,
"schema": {"title": "Item Id", "type": "string"},
"name": "item_id",
"in": "path",
}
],
}
},
},
"components": {
"schemas": {
"Item": {
"title": "Item",
"required": ["name"],
"type": "object",
"properties": {
"name": {"title": "Name", "type": "string"},
"price": {"title": "Price", "type": "number"},
},
},
"ValidationError": {
"title": "ValidationError",
"required": ["loc", "msg", "type"],
"type": "object",
"properties": {
"loc": {
"title": "Location",
"type": "array",
"items": {"anyOf": [{"type": "string"}, {"type": "integer"}]},
},
"msg": {"title": "Message", "type": "string"},
"type": {"title": "Error Type", "type": "string"},
},
},
"HTTPValidationError": {
"title": "HTTPValidationError",
"type": "object",
"properties": {
"detail": {
"title": "Detail",
"type": "array",
"items": {"$ref": "#/components/schemas/ValidationError"},
}
},
},
}
},
}
def test_openapi_schema():
response = client.get("/openapi.json")
assert response.status_code == 200, response.text
assert response.json() == openapi_schema
def test_get_api_route():
response = client.get("/items/foo")
@@ -360,3 +93,270 @@ def test_trace():
response = client.request("trace", "/items/foo")
assert response.status_code == 200, response.text
assert response.headers["content-type"] == "message/http"
def test_openapi_schema():
response = client.get("/openapi.json")
assert response.status_code == 200, response.text
assert response.json() == {
"openapi": "3.0.2",
"info": {"title": "FastAPI", "version": "0.1.0"},
"paths": {
"/items/{item_id}": {
"get": {
"responses": {
"200": {
"description": "Successful Response",
"content": {"application/json": {"schema": {}}},
},
"422": {
"description": "Validation Error",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/HTTPValidationError"
}
}
},
},
},
"summary": "Get Items",
"operationId": "get_items_items__item_id__get",
"parameters": [
{
"required": True,
"schema": {"title": "Item Id", "type": "string"},
"name": "item_id",
"in": "path",
}
],
},
"delete": {
"responses": {
"200": {
"description": "Successful Response",
"content": {"application/json": {"schema": {}}},
},
"422": {
"description": "Validation Error",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/HTTPValidationError"
}
}
},
},
},
"summary": "Delete Item",
"operationId": "delete_item_items__item_id__delete",
"parameters": [
{
"required": True,
"schema": {"title": "Item Id", "type": "string"},
"name": "item_id",
"in": "path",
}
],
"requestBody": {
"content": {
"application/json": {
"schema": {"$ref": "#/components/schemas/Item"}
}
},
"required": True,
},
},
"options": {
"responses": {
"200": {
"description": "Successful Response",
"content": {"application/json": {"schema": {}}},
},
"422": {
"description": "Validation Error",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/HTTPValidationError"
}
}
},
},
},
"summary": "Options Item",
"operationId": "options_item_items__item_id__options",
"parameters": [
{
"required": True,
"schema": {"title": "Item Id", "type": "string"},
"name": "item_id",
"in": "path",
}
],
},
"head": {
"responses": {
"200": {
"description": "Successful Response",
"content": {"application/json": {"schema": {}}},
},
"422": {
"description": "Validation Error",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/HTTPValidationError"
}
}
},
},
},
"summary": "Head Item",
"operationId": "head_item_items__item_id__head",
"parameters": [
{
"required": True,
"schema": {"title": "Item Id", "type": "string"},
"name": "item_id",
"in": "path",
}
],
},
"patch": {
"responses": {
"200": {
"description": "Successful Response",
"content": {"application/json": {"schema": {}}},
},
"422": {
"description": "Validation Error",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/HTTPValidationError"
}
}
},
},
},
"summary": "Patch Item",
"operationId": "patch_item_items__item_id__patch",
"parameters": [
{
"required": True,
"schema": {"title": "Item Id", "type": "string"},
"name": "item_id",
"in": "path",
}
],
"requestBody": {
"content": {
"application/json": {
"schema": {"$ref": "#/components/schemas/Item"}
}
},
"required": True,
},
},
"trace": {
"responses": {
"200": {
"description": "Successful Response",
"content": {"application/json": {"schema": {}}},
},
"422": {
"description": "Validation Error",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/HTTPValidationError"
}
}
},
},
},
"summary": "Trace Item",
"operationId": "trace_item_items__item_id__trace",
"parameters": [
{
"required": True,
"schema": {"title": "Item Id", "type": "string"},
"name": "item_id",
"in": "path",
}
],
},
},
"/items-not-decorated/{item_id}": {
"get": {
"responses": {
"200": {
"description": "Successful Response",
"content": {"application/json": {"schema": {}}},
},
"422": {
"description": "Validation Error",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/HTTPValidationError"
}
}
},
},
},
"summary": "Get Not Decorated",
"operationId": "get_not_decorated_items_not_decorated__item_id__get",
"parameters": [
{
"required": True,
"schema": {"title": "Item Id", "type": "string"},
"name": "item_id",
"in": "path",
}
],
}
},
},
"components": {
"schemas": {
"Item": {
"title": "Item",
"required": ["name"],
"type": "object",
"properties": {
"name": {"title": "Name", "type": "string"},
"price": {"title": "Price", "type": "number"},
},
},
"ValidationError": {
"title": "ValidationError",
"required": ["loc", "msg", "type"],
"type": "object",
"properties": {
"loc": {
"title": "Location",
"type": "array",
"items": {
"anyOf": [{"type": "string"}, {"type": "integer"}]
},
},
"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"},
}
},
},
}
},
}

View File

@@ -40,99 +40,6 @@ async def get_model_a(name: str, model_c=Depends(get_model_c)):
client = TestClient(app)
openapi_schema = {
"openapi": "3.0.2",
"info": {"title": "FastAPI", "version": "0.1.0"},
"paths": {
"/model/{name}": {
"get": {
"summary": "Get Model A",
"operationId": "get_model_a_model__name__get",
"parameters": [
{
"required": True,
"schema": {"title": "Name", "type": "string"},
"name": "name",
"in": "path",
}
],
"responses": {
"200": {
"description": "Successful Response",
"content": {
"application/json": {
"schema": {"$ref": "#/components/schemas/ModelA"}
}
},
},
"422": {
"description": "Validation Error",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/HTTPValidationError"
}
}
},
},
},
}
}
},
"components": {
"schemas": {
"HTTPValidationError": {
"title": "HTTPValidationError",
"type": "object",
"properties": {
"detail": {
"title": "Detail",
"type": "array",
"items": {"$ref": "#/components/schemas/ValidationError"},
}
},
},
"ModelA": {
"title": "ModelA",
"required": ["name", "model_b"],
"type": "object",
"properties": {
"name": {"title": "Name", "type": "string"},
"description": {"title": "Description", "type": "string"},
"model_b": {"$ref": "#/components/schemas/ModelB"},
},
},
"ModelB": {
"title": "ModelB",
"required": ["username"],
"type": "object",
"properties": {"username": {"title": "Username", "type": "string"}},
},
"ValidationError": {
"title": "ValidationError",
"required": ["loc", "msg", "type"],
"type": "object",
"properties": {
"loc": {
"title": "Location",
"type": "array",
"items": {"anyOf": [{"type": "string"}, {"type": "integer"}]},
},
"msg": {"title": "Message", "type": "string"},
"type": {"title": "Error Type", "type": "string"},
},
},
}
},
}
def test_openapi_schema():
response = client.get("/openapi.json")
assert response.status_code == 200, response.text
assert response.json() == openapi_schema
def test_filter_sub_model():
response = client.get("/model/modelA")
assert response.status_code == 200, response.text
@@ -153,3 +60,95 @@ def test_validator_is_cloned():
"type": "value_error",
}
]
def test_openapi_schema():
response = client.get("/openapi.json")
assert response.status_code == 200, response.text
assert response.json() == {
"openapi": "3.0.2",
"info": {"title": "FastAPI", "version": "0.1.0"},
"paths": {
"/model/{name}": {
"get": {
"summary": "Get Model A",
"operationId": "get_model_a_model__name__get",
"parameters": [
{
"required": True,
"schema": {"title": "Name", "type": "string"},
"name": "name",
"in": "path",
}
],
"responses": {
"200": {
"description": "Successful Response",
"content": {
"application/json": {
"schema": {"$ref": "#/components/schemas/ModelA"}
}
},
},
"422": {
"description": "Validation Error",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/HTTPValidationError"
}
}
},
},
},
}
}
},
"components": {
"schemas": {
"HTTPValidationError": {
"title": "HTTPValidationError",
"type": "object",
"properties": {
"detail": {
"title": "Detail",
"type": "array",
"items": {"$ref": "#/components/schemas/ValidationError"},
}
},
},
"ModelA": {
"title": "ModelA",
"required": ["name", "model_b"],
"type": "object",
"properties": {
"name": {"title": "Name", "type": "string"},
"description": {"title": "Description", "type": "string"},
"model_b": {"$ref": "#/components/schemas/ModelB"},
},
},
"ModelB": {
"title": "ModelB",
"required": ["username"],
"type": "object",
"properties": {"username": {"title": "Username", "type": "string"}},
},
"ValidationError": {
"title": "ValidationError",
"required": ["loc", "msg", "type"],
"type": "object",
"properties": {
"loc": {
"title": "Location",
"type": "array",
"items": {
"anyOf": [{"type": "string"}, {"type": "integer"}]
},
},
"msg": {"title": "Message", "type": "string"},
"type": {"title": "Error Type", "type": "string"},
},
},
}
},
}

View File

@@ -19,90 +19,89 @@ async def create_item(product: Product):
client = TestClient(app)
openapi_schema = {
"openapi": "3.0.2",
"info": {"title": "FastAPI", "version": "0.1.0"},
"paths": {
"/product": {
"get": {
"summary": "Create Item",
"operationId": "create_item_product_get",
"requestBody": {
"content": {
"application/json": {
"schema": {"$ref": "#/components/schemas/Product"}
}
},
"required": True,
},
"responses": {
"200": {
"description": "Successful Response",
"content": {"application/json": {"schema": {}}},
},
"422": {
"description": "Validation Error",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/HTTPValidationError"
}
}
},
},
},
}
}
},
"components": {
"schemas": {
"HTTPValidationError": {
"title": "HTTPValidationError",
"type": "object",
"properties": {
"detail": {
"title": "Detail",
"type": "array",
"items": {"$ref": "#/components/schemas/ValidationError"},
}
},
},
"Product": {
"title": "Product",
"required": ["name", "price"],
"type": "object",
"properties": {
"name": {"title": "Name", "type": "string"},
"description": {"title": "Description", "type": "string"},
"price": {"title": "Price", "type": "number"},
},
},
"ValidationError": {
"title": "ValidationError",
"required": ["loc", "msg", "type"],
"type": "object",
"properties": {
"loc": {
"title": "Location",
"type": "array",
"items": {"anyOf": [{"type": "string"}, {"type": "integer"}]},
},
"msg": {"title": "Message", "type": "string"},
"type": {"title": "Error Type", "type": "string"},
},
},
}
},
}
def test_get_with_body():
body = {"name": "Foo", "description": "Some description", "price": 5.5}
response = client.request("GET", "/product", json=body)
assert response.json() == body
def test_openapi_schema():
response = client.get("/openapi.json")
assert response.status_code == 200, response.text
assert response.json() == openapi_schema
def test_get_with_body():
body = {"name": "Foo", "description": "Some description", "price": 5.5}
response = client.request("GET", "/product", json=body)
assert response.json() == body
assert response.json() == {
"openapi": "3.0.2",
"info": {"title": "FastAPI", "version": "0.1.0"},
"paths": {
"/product": {
"get": {
"summary": "Create Item",
"operationId": "create_item_product_get",
"requestBody": {
"content": {
"application/json": {
"schema": {"$ref": "#/components/schemas/Product"}
}
},
"required": True,
},
"responses": {
"200": {
"description": "Successful Response",
"content": {"application/json": {"schema": {}}},
},
"422": {
"description": "Validation Error",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/HTTPValidationError"
}
}
},
},
},
}
}
},
"components": {
"schemas": {
"HTTPValidationError": {
"title": "HTTPValidationError",
"type": "object",
"properties": {
"detail": {
"title": "Detail",
"type": "array",
"items": {"$ref": "#/components/schemas/ValidationError"},
}
},
},
"Product": {
"title": "Product",
"required": ["name", "price"],
"type": "object",
"properties": {
"name": {"title": "Name", "type": "string"},
"description": {"title": "Description", "type": "string"},
"price": {"title": "Price", "type": "number"},
},
},
"ValidationError": {
"title": "ValidationError",
"required": ["loc", "msg", "type"],
"type": "object",
"properties": {
"loc": {
"title": "Location",
"type": "array",
"items": {
"anyOf": [{"type": "string"}, {"type": "integer"}]
},
},
"msg": {"title": "Message", "type": "string"},
"type": {"title": "Error Type", "type": "string"},
},
},
}
},
}

View File

File diff suppressed because it is too large Load Diff

View File

@@ -4,130 +4,6 @@ from .app.main import app
client = TestClient(app)
openapi_schema = {
"openapi": "3.0.2",
"info": {"title": "FastAPI", "version": "0.1.0"},
"paths": {
"/a/compute": {
"post": {
"responses": {
"200": {
"description": "Successful Response",
"content": {"application/json": {"schema": {}}},
},
"422": {
"description": "Validation Error",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/HTTPValidationError"
}
}
},
},
},
"summary": "Compute",
"operationId": "compute_a_compute_post",
"requestBody": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Body_compute_a_compute_post"
}
}
},
"required": True,
},
}
},
"/b/compute/": {
"post": {
"responses": {
"200": {
"description": "Successful Response",
"content": {"application/json": {"schema": {}}},
},
"422": {
"description": "Validation Error",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/HTTPValidationError"
}
}
},
},
},
"summary": "Compute",
"operationId": "compute_b_compute__post",
"requestBody": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Body_compute_b_compute__post"
}
}
},
"required": True,
},
}
},
},
"components": {
"schemas": {
"Body_compute_b_compute__post": {
"title": "Body_compute_b_compute__post",
"required": ["a", "b"],
"type": "object",
"properties": {
"a": {"title": "A", "type": "integer"},
"b": {"title": "B", "type": "string"},
},
},
"Body_compute_a_compute_post": {
"title": "Body_compute_a_compute_post",
"required": ["a", "b"],
"type": "object",
"properties": {
"a": {"title": "A", "type": "integer"},
"b": {"title": "B", "type": "string"},
},
},
"ValidationError": {
"title": "ValidationError",
"required": ["loc", "msg", "type"],
"type": "object",
"properties": {
"loc": {
"title": "Location",
"type": "array",
"items": {"anyOf": [{"type": "string"}, {"type": "integer"}]},
},
"msg": {"title": "Message", "type": "string"},
"type": {"title": "Error Type", "type": "string"},
},
},
"HTTPValidationError": {
"title": "HTTPValidationError",
"type": "object",
"properties": {
"detail": {
"title": "Detail",
"type": "array",
"items": {"$ref": "#/components/schemas/ValidationError"},
}
},
},
}
},
}
def test_openapi_schema():
response = client.get("/openapi.json")
assert response.status_code == 200, response.text
assert response.json() == openapi_schema
def test_post_a():
data = {"a": 2, "b": "foo"}
@@ -153,3 +29,127 @@ def test_post_b_invalid():
data = {"a": "bar", "b": "foo"}
response = client.post("/b/compute/", json=data)
assert response.status_code == 422, response.text
def test_openapi_schema():
response = client.get("/openapi.json")
assert response.status_code == 200, response.text
assert response.json() == {
"openapi": "3.0.2",
"info": {"title": "FastAPI", "version": "0.1.0"},
"paths": {
"/a/compute": {
"post": {
"responses": {
"200": {
"description": "Successful Response",
"content": {"application/json": {"schema": {}}},
},
"422": {
"description": "Validation Error",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/HTTPValidationError"
}
}
},
},
},
"summary": "Compute",
"operationId": "compute_a_compute_post",
"requestBody": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Body_compute_a_compute_post"
}
}
},
"required": True,
},
}
},
"/b/compute/": {
"post": {
"responses": {
"200": {
"description": "Successful Response",
"content": {"application/json": {"schema": {}}},
},
"422": {
"description": "Validation Error",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/HTTPValidationError"
}
}
},
},
},
"summary": "Compute",
"operationId": "compute_b_compute__post",
"requestBody": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Body_compute_b_compute__post"
}
}
},
"required": True,
},
}
},
},
"components": {
"schemas": {
"Body_compute_b_compute__post": {
"title": "Body_compute_b_compute__post",
"required": ["a", "b"],
"type": "object",
"properties": {
"a": {"title": "A", "type": "integer"},
"b": {"title": "B", "type": "string"},
},
},
"Body_compute_a_compute_post": {
"title": "Body_compute_a_compute_post",
"required": ["a", "b"],
"type": "object",
"properties": {
"a": {"title": "A", "type": "integer"},
"b": {"title": "B", "type": "string"},
},
},
"ValidationError": {
"title": "ValidationError",
"required": ["loc", "msg", "type"],
"type": "object",
"properties": {
"loc": {
"title": "Location",
"type": "array",
"items": {
"anyOf": [{"type": "string"}, {"type": "integer"}]
},
},
"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"},
}
},
},
}
},
}

View File

@@ -21,85 +21,6 @@ def save_item_no_body(item: List[Item]):
client = TestClient(app)
openapi_schema = {
"openapi": "3.0.2",
"info": {"title": "FastAPI", "version": "0.1.0"},
"paths": {
"/items/": {
"post": {
"responses": {
"200": {
"description": "Successful Response",
"content": {"application/json": {"schema": {}}},
},
"422": {
"description": "Validation Error",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/HTTPValidationError"
}
}
},
},
},
"summary": "Save Item No Body",
"operationId": "save_item_no_body_items__post",
"requestBody": {
"content": {
"application/json": {
"schema": {
"title": "Item",
"type": "array",
"items": {"$ref": "#/components/schemas/Item"},
}
}
},
"required": True,
},
}
}
},
"components": {
"schemas": {
"Item": {
"title": "Item",
"required": ["name", "age"],
"type": "object",
"properties": {
"name": {"title": "Name", "type": "string"},
"age": {"title": "Age", "exclusiveMinimum": 0.0, "type": "number"},
},
},
"ValidationError": {
"title": "ValidationError",
"required": ["loc", "msg", "type"],
"type": "object",
"properties": {
"loc": {
"title": "Location",
"type": "array",
"items": {"anyOf": [{"type": "string"}, {"type": "integer"}]},
},
"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"},
}
},
},
}
},
}
single_error = {
"detail": [
{
@@ -137,12 +58,6 @@ multiple_errors = {
}
def test_openapi_schema():
response = client.get("/openapi.json")
assert response.status_code == 200, response.text
assert response.json() == openapi_schema
def test_put_correct_body():
response = client.post("/items/", json=[{"name": "Foo", "age": 5}])
assert response.status_code == 200, response.text
@@ -159,3 +74,92 @@ def test_put_incorrect_body_multiple():
response = client.post("/items/", json=[{"age": "five"}, {"age": "six"}])
assert response.status_code == 422, response.text
assert response.json() == multiple_errors
def test_openapi_schema():
response = client.get("/openapi.json")
assert response.status_code == 200, response.text
assert response.json() == {
"openapi": "3.0.2",
"info": {"title": "FastAPI", "version": "0.1.0"},
"paths": {
"/items/": {
"post": {
"responses": {
"200": {
"description": "Successful Response",
"content": {"application/json": {"schema": {}}},
},
"422": {
"description": "Validation Error",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/HTTPValidationError"
}
}
},
},
},
"summary": "Save Item No Body",
"operationId": "save_item_no_body_items__post",
"requestBody": {
"content": {
"application/json": {
"schema": {
"title": "Item",
"type": "array",
"items": {"$ref": "#/components/schemas/Item"},
}
}
},
"required": True,
},
}
}
},
"components": {
"schemas": {
"Item": {
"title": "Item",
"required": ["name", "age"],
"type": "object",
"properties": {
"name": {"title": "Name", "type": "string"},
"age": {
"title": "Age",
"exclusiveMinimum": 0.0,
"type": "number",
},
},
},
"ValidationError": {
"title": "ValidationError",
"required": ["loc", "msg", "type"],
"type": "object",
"properties": {
"loc": {
"title": "Location",
"type": "array",
"items": {
"anyOf": [{"type": "string"}, {"type": "integer"}]
},
},
"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"},
}
},
},
}
},
}

View File

@@ -14,76 +14,6 @@ def read_items(q: List[int] = Query(default=None)):
client = TestClient(app)
openapi_schema = {
"openapi": "3.0.2",
"info": {"title": "FastAPI", "version": "0.1.0"},
"paths": {
"/items/": {
"get": {
"responses": {
"200": {
"description": "Successful Response",
"content": {"application/json": {"schema": {}}},
},
"422": {
"description": "Validation Error",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/HTTPValidationError"
}
}
},
},
},
"summary": "Read Items",
"operationId": "read_items_items__get",
"parameters": [
{
"required": False,
"schema": {
"title": "Q",
"type": "array",
"items": {"type": "integer"},
},
"name": "q",
"in": "query",
}
],
}
}
},
"components": {
"schemas": {
"ValidationError": {
"title": "ValidationError",
"required": ["loc", "msg", "type"],
"type": "object",
"properties": {
"loc": {
"title": "Location",
"type": "array",
"items": {"anyOf": [{"type": "string"}, {"type": "integer"}]},
},
"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"},
}
},
},
}
},
}
multiple_errors = {
"detail": [
{
@@ -100,12 +30,6 @@ multiple_errors = {
}
def test_openapi_schema():
response = client.get("/openapi.json")
assert response.status_code == 200, response.text
assert response.json() == openapi_schema
def test_multi_query():
response = client.get("/items/?q=5&q=6")
assert response.status_code == 200, response.text
@@ -116,3 +40,79 @@ def test_multi_query_incorrect():
response = client.get("/items/?q=five&q=six")
assert response.status_code == 422, response.text
assert response.json() == multiple_errors
def test_openapi_schema():
response = client.get("/openapi.json")
assert response.status_code == 200, response.text
assert response.json() == {
"openapi": "3.0.2",
"info": {"title": "FastAPI", "version": "0.1.0"},
"paths": {
"/items/": {
"get": {
"responses": {
"200": {
"description": "Successful Response",
"content": {"application/json": {"schema": {}}},
},
"422": {
"description": "Validation Error",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/HTTPValidationError"
}
}
},
},
},
"summary": "Read Items",
"operationId": "read_items_items__get",
"parameters": [
{
"required": False,
"schema": {
"title": "Q",
"type": "array",
"items": {"type": "integer"},
},
"name": "q",
"in": "query",
}
],
}
}
},
"components": {
"schemas": {
"ValidationError": {
"title": "ValidationError",
"required": ["loc", "msg", "type"],
"type": "object",
"properties": {
"loc": {
"title": "Location",
"type": "array",
"items": {
"anyOf": [{"type": "string"}, {"type": "integer"}]
},
},
"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"},
}
},
},
}
},
}

View File

@@ -32,96 +32,95 @@ def route_with_extra_query_parameters(standard_query_param: Optional[int] = 50):
client = TestClient(app)
openapi_schema = {
"openapi": "3.0.2",
"info": {"title": "FastAPI", "version": "0.1.0"},
"paths": {
"/": {
"get": {
"summary": "Route With Extra Query Parameters",
"operationId": "route_with_extra_query_parameters__get",
"parameters": [
{
"required": False,
"schema": {
"title": "Standard Query Param",
"type": "integer",
"default": 50,
},
"name": "standard_query_param",
"in": "query",
},
{
"required": False,
"schema": {"title": "Extra Param 1"},
"name": "extra_param_1",
"in": "query",
},
{
"required": True,
"schema": {"title": "Extra Param 2"},
"name": "extra_param_2",
"in": "query",
},
],
"responses": {
"200": {
"description": "Successful Response",
"content": {"application/json": {"schema": {}}},
},
"422": {
"description": "Validation Error",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/HTTPValidationError"
}
}
},
},
},
}
}
},
"components": {
"schemas": {
"HTTPValidationError": {
"title": "HTTPValidationError",
"type": "object",
"properties": {
"detail": {
"title": "Detail",
"type": "array",
"items": {"$ref": "#/components/schemas/ValidationError"},
}
},
},
"ValidationError": {
"title": "ValidationError",
"required": ["loc", "msg", "type"],
"type": "object",
"properties": {
"loc": {
"title": "Location",
"type": "array",
"items": {"anyOf": [{"type": "string"}, {"type": "integer"}]},
},
"msg": {"title": "Message", "type": "string"},
"type": {"title": "Error Type", "type": "string"},
},
},
}
},
}
def test_get_route():
response = client.get("/")
assert response.status_code == 200, response.text
assert response.json() == {}
def test_openapi():
response = client.get("/openapi.json")
assert response.status_code == 200, response.text
assert response.json() == openapi_schema
def test_get_route():
response = client.get("/")
assert response.status_code == 200, response.text
assert response.json() == {}
assert response.json() == {
"openapi": "3.0.2",
"info": {"title": "FastAPI", "version": "0.1.0"},
"paths": {
"/": {
"get": {
"summary": "Route With Extra Query Parameters",
"operationId": "route_with_extra_query_parameters__get",
"parameters": [
{
"required": False,
"schema": {
"title": "Standard Query Param",
"type": "integer",
"default": 50,
},
"name": "standard_query_param",
"in": "query",
},
{
"required": False,
"schema": {"title": "Extra Param 1"},
"name": "extra_param_1",
"in": "query",
},
{
"required": True,
"schema": {"title": "Extra Param 2"},
"name": "extra_param_2",
"in": "query",
},
],
"responses": {
"200": {
"description": "Successful Response",
"content": {"application/json": {"schema": {}}},
},
"422": {
"description": "Validation Error",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/HTTPValidationError"
}
}
},
},
},
}
}
},
"components": {
"schemas": {
"HTTPValidationError": {
"title": "HTTPValidationError",
"type": "object",
"properties": {
"detail": {
"title": "Detail",
"type": "array",
"items": {"$ref": "#/components/schemas/ValidationError"},
}
},
},
"ValidationError": {
"title": "ValidationError",
"required": ["loc", "msg", "type"],
"type": "object",
"properties": {
"loc": {
"title": "Location",
"type": "array",
"items": {
"anyOf": [{"type": "string"}, {"type": "integer"}]
},
},
"msg": {"title": "Message", "type": "string"},
"type": {"title": "Error Type", "type": "string"},
},
},
}
},
}

View File

@@ -12,34 +12,31 @@ def route_with_extras():
client = TestClient(app)
openapi_schema = {
"openapi": "3.0.2",
"info": {"title": "FastAPI", "version": "0.1.0"},
"paths": {
"/": {
"get": {
"responses": {
"200": {
"description": "Successful Response",
"content": {"application/json": {"schema": {}}},
},
},
"summary": "Route With Extras",
"operationId": "route_with_extras__get",
"x-custom-extension": "value",
}
},
},
}
def test_get_route():
response = client.get("/")
assert response.status_code == 200, response.text
assert response.json() == {}
def test_openapi():
response = client.get("/openapi.json")
assert response.status_code == 200, response.text
assert response.json() == openapi_schema
def test_get_route():
response = client.get("/")
assert response.status_code == 200, response.text
assert response.json() == {}
assert response.json() == {
"openapi": "3.0.2",
"info": {"title": "FastAPI", "version": "0.1.0"},
"paths": {
"/": {
"get": {
"responses": {
"200": {
"description": "Successful Response",
"content": {"application/json": {"schema": {}}},
},
},
"summary": "Route With Extras",
"operationId": "route_with_extras__get",
"x-custom-extension": "value",
}
},
},
}

View File

@@ -21,40 +21,37 @@ def foo():
client = TestClient(app)
openapi_schema = {
"openapi": "3.0.2",
"info": {"title": "FastAPI", "version": "0.1.0"},
"servers": [
{"url": "/", "description": "Default, relative server"},
{
"url": "http://staging.localhost.tiangolo.com:8000",
"description": "Staging but actually localhost still",
},
{"url": "https://prod.example.com"},
],
"paths": {
"/foo": {
"get": {
"summary": "Foo",
"operationId": "foo_foo_get",
"responses": {
"200": {
"description": "Successful Response",
"content": {"application/json": {"schema": {}}},
}
},
}
}
},
}
def test_openapi_servers():
response = client.get("/openapi.json")
assert response.status_code == 200, response.text
assert response.json() == openapi_schema
def test_app():
response = client.get("/foo")
assert response.status_code == 200, response.text
def test_openapi_schema():
response = client.get("/openapi.json")
assert response.status_code == 200, response.text
assert response.json() == {
"openapi": "3.0.2",
"info": {"title": "FastAPI", "version": "0.1.0"},
"servers": [
{"url": "/", "description": "Default, relative server"},
{
"url": "http://staging.localhost.tiangolo.com:8000",
"description": "Staging but actually localhost still",
},
{"url": "https://prod.example.com"},
],
"paths": {
"/foo": {
"get": {
"summary": "Foo",
"operationId": "foo_foo_get",
"responses": {
"200": {
"description": "Successful Response",
"content": {"application/json": {"schema": {}}},
}
},
}
}
},
}

View File

@@ -15,79 +15,79 @@ async def read_users(user_id: int):
client = TestClient(app)
openapi_schema = {
"openapi": "3.0.2",
"info": {"title": "FastAPI", "version": "0.1.0"},
"paths": {
"/users/{user_id}": {
"get": {
"summary": "Read Users",
"operationId": "read_users_users__user_id__get",
"parameters": [
{
"required": True,
"schema": {"title": "User Id", "type": "integer"},
"name": "user_id",
"in": "path",
},
],
"responses": {
"200": {
"description": "Successful Response",
"content": {"application/json": {"schema": {}}},
},
"422": {
"description": "Validation Error",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/HTTPValidationError"
}
}
},
},
},
}
}
},
"components": {
"schemas": {
"HTTPValidationError": {
"title": "HTTPValidationError",
"type": "object",
"properties": {
"detail": {
"title": "Detail",
"type": "array",
"items": {"$ref": "#/components/schemas/ValidationError"},
}
},
},
"ValidationError": {
"title": "ValidationError",
"required": ["loc", "msg", "type"],
"type": "object",
"properties": {
"loc": {
"title": "Location",
"type": "array",
"items": {"anyOf": [{"type": "string"}, {"type": "integer"}]},
},
"msg": {"title": "Message", "type": "string"},
"type": {"title": "Error Type", "type": "string"},
},
},
}
},
}
def test_reused_param():
response = client.get("/openapi.json")
data = response.json()
assert data == openapi_schema
def test_read_users():
response = client.get("/users/42")
assert response.status_code == 200, response.text
def test_openapi_schema():
response = client.get("/openapi.json")
data = response.json()
assert data == {
"openapi": "3.0.2",
"info": {"title": "FastAPI", "version": "0.1.0"},
"paths": {
"/users/{user_id}": {
"get": {
"summary": "Read Users",
"operationId": "read_users_users__user_id__get",
"parameters": [
{
"required": True,
"schema": {"title": "User Id", "type": "integer"},
"name": "user_id",
"in": "path",
},
],
"responses": {
"200": {
"description": "Successful Response",
"content": {"application/json": {"schema": {}}},
},
"422": {
"description": "Validation Error",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/HTTPValidationError"
}
}
},
},
},
}
}
},
"components": {
"schemas": {
"HTTPValidationError": {
"title": "HTTPValidationError",
"type": "object",
"properties": {
"detail": {
"title": "Detail",
"type": "array",
"items": {"$ref": "#/components/schemas/ValidationError"},
}
},
},
"ValidationError": {
"title": "ValidationError",
"required": ["loc", "msg", "type"],
"type": "object",
"properties": {
"loc": {
"title": "Location",
"type": "array",
"items": {
"anyOf": [{"type": "string"}, {"type": "integer"}]
},
},
"msg": {"title": "Message", "type": "string"},
"type": {"title": "Error Type", "type": "string"},
},
},
}
},
}

View File

@@ -12,79 +12,6 @@ def save_item_no_body(item_id: str):
client = TestClient(app)
openapi_schema = {
"openapi": "3.0.2",
"info": {"title": "FastAPI", "version": "0.1.0"},
"paths": {
"/items/{item_id}": {
"put": {
"responses": {
"200": {
"description": "Successful Response",
"content": {"application/json": {"schema": {}}},
},
"422": {
"description": "Validation Error",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/HTTPValidationError"
}
}
},
},
},
"summary": "Save Item No Body",
"operationId": "save_item_no_body_items__item_id__put",
"parameters": [
{
"required": True,
"schema": {"title": "Item Id", "type": "string"},
"name": "item_id",
"in": "path",
}
],
}
}
},
"components": {
"schemas": {
"ValidationError": {
"title": "ValidationError",
"required": ["loc", "msg", "type"],
"type": "object",
"properties": {
"loc": {
"title": "Location",
"type": "array",
"items": {"anyOf": [{"type": "string"}, {"type": "integer"}]},
},
"msg": {"title": "Message", "type": "string"},
"type": {"title": "Error Type", "type": "string"},
},
},
"HTTPValidationError": {
"title": "HTTPValidationError",
"type": "object",
"properties": {
"detail": {
"title": "Detail",
"type": "array",
"items": {"$ref": "#/components/schemas/ValidationError"},
}
},
},
}
},
}
def test_openapi_schema():
response = client.get("/openapi.json")
assert response.status_code == 200, response.text
assert response.json() == openapi_schema
def test_put_no_body():
response = client.put("/items/foo")
assert response.status_code == 200, response.text
@@ -95,3 +22,75 @@ def test_put_no_body_with_body():
response = client.put("/items/foo", json={"name": "Foo"})
assert response.status_code == 200, response.text
assert response.json() == {"item_id": "foo"}
def test_openapi_schema():
response = client.get("/openapi.json")
assert response.status_code == 200, response.text
assert response.json() == {
"openapi": "3.0.2",
"info": {"title": "FastAPI", "version": "0.1.0"},
"paths": {
"/items/{item_id}": {
"put": {
"responses": {
"200": {
"description": "Successful Response",
"content": {"application/json": {"schema": {}}},
},
"422": {
"description": "Validation Error",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/HTTPValidationError"
}
}
},
},
},
"summary": "Save Item No Body",
"operationId": "save_item_no_body_items__item_id__put",
"parameters": [
{
"required": True,
"schema": {"title": "Item Id", "type": "string"},
"name": "item_id",
"in": "path",
}
],
}
}
},
"components": {
"schemas": {
"ValidationError": {
"title": "ValidationError",
"required": ["loc", "msg", "type"],
"type": "object",
"properties": {
"loc": {
"title": "Location",
"type": "array",
"items": {
"anyOf": [{"type": "string"}, {"type": "integer"}]
},
},
"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"},
}
},
},
}
},
}

View File

@@ -14,87 +14,87 @@ def get_parameters_with_repeated_aliases(
client = TestClient(app)
openapi_schema = {
"components": {
"schemas": {
"HTTPValidationError": {
"properties": {
"detail": {
"items": {"$ref": "#/components/schemas/ValidationError"},
"title": "Detail",
"type": "array",
}
},
"title": "HTTPValidationError",
"type": "object",
},
"ValidationError": {
"properties": {
"loc": {
"items": {"anyOf": [{"type": "string"}, {"type": "integer"}]},
"title": "Location",
"type": "array",
},
"msg": {"title": "Message", "type": "string"},
"type": {"title": "Error Type", "type": "string"},
},
"required": ["loc", "msg", "type"],
"title": "ValidationError",
"type": "object",
},
}
},
"info": {"title": "FastAPI", "version": "0.1.0"},
"openapi": "3.0.2",
"paths": {
"/{repeated_alias}": {
"get": {
"operationId": "get_parameters_with_repeated_aliases__repeated_alias__get",
"parameters": [
{
"in": "path",
"name": "repeated_alias",
"required": True,
"schema": {"title": "Repeated Alias", "type": "string"},
},
{
"in": "query",
"name": "repeated_alias",
"required": True,
"schema": {"title": "Repeated Alias", "type": "string"},
},
],
"responses": {
"200": {
"content": {"application/json": {"schema": {}}},
"description": "Successful Response",
},
"422": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/HTTPValidationError"
}
}
},
"description": "Validation Error",
},
},
"summary": "Get Parameters With Repeated Aliases",
}
}
},
}
def test_get_parameters():
response = client.get("/test_path", params={"repeated_alias": "test_query"})
assert response.status_code == 200, response.text
assert response.json() == {"path": "test_path", "query": "test_query"}
def test_openapi_schema():
response = client.get("/openapi.json")
assert response.status_code == status.HTTP_200_OK
actual_schema = response.json()
assert actual_schema == openapi_schema
def test_get_parameters():
response = client.get("/test_path", params={"repeated_alias": "test_query"})
assert response.status_code == 200, response.text
assert response.json() == {"path": "test_path", "query": "test_query"}
assert actual_schema == {
"components": {
"schemas": {
"HTTPValidationError": {
"properties": {
"detail": {
"items": {"$ref": "#/components/schemas/ValidationError"},
"title": "Detail",
"type": "array",
}
},
"title": "HTTPValidationError",
"type": "object",
},
"ValidationError": {
"properties": {
"loc": {
"items": {
"anyOf": [{"type": "string"}, {"type": "integer"}]
},
"title": "Location",
"type": "array",
},
"msg": {"title": "Message", "type": "string"},
"type": {"title": "Error Type", "type": "string"},
},
"required": ["loc", "msg", "type"],
"title": "ValidationError",
"type": "object",
},
}
},
"info": {"title": "FastAPI", "version": "0.1.0"},
"openapi": "3.0.2",
"paths": {
"/{repeated_alias}": {
"get": {
"operationId": "get_parameters_with_repeated_aliases__repeated_alias__get",
"parameters": [
{
"in": "path",
"name": "repeated_alias",
"required": True,
"schema": {"title": "Repeated Alias", "type": "string"},
},
{
"in": "query",
"name": "repeated_alias",
"required": True,
"schema": {"title": "Repeated Alias", "type": "string"},
},
],
"responses": {
"200": {
"content": {"application/json": {"schema": {}}},
"description": "Successful Response",
},
"422": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/HTTPValidationError"
}
}
},
"description": "Validation Error",
},
},
"summary": "Get Parameters With Repeated Aliases",
}
}
},
}

View File

@@ -22,77 +22,76 @@ async def delete_deployment(
client = TestClient(app)
openapi_schema = {
"openapi": "3.0.2",
"info": {"title": "FastAPI", "version": "0.1.0"},
"paths": {
"/{id}": {
"delete": {
"summary": "Delete Deployment",
"operationId": "delete_deployment__id__delete",
"parameters": [
{
"required": True,
"schema": {"title": "Id", "type": "integer"},
"name": "id",
"in": "path",
}
],
"responses": {
"204": {"description": "Successful Response"},
"422": {
"description": "Validation Error",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/HTTPValidationError"
}
}
},
},
},
}
}
},
"components": {
"schemas": {
"HTTPValidationError": {
"title": "HTTPValidationError",
"type": "object",
"properties": {
"detail": {
"title": "Detail",
"type": "array",
"items": {"$ref": "#/components/schemas/ValidationError"},
}
},
},
"ValidationError": {
"title": "ValidationError",
"required": ["loc", "msg", "type"],
"type": "object",
"properties": {
"loc": {
"title": "Location",
"type": "array",
"items": {"anyOf": [{"type": "string"}, {"type": "integer"}]},
},
"msg": {"title": "Message", "type": "string"},
"type": {"title": "Error Type", "type": "string"},
},
},
}
},
}
def test_dependency_set_status_code():
response = client.delete("/1")
assert response.status_code == 400 and response.content
assert response.json() == {"msg": "Status overwritten", "id": 1}
def test_openapi_schema():
response = client.get("/openapi.json")
assert response.status_code == 200, response.text
assert response.json() == openapi_schema
def test_dependency_set_status_code():
response = client.delete("/1")
assert response.status_code == 400 and response.content
assert response.json() == {"msg": "Status overwritten", "id": 1}
assert response.json() == {
"openapi": "3.0.2",
"info": {"title": "FastAPI", "version": "0.1.0"},
"paths": {
"/{id}": {
"delete": {
"summary": "Delete Deployment",
"operationId": "delete_deployment__id__delete",
"parameters": [
{
"required": True,
"schema": {"title": "Id", "type": "integer"},
"name": "id",
"in": "path",
}
],
"responses": {
"204": {"description": "Successful Response"},
"422": {
"description": "Validation Error",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/HTTPValidationError"
}
}
},
},
},
}
}
},
"components": {
"schemas": {
"HTTPValidationError": {
"title": "HTTPValidationError",
"type": "object",
"properties": {
"detail": {
"title": "Detail",
"type": "array",
"items": {"$ref": "#/components/schemas/ValidationError"},
}
},
},
"ValidationError": {
"title": "ValidationError",
"required": ["loc", "msg", "type"],
"type": "object",
"properties": {
"loc": {
"title": "Location",
"type": "array",
"items": {
"anyOf": [{"type": "string"}, {"type": "integer"}]
},
},
"msg": {"title": "Message", "type": "string"},
"type": {"title": "Error Type", "type": "string"},
},
},
}
},
}

View File

@@ -33,36 +33,146 @@ async def create_shop(
pass # pragma: no cover
create_product_request_body = {
"content": {
"application/vnd.api+json": {
"schema": {"$ref": "#/components/schemas/Body_create_product_products_post"}
}
},
"required": True,
}
create_shop_request_body = {
"content": {
"application/vnd.api+json": {
"schema": {"$ref": "#/components/schemas/Body_create_shop_shops_post"}
}
},
"required": True,
}
client = TestClient(app)
def test_openapi_schema():
response = client.get("/openapi.json")
assert response.status_code == 200, response.text
openapi_schema = response.json()
assert (
openapi_schema["paths"]["/products"]["post"]["requestBody"]
== create_product_request_body
)
assert (
openapi_schema["paths"]["/shops"]["post"]["requestBody"]
== create_shop_request_body
)
# insert_assert(response.json())
assert response.json() == {
"openapi": "3.0.2",
"info": {"title": "FastAPI", "version": "0.1.0"},
"paths": {
"/products": {
"post": {
"summary": "Create Product",
"operationId": "create_product_products_post",
"requestBody": {
"content": {
"application/vnd.api+json": {
"schema": {
"$ref": "#/components/schemas/Body_create_product_products_post"
}
}
},
"required": True,
},
"responses": {
"200": {
"description": "Successful Response",
"content": {"application/json": {"schema": {}}},
},
"422": {
"description": "Validation Error",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/HTTPValidationError"
}
}
},
},
},
}
},
"/shops": {
"post": {
"summary": "Create Shop",
"operationId": "create_shop_shops_post",
"requestBody": {
"content": {
"application/vnd.api+json": {
"schema": {
"$ref": "#/components/schemas/Body_create_shop_shops_post"
}
}
},
"required": True,
},
"responses": {
"200": {
"description": "Successful Response",
"content": {"application/json": {"schema": {}}},
},
"422": {
"description": "Validation Error",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/HTTPValidationError"
}
}
},
},
},
}
},
},
"components": {
"schemas": {
"Body_create_product_products_post": {
"title": "Body_create_product_products_post",
"required": ["data"],
"type": "object",
"properties": {"data": {"$ref": "#/components/schemas/Product"}},
},
"Body_create_shop_shops_post": {
"title": "Body_create_shop_shops_post",
"required": ["data"],
"type": "object",
"properties": {
"data": {"$ref": "#/components/schemas/Shop"},
"included": {
"title": "Included",
"type": "array",
"items": {"$ref": "#/components/schemas/Product"},
"default": [],
},
},
},
"HTTPValidationError": {
"title": "HTTPValidationError",
"type": "object",
"properties": {
"detail": {
"title": "Detail",
"type": "array",
"items": {"$ref": "#/components/schemas/ValidationError"},
}
},
},
"Product": {
"title": "Product",
"required": ["name", "price"],
"type": "object",
"properties": {
"name": {"title": "Name", "type": "string"},
"price": {"title": "Price", "type": "number"},
},
},
"Shop": {
"title": "Shop",
"required": ["name"],
"type": "object",
"properties": {"name": {"title": "Name", "type": "string"}},
},
"ValidationError": {
"title": "ValidationError",
"required": ["loc", "msg", "type"],
"type": "object",
"properties": {
"loc": {
"title": "Location",
"type": "array",
"items": {
"anyOf": [{"type": "string"}, {"type": "integer"}]
},
},
"msg": {"title": "Message", "type": "string"},
"type": {"title": "Error Type", "type": "string"},
},
},
}
},
}

View File

@@ -68,198 +68,9 @@ def no_alias_list():
return [{"name": "Foo"}, {"name": "Bar"}]
openapi_schema = {
"openapi": "3.0.2",
"info": {"title": "FastAPI", "version": "0.1.0"},
"paths": {
"/dict": {
"get": {
"summary": "Read Dict",
"operationId": "read_dict_dict_get",
"responses": {
"200": {
"description": "Successful Response",
"content": {
"application/json": {
"schema": {"$ref": "#/components/schemas/Model"}
}
},
}
},
}
},
"/model": {
"get": {
"summary": "Read Model",
"operationId": "read_model_model_get",
"responses": {
"200": {
"description": "Successful Response",
"content": {
"application/json": {
"schema": {"$ref": "#/components/schemas/Model"}
}
},
}
},
}
},
"/list": {
"get": {
"summary": "Read List",
"operationId": "read_list_list_get",
"responses": {
"200": {
"description": "Successful Response",
"content": {
"application/json": {
"schema": {
"title": "Response Read List List Get",
"type": "array",
"items": {"$ref": "#/components/schemas/Model"},
}
}
},
}
},
}
},
"/by-alias/dict": {
"get": {
"summary": "By Alias Dict",
"operationId": "by_alias_dict_by_alias_dict_get",
"responses": {
"200": {
"description": "Successful Response",
"content": {
"application/json": {
"schema": {"$ref": "#/components/schemas/Model"}
}
},
}
},
}
},
"/by-alias/model": {
"get": {
"summary": "By Alias Model",
"operationId": "by_alias_model_by_alias_model_get",
"responses": {
"200": {
"description": "Successful Response",
"content": {
"application/json": {
"schema": {"$ref": "#/components/schemas/Model"}
}
},
}
},
}
},
"/by-alias/list": {
"get": {
"summary": "By Alias List",
"operationId": "by_alias_list_by_alias_list_get",
"responses": {
"200": {
"description": "Successful Response",
"content": {
"application/json": {
"schema": {
"title": "Response By Alias List By Alias List Get",
"type": "array",
"items": {"$ref": "#/components/schemas/Model"},
}
}
},
}
},
}
},
"/no-alias/dict": {
"get": {
"summary": "No Alias Dict",
"operationId": "no_alias_dict_no_alias_dict_get",
"responses": {
"200": {
"description": "Successful Response",
"content": {
"application/json": {
"schema": {"$ref": "#/components/schemas/ModelNoAlias"}
}
},
}
},
}
},
"/no-alias/model": {
"get": {
"summary": "No Alias Model",
"operationId": "no_alias_model_no_alias_model_get",
"responses": {
"200": {
"description": "Successful Response",
"content": {
"application/json": {
"schema": {"$ref": "#/components/schemas/ModelNoAlias"}
}
},
}
},
}
},
"/no-alias/list": {
"get": {
"summary": "No Alias List",
"operationId": "no_alias_list_no_alias_list_get",
"responses": {
"200": {
"description": "Successful Response",
"content": {
"application/json": {
"schema": {
"title": "Response No Alias List No Alias List Get",
"type": "array",
"items": {
"$ref": "#/components/schemas/ModelNoAlias"
},
}
}
},
}
},
}
},
},
"components": {
"schemas": {
"Model": {
"title": "Model",
"required": ["alias"],
"type": "object",
"properties": {"alias": {"title": "Alias", "type": "string"}},
},
"ModelNoAlias": {
"title": "ModelNoAlias",
"required": ["name"],
"type": "object",
"properties": {"name": {"title": "Name", "type": "string"}},
"description": "response_model_by_alias=False is basically a quick hack, to support proper OpenAPI use another model with the correct field names",
},
}
},
}
client = TestClient(app)
def test_openapi_schema():
response = client.get("/openapi.json")
assert response.status_code == 200, response.text
assert response.json() == openapi_schema
def test_read_dict():
response = client.get("/dict")
assert response.status_code == 200, response.text
@@ -321,3 +132,193 @@ def test_read_list_no_alias():
{"name": "Foo"},
{"name": "Bar"},
]
def test_openapi_schema():
response = client.get("/openapi.json")
assert response.status_code == 200, response.text
assert response.json() == {
"openapi": "3.0.2",
"info": {"title": "FastAPI", "version": "0.1.0"},
"paths": {
"/dict": {
"get": {
"summary": "Read Dict",
"operationId": "read_dict_dict_get",
"responses": {
"200": {
"description": "Successful Response",
"content": {
"application/json": {
"schema": {"$ref": "#/components/schemas/Model"}
}
},
}
},
}
},
"/model": {
"get": {
"summary": "Read Model",
"operationId": "read_model_model_get",
"responses": {
"200": {
"description": "Successful Response",
"content": {
"application/json": {
"schema": {"$ref": "#/components/schemas/Model"}
}
},
}
},
}
},
"/list": {
"get": {
"summary": "Read List",
"operationId": "read_list_list_get",
"responses": {
"200": {
"description": "Successful Response",
"content": {
"application/json": {
"schema": {
"title": "Response Read List List Get",
"type": "array",
"items": {"$ref": "#/components/schemas/Model"},
}
}
},
}
},
}
},
"/by-alias/dict": {
"get": {
"summary": "By Alias Dict",
"operationId": "by_alias_dict_by_alias_dict_get",
"responses": {
"200": {
"description": "Successful Response",
"content": {
"application/json": {
"schema": {"$ref": "#/components/schemas/Model"}
}
},
}
},
}
},
"/by-alias/model": {
"get": {
"summary": "By Alias Model",
"operationId": "by_alias_model_by_alias_model_get",
"responses": {
"200": {
"description": "Successful Response",
"content": {
"application/json": {
"schema": {"$ref": "#/components/schemas/Model"}
}
},
}
},
}
},
"/by-alias/list": {
"get": {
"summary": "By Alias List",
"operationId": "by_alias_list_by_alias_list_get",
"responses": {
"200": {
"description": "Successful Response",
"content": {
"application/json": {
"schema": {
"title": "Response By Alias List By Alias List Get",
"type": "array",
"items": {"$ref": "#/components/schemas/Model"},
}
}
},
}
},
}
},
"/no-alias/dict": {
"get": {
"summary": "No Alias Dict",
"operationId": "no_alias_dict_no_alias_dict_get",
"responses": {
"200": {
"description": "Successful Response",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ModelNoAlias"
}
}
},
}
},
}
},
"/no-alias/model": {
"get": {
"summary": "No Alias Model",
"operationId": "no_alias_model_no_alias_model_get",
"responses": {
"200": {
"description": "Successful Response",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ModelNoAlias"
}
}
},
}
},
}
},
"/no-alias/list": {
"get": {
"summary": "No Alias List",
"operationId": "no_alias_list_no_alias_list_get",
"responses": {
"200": {
"description": "Successful Response",
"content": {
"application/json": {
"schema": {
"title": "Response No Alias List No Alias List Get",
"type": "array",
"items": {
"$ref": "#/components/schemas/ModelNoAlias"
},
}
}
},
}
},
}
},
},
"components": {
"schemas": {
"Model": {
"title": "Model",
"required": ["alias"],
"type": "object",
"properties": {"alias": {"title": "Alias", "type": "string"}},
},
"ModelNoAlias": {
"title": "ModelNoAlias",
"required": ["name"],
"type": "object",
"properties": {"name": {"title": "Name", "type": "string"}},
"description": "response_model_by_alias=False is basically a quick hack, to support proper OpenAPI use another model with the correct field names",
},
}
},
}

View File

@@ -35,80 +35,79 @@ async def b():
pass # pragma: no cover
openapi_schema = {
"openapi": "3.0.2",
"info": {"title": "FastAPI", "version": "0.1.0"},
"paths": {
"/a": {
"get": {
"responses": {
"500": {
"description": "Error",
"content": {
"application/json": {
"schema": {"$ref": "#/components/schemas/JsonApiError"}
}
},
},
"200": {"description": "Successful Response"},
},
"summary": "A",
"operationId": "a_a_get",
}
},
"/b": {
"get": {
"responses": {
"500": {
"description": "Error",
"content": {
"application/json": {
"schema": {"$ref": "#/components/schemas/Error"}
}
},
},
"200": {
"description": "Successful Response",
"content": {"application/json": {"schema": {}}},
},
},
"summary": "B",
"operationId": "b_b_get",
}
},
},
"components": {
"schemas": {
"Error": {
"title": "Error",
"required": ["status", "title"],
"type": "object",
"properties": {
"status": {"title": "Status", "type": "string"},
"title": {"title": "Title", "type": "string"},
},
},
"JsonApiError": {
"title": "JsonApiError",
"required": ["errors"],
"type": "object",
"properties": {
"errors": {
"title": "Errors",
"type": "array",
"items": {"$ref": "#/components/schemas/Error"},
}
},
},
}
},
}
client = TestClient(app)
def test_openapi_schema():
response = client.get("/openapi.json")
assert response.status_code == 200, response.text
assert response.json() == openapi_schema
assert response.json() == {
"openapi": "3.0.2",
"info": {"title": "FastAPI", "version": "0.1.0"},
"paths": {
"/a": {
"get": {
"responses": {
"500": {
"description": "Error",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/JsonApiError"
}
}
},
},
"200": {"description": "Successful Response"},
},
"summary": "A",
"operationId": "a_a_get",
}
},
"/b": {
"get": {
"responses": {
"500": {
"description": "Error",
"content": {
"application/json": {
"schema": {"$ref": "#/components/schemas/Error"}
}
},
},
"200": {
"description": "Successful Response",
"content": {"application/json": {"schema": {}}},
},
},
"summary": "B",
"operationId": "b_b_get",
}
},
},
"components": {
"schemas": {
"Error": {
"title": "Error",
"required": ["status", "title"],
"type": "object",
"properties": {
"status": {"title": "Status", "type": "string"},
"title": {"title": "Title", "type": "string"},
},
},
"JsonApiError": {
"title": "JsonApiError",
"required": ["errors"],
"type": "object",
"properties": {
"errors": {
"title": "Errors",
"type": "array",
"items": {"$ref": "#/components/schemas/Error"},
}
},
},
}
},
}

View File

@@ -36,80 +36,79 @@ async def b():
pass # pragma: no cover
openapi_schema = {
"openapi": "3.0.2",
"info": {"title": "FastAPI", "version": "0.1.0"},
"paths": {
"/a": {
"get": {
"responses": {
"500": {
"description": "Error",
"content": {
"application/vnd.api+json": {
"schema": {"$ref": "#/components/schemas/JsonApiError"}
}
},
},
"204": {"description": "Successful Response"},
},
"summary": "A",
"operationId": "a_a_get",
}
},
"/b": {
"get": {
"responses": {
"204": {"description": "No Content"},
"200": {
"description": "Successful Response",
"content": {"application/json": {"schema": {}}},
},
},
"summary": "B",
"operationId": "b_b_get",
}
},
},
"components": {
"schemas": {
"Error": {
"title": "Error",
"required": ["status", "title"],
"type": "object",
"properties": {
"status": {"title": "Status", "type": "string"},
"title": {"title": "Title", "type": "string"},
},
},
"JsonApiError": {
"title": "JsonApiError",
"required": ["errors"],
"type": "object",
"properties": {
"errors": {
"title": "Errors",
"type": "array",
"items": {"$ref": "#/components/schemas/Error"},
}
},
},
}
},
}
client = TestClient(app)
def test_openapi_schema():
response = client.get("/openapi.json")
assert response.status_code == 200, response.text
assert response.json() == openapi_schema
def test_get_response():
response = client.get("/a")
assert response.status_code == 204, response.text
assert "content-length" not in response.headers
assert response.content == b""
def test_openapi_schema():
response = client.get("/openapi.json")
assert response.status_code == 200, response.text
assert response.json() == {
"openapi": "3.0.2",
"info": {"title": "FastAPI", "version": "0.1.0"},
"paths": {
"/a": {
"get": {
"responses": {
"500": {
"description": "Error",
"content": {
"application/vnd.api+json": {
"schema": {
"$ref": "#/components/schemas/JsonApiError"
}
}
},
},
"204": {"description": "Successful Response"},
},
"summary": "A",
"operationId": "a_a_get",
}
},
"/b": {
"get": {
"responses": {
"204": {"description": "No Content"},
"200": {
"description": "Successful Response",
"content": {"application/json": {"schema": {}}},
},
},
"summary": "B",
"operationId": "b_b_get",
}
},
},
"components": {
"schemas": {
"Error": {
"title": "Error",
"required": ["status", "title"],
"type": "object",
"properties": {
"status": {"title": "Status", "type": "string"},
"title": {"title": "Title", "type": "string"},
},
},
"JsonApiError": {
"title": "JsonApiError",
"required": ["errors"],
"type": "object",
"properties": {
"errors": {
"title": "Errors",
"type": "array",
"items": {"$ref": "#/components/schemas/Error"},
}
},
},
}
},
}

View File

File diff suppressed because it is too large Load Diff

View File

@@ -32,123 +32,9 @@ def valid4():
pass
openapi_schema = {
"openapi": "3.0.2",
"info": {"title": "FastAPI", "version": "0.1.0"},
"paths": {
"/valid1": {
"get": {
"summary": "Valid1",
"operationId": "valid1_valid1_get",
"responses": {
"200": {
"description": "Successful Response",
"content": {"application/json": {"schema": {}}},
},
"500": {
"description": "Internal Server Error",
"content": {
"application/json": {
"schema": {
"title": "Response 500 Valid1 Valid1 Get",
"type": "integer",
}
}
},
},
},
}
},
"/valid2": {
"get": {
"summary": "Valid2",
"operationId": "valid2_valid2_get",
"responses": {
"200": {
"description": "Successful Response",
"content": {"application/json": {"schema": {}}},
},
"500": {
"description": "Internal Server Error",
"content": {
"application/json": {
"schema": {
"title": "Response 500 Valid2 Valid2 Get",
"type": "array",
"items": {"type": "integer"},
}
}
},
},
},
}
},
"/valid3": {
"get": {
"summary": "Valid3",
"operationId": "valid3_valid3_get",
"responses": {
"200": {
"description": "Successful Response",
"content": {"application/json": {"schema": {}}},
},
"500": {
"description": "Internal Server Error",
"content": {
"application/json": {
"schema": {"$ref": "#/components/schemas/Model"}
}
},
},
},
}
},
"/valid4": {
"get": {
"summary": "Valid4",
"operationId": "valid4_valid4_get",
"responses": {
"200": {
"description": "Successful Response",
"content": {"application/json": {"schema": {}}},
},
"500": {
"description": "Internal Server Error",
"content": {
"application/json": {
"schema": {
"title": "Response 500 Valid4 Valid4 Get",
"type": "array",
"items": {"$ref": "#/components/schemas/Model"},
}
}
},
},
},
}
},
},
"components": {
"schemas": {
"Model": {
"title": "Model",
"required": ["name"],
"type": "object",
"properties": {"name": {"title": "Name", "type": "string"}},
}
}
},
}
client = TestClient(app)
def test_openapi_schema():
response = client.get("/openapi.json")
assert response.status_code == 200, response.text
assert response.json() == openapi_schema
def test_path_operations():
response = client.get("/valid1")
assert response.status_code == 200, response.text
@@ -158,3 +44,115 @@ def test_path_operations():
assert response.status_code == 200, response.text
response = client.get("/valid4")
assert response.status_code == 200, response.text
def test_openapi_schema():
response = client.get("/openapi.json")
assert response.status_code == 200, response.text
assert response.json() == {
"openapi": "3.0.2",
"info": {"title": "FastAPI", "version": "0.1.0"},
"paths": {
"/valid1": {
"get": {
"summary": "Valid1",
"operationId": "valid1_valid1_get",
"responses": {
"200": {
"description": "Successful Response",
"content": {"application/json": {"schema": {}}},
},
"500": {
"description": "Internal Server Error",
"content": {
"application/json": {
"schema": {
"title": "Response 500 Valid1 Valid1 Get",
"type": "integer",
}
}
},
},
},
}
},
"/valid2": {
"get": {
"summary": "Valid2",
"operationId": "valid2_valid2_get",
"responses": {
"200": {
"description": "Successful Response",
"content": {"application/json": {"schema": {}}},
},
"500": {
"description": "Internal Server Error",
"content": {
"application/json": {
"schema": {
"title": "Response 500 Valid2 Valid2 Get",
"type": "array",
"items": {"type": "integer"},
}
}
},
},
},
}
},
"/valid3": {
"get": {
"summary": "Valid3",
"operationId": "valid3_valid3_get",
"responses": {
"200": {
"description": "Successful Response",
"content": {"application/json": {"schema": {}}},
},
"500": {
"description": "Internal Server Error",
"content": {
"application/json": {
"schema": {"$ref": "#/components/schemas/Model"}
}
},
},
},
}
},
"/valid4": {
"get": {
"summary": "Valid4",
"operationId": "valid4_valid4_get",
"responses": {
"200": {
"description": "Successful Response",
"content": {"application/json": {"schema": {}}},
},
"500": {
"description": "Internal Server Error",
"content": {
"application/json": {
"schema": {
"title": "Response 500 Valid4 Valid4 Get",
"type": "array",
"items": {"$ref": "#/components/schemas/Model"},
}
}
},
},
},
}
},
},
"components": {
"schemas": {
"Model": {
"title": "Model",
"required": ["name"],
"type": "object",
"properties": {"name": {"title": "Name", "type": "string"}},
}
}
},
}

View File

File diff suppressed because it is too large Load Diff

View File

@@ -22,39 +22,6 @@ def read_current_user(current_user: User = Depends(get_current_user)):
return current_user
openapi_schema = {
"openapi": "3.0.2",
"info": {"title": "FastAPI", "version": "0.1.0"},
"paths": {
"/users/me": {
"get": {
"responses": {
"200": {
"description": "Successful Response",
"content": {"application/json": {"schema": {}}},
}
},
"summary": "Read Current User",
"operationId": "read_current_user_users_me_get",
"security": [{"APIKeyCookie": []}],
}
}
},
"components": {
"securitySchemes": {
"APIKeyCookie": {"type": "apiKey", "name": "key", "in": "cookie"}
}
},
}
def test_openapi_schema():
client = TestClient(app)
response = client.get("/openapi.json")
assert response.status_code == 200, response.text
assert response.json() == openapi_schema
def test_security_api_key():
client = TestClient(app, cookies={"key": "secret"})
response = client.get("/users/me")
@@ -67,3 +34,33 @@ def test_security_api_key_no_key():
response = client.get("/users/me")
assert response.status_code == 403, response.text
assert response.json() == {"detail": "Not authenticated"}
def test_openapi_schema():
client = TestClient(app)
response = client.get("/openapi.json")
assert response.status_code == 200, response.text
assert response.json() == {
"openapi": "3.0.2",
"info": {"title": "FastAPI", "version": "0.1.0"},
"paths": {
"/users/me": {
"get": {
"responses": {
"200": {
"description": "Successful Response",
"content": {"application/json": {"schema": {}}},
}
},
"summary": "Read Current User",
"operationId": "read_current_user_users_me_get",
"security": [{"APIKeyCookie": []}],
}
}
},
"components": {
"securitySchemes": {
"APIKeyCookie": {"type": "apiKey", "name": "key", "in": "cookie"}
}
},
}

View File

@@ -22,44 +22,6 @@ def read_current_user(current_user: User = Depends(get_current_user)):
return current_user
openapi_schema = {
"openapi": "3.0.2",
"info": {"title": "FastAPI", "version": "0.1.0"},
"paths": {
"/users/me": {
"get": {
"responses": {
"200": {
"description": "Successful Response",
"content": {"application/json": {"schema": {}}},
}
},
"summary": "Read Current User",
"operationId": "read_current_user_users_me_get",
"security": [{"APIKeyCookie": []}],
}
}
},
"components": {
"securitySchemes": {
"APIKeyCookie": {
"type": "apiKey",
"name": "key",
"in": "cookie",
"description": "An API Cookie Key",
}
}
},
}
def test_openapi_schema():
client = TestClient(app)
response = client.get("/openapi.json")
assert response.status_code == 200, response.text
assert response.json() == openapi_schema
def test_security_api_key():
client = TestClient(app, cookies={"key": "secret"})
response = client.get("/users/me")
@@ -72,3 +34,38 @@ def test_security_api_key_no_key():
response = client.get("/users/me")
assert response.status_code == 403, response.text
assert response.json() == {"detail": "Not authenticated"}
def test_openapi_schema():
client = TestClient(app)
response = client.get("/openapi.json")
assert response.status_code == 200, response.text
assert response.json() == {
"openapi": "3.0.2",
"info": {"title": "FastAPI", "version": "0.1.0"},
"paths": {
"/users/me": {
"get": {
"responses": {
"200": {
"description": "Successful Response",
"content": {"application/json": {"schema": {}}},
}
},
"summary": "Read Current User",
"operationId": "read_current_user_users_me_get",
"security": [{"APIKeyCookie": []}],
}
}
},
"components": {
"securitySchemes": {
"APIKeyCookie": {
"type": "apiKey",
"name": "key",
"in": "cookie",
"description": "An API Cookie Key",
}
}
},
}

View File

@@ -29,39 +29,6 @@ def read_current_user(current_user: User = Depends(get_current_user)):
return current_user
openapi_schema = {
"openapi": "3.0.2",
"info": {"title": "FastAPI", "version": "0.1.0"},
"paths": {
"/users/me": {
"get": {
"responses": {
"200": {
"description": "Successful Response",
"content": {"application/json": {"schema": {}}},
}
},
"summary": "Read Current User",
"operationId": "read_current_user_users_me_get",
"security": [{"APIKeyCookie": []}],
}
}
},
"components": {
"securitySchemes": {
"APIKeyCookie": {"type": "apiKey", "name": "key", "in": "cookie"}
}
},
}
def test_openapi_schema():
client = TestClient(app)
response = client.get("/openapi.json")
assert response.status_code == 200, response.text
assert response.json() == openapi_schema
def test_security_api_key():
client = TestClient(app, cookies={"key": "secret"})
response = client.get("/users/me")
@@ -74,3 +41,33 @@ def test_security_api_key_no_key():
response = client.get("/users/me")
assert response.status_code == 200, response.text
assert response.json() == {"msg": "Create an account first"}
def test_openapi_schema():
client = TestClient(app)
response = client.get("/openapi.json")
assert response.status_code == 200, response.text
assert response.json() == {
"openapi": "3.0.2",
"info": {"title": "FastAPI", "version": "0.1.0"},
"paths": {
"/users/me": {
"get": {
"responses": {
"200": {
"description": "Successful Response",
"content": {"application/json": {"schema": {}}},
}
},
"summary": "Read Current User",
"operationId": "read_current_user_users_me_get",
"security": [{"APIKeyCookie": []}],
}
}
},
"components": {
"securitySchemes": {
"APIKeyCookie": {"type": "apiKey", "name": "key", "in": "cookie"}
}
},
}

View File

@@ -24,37 +24,6 @@ def read_current_user(current_user: User = Depends(get_current_user)):
client = TestClient(app)
openapi_schema = {
"openapi": "3.0.2",
"info": {"title": "FastAPI", "version": "0.1.0"},
"paths": {
"/users/me": {
"get": {
"responses": {
"200": {
"description": "Successful Response",
"content": {"application/json": {"schema": {}}},
}
},
"summary": "Read Current User",
"operationId": "read_current_user_users_me_get",
"security": [{"APIKeyHeader": []}],
}
}
},
"components": {
"securitySchemes": {
"APIKeyHeader": {"type": "apiKey", "name": "key", "in": "header"}
}
},
}
def test_openapi_schema():
response = client.get("/openapi.json")
assert response.status_code == 200, response.text
assert response.json() == openapi_schema
def test_security_api_key():
response = client.get("/users/me", headers={"key": "secret"})
@@ -66,3 +35,32 @@ def test_security_api_key_no_key():
response = client.get("/users/me")
assert response.status_code == 403, response.text
assert response.json() == {"detail": "Not authenticated"}
def test_openapi_schema():
response = client.get("/openapi.json")
assert response.status_code == 200, response.text
assert response.json() == {
"openapi": "3.0.2",
"info": {"title": "FastAPI", "version": "0.1.0"},
"paths": {
"/users/me": {
"get": {
"responses": {
"200": {
"description": "Successful Response",
"content": {"application/json": {"schema": {}}},
}
},
"summary": "Read Current User",
"operationId": "read_current_user_users_me_get",
"security": [{"APIKeyHeader": []}],
}
}
},
"components": {
"securitySchemes": {
"APIKeyHeader": {"type": "apiKey", "name": "key", "in": "header"}
}
},
}

View File

@@ -24,42 +24,6 @@ def read_current_user(current_user: User = Depends(get_current_user)):
client = TestClient(app)
openapi_schema = {
"openapi": "3.0.2",
"info": {"title": "FastAPI", "version": "0.1.0"},
"paths": {
"/users/me": {
"get": {
"responses": {
"200": {
"description": "Successful Response",
"content": {"application/json": {"schema": {}}},
}
},
"summary": "Read Current User",
"operationId": "read_current_user_users_me_get",
"security": [{"APIKeyHeader": []}],
}
}
},
"components": {
"securitySchemes": {
"APIKeyHeader": {
"type": "apiKey",
"name": "key",
"in": "header",
"description": "An API Key Header",
}
}
},
}
def test_openapi_schema():
response = client.get("/openapi.json")
assert response.status_code == 200, response.text
assert response.json() == openapi_schema
def test_security_api_key():
response = client.get("/users/me", headers={"key": "secret"})
@@ -71,3 +35,37 @@ def test_security_api_key_no_key():
response = client.get("/users/me")
assert response.status_code == 403, response.text
assert response.json() == {"detail": "Not authenticated"}
def test_openapi_schema():
response = client.get("/openapi.json")
assert response.status_code == 200, response.text
assert response.json() == {
"openapi": "3.0.2",
"info": {"title": "FastAPI", "version": "0.1.0"},
"paths": {
"/users/me": {
"get": {
"responses": {
"200": {
"description": "Successful Response",
"content": {"application/json": {"schema": {}}},
}
},
"summary": "Read Current User",
"operationId": "read_current_user_users_me_get",
"security": [{"APIKeyHeader": []}],
}
}
},
"components": {
"securitySchemes": {
"APIKeyHeader": {
"type": "apiKey",
"name": "key",
"in": "header",
"description": "An API Key Header",
}
}
},
}

View File

@@ -30,37 +30,6 @@ def read_current_user(current_user: Optional[User] = Depends(get_current_user)):
client = TestClient(app)
openapi_schema = {
"openapi": "3.0.2",
"info": {"title": "FastAPI", "version": "0.1.0"},
"paths": {
"/users/me": {
"get": {
"responses": {
"200": {
"description": "Successful Response",
"content": {"application/json": {"schema": {}}},
}
},
"summary": "Read Current User",
"operationId": "read_current_user_users_me_get",
"security": [{"APIKeyHeader": []}],
}
}
},
"components": {
"securitySchemes": {
"APIKeyHeader": {"type": "apiKey", "name": "key", "in": "header"}
}
},
}
def test_openapi_schema():
response = client.get("/openapi.json")
assert response.status_code == 200, response.text
assert response.json() == openapi_schema
def test_security_api_key():
response = client.get("/users/me", headers={"key": "secret"})
@@ -72,3 +41,32 @@ def test_security_api_key_no_key():
response = client.get("/users/me")
assert response.status_code == 200, response.text
assert response.json() == {"msg": "Create an account first"}
def test_openapi_schema():
response = client.get("/openapi.json")
assert response.status_code == 200, response.text
assert response.json() == {
"openapi": "3.0.2",
"info": {"title": "FastAPI", "version": "0.1.0"},
"paths": {
"/users/me": {
"get": {
"responses": {
"200": {
"description": "Successful Response",
"content": {"application/json": {"schema": {}}},
}
},
"summary": "Read Current User",
"operationId": "read_current_user_users_me_get",
"security": [{"APIKeyHeader": []}],
}
}
},
"components": {
"securitySchemes": {
"APIKeyHeader": {"type": "apiKey", "name": "key", "in": "header"}
}
},
}

View File

@@ -24,37 +24,6 @@ def read_current_user(current_user: User = Depends(get_current_user)):
client = TestClient(app)
openapi_schema = {
"openapi": "3.0.2",
"info": {"title": "FastAPI", "version": "0.1.0"},
"paths": {
"/users/me": {
"get": {
"responses": {
"200": {
"description": "Successful Response",
"content": {"application/json": {"schema": {}}},
}
},
"summary": "Read Current User",
"operationId": "read_current_user_users_me_get",
"security": [{"APIKeyQuery": []}],
}
}
},
"components": {
"securitySchemes": {
"APIKeyQuery": {"type": "apiKey", "name": "key", "in": "query"}
}
},
}
def test_openapi_schema():
response = client.get("/openapi.json")
assert response.status_code == 200, response.text
assert response.json() == openapi_schema
def test_security_api_key():
response = client.get("/users/me?key=secret")
@@ -66,3 +35,32 @@ def test_security_api_key_no_key():
response = client.get("/users/me")
assert response.status_code == 403, response.text
assert response.json() == {"detail": "Not authenticated"}
def test_openapi_schema():
response = client.get("/openapi.json")
assert response.status_code == 200, response.text
assert response.json() == {
"openapi": "3.0.2",
"info": {"title": "FastAPI", "version": "0.1.0"},
"paths": {
"/users/me": {
"get": {
"responses": {
"200": {
"description": "Successful Response",
"content": {"application/json": {"schema": {}}},
}
},
"summary": "Read Current User",
"operationId": "read_current_user_users_me_get",
"security": [{"APIKeyQuery": []}],
}
}
},
"components": {
"securitySchemes": {
"APIKeyQuery": {"type": "apiKey", "name": "key", "in": "query"}
}
},
}

View File

@@ -24,42 +24,6 @@ def read_current_user(current_user: User = Depends(get_current_user)):
client = TestClient(app)
openapi_schema = {
"openapi": "3.0.2",
"info": {"title": "FastAPI", "version": "0.1.0"},
"paths": {
"/users/me": {
"get": {
"responses": {
"200": {
"description": "Successful Response",
"content": {"application/json": {"schema": {}}},
}
},
"summary": "Read Current User",
"operationId": "read_current_user_users_me_get",
"security": [{"APIKeyQuery": []}],
}
}
},
"components": {
"securitySchemes": {
"APIKeyQuery": {
"type": "apiKey",
"name": "key",
"in": "query",
"description": "API Key Query",
}
}
},
}
def test_openapi_schema():
response = client.get("/openapi.json")
assert response.status_code == 200, response.text
assert response.json() == openapi_schema
def test_security_api_key():
response = client.get("/users/me?key=secret")
@@ -71,3 +35,37 @@ def test_security_api_key_no_key():
response = client.get("/users/me")
assert response.status_code == 403, response.text
assert response.json() == {"detail": "Not authenticated"}
def test_openapi_schema():
response = client.get("/openapi.json")
assert response.status_code == 200, response.text
assert response.json() == {
"openapi": "3.0.2",
"info": {"title": "FastAPI", "version": "0.1.0"},
"paths": {
"/users/me": {
"get": {
"responses": {
"200": {
"description": "Successful Response",
"content": {"application/json": {"schema": {}}},
}
},
"summary": "Read Current User",
"operationId": "read_current_user_users_me_get",
"security": [{"APIKeyQuery": []}],
}
}
},
"components": {
"securitySchemes": {
"APIKeyQuery": {
"type": "apiKey",
"name": "key",
"in": "query",
"description": "API Key Query",
}
}
},
}

View File

@@ -30,37 +30,6 @@ def read_current_user(current_user: Optional[User] = Depends(get_current_user)):
client = TestClient(app)
openapi_schema = {
"openapi": "3.0.2",
"info": {"title": "FastAPI", "version": "0.1.0"},
"paths": {
"/users/me": {
"get": {
"responses": {
"200": {
"description": "Successful Response",
"content": {"application/json": {"schema": {}}},
}
},
"summary": "Read Current User",
"operationId": "read_current_user_users_me_get",
"security": [{"APIKeyQuery": []}],
}
}
},
"components": {
"securitySchemes": {
"APIKeyQuery": {"type": "apiKey", "name": "key", "in": "query"}
}
},
}
def test_openapi_schema():
response = client.get("/openapi.json")
assert response.status_code == 200, response.text
assert response.json() == openapi_schema
def test_security_api_key():
response = client.get("/users/me?key=secret")
@@ -72,3 +41,32 @@ def test_security_api_key_no_key():
response = client.get("/users/me")
assert response.status_code == 200, response.text
assert response.json() == {"msg": "Create an account first"}
def test_openapi_schema():
response = client.get("/openapi.json")
assert response.status_code == 200, response.text
assert response.json() == {
"openapi": "3.0.2",
"info": {"title": "FastAPI", "version": "0.1.0"},
"paths": {
"/users/me": {
"get": {
"responses": {
"200": {
"description": "Successful Response",
"content": {"application/json": {"schema": {}}},
}
},
"summary": "Read Current User",
"operationId": "read_current_user_users_me_get",
"security": [{"APIKeyQuery": []}],
}
}
},
"components": {
"securitySchemes": {
"APIKeyQuery": {"type": "apiKey", "name": "key", "in": "query"}
}
},
}

View File

@@ -14,35 +14,6 @@ def read_current_user(credentials: HTTPAuthorizationCredentials = Security(secur
client = TestClient(app)
openapi_schema = {
"openapi": "3.0.2",
"info": {"title": "FastAPI", "version": "0.1.0"},
"paths": {
"/users/me": {
"get": {
"responses": {
"200": {
"description": "Successful Response",
"content": {"application/json": {"schema": {}}},
}
},
"summary": "Read Current User",
"operationId": "read_current_user_users_me_get",
"security": [{"HTTPBase": []}],
}
}
},
"components": {
"securitySchemes": {"HTTPBase": {"type": "http", "scheme": "Other"}}
},
}
def test_openapi_schema():
response = client.get("/openapi.json")
assert response.status_code == 200, response.text
assert response.json() == openapi_schema
def test_security_http_base():
response = client.get("/users/me", headers={"Authorization": "Other foobar"})
@@ -54,3 +25,30 @@ def test_security_http_base_no_credentials():
response = client.get("/users/me")
assert response.status_code == 403, response.text
assert response.json() == {"detail": "Not authenticated"}
def test_openapi_schema():
response = client.get("/openapi.json")
assert response.status_code == 200, response.text
assert response.json() == {
"openapi": "3.0.2",
"info": {"title": "FastAPI", "version": "0.1.0"},
"paths": {
"/users/me": {
"get": {
"responses": {
"200": {
"description": "Successful Response",
"content": {"application/json": {"schema": {}}},
}
},
"summary": "Read Current User",
"operationId": "read_current_user_users_me_get",
"security": [{"HTTPBase": []}],
}
}
},
"components": {
"securitySchemes": {"HTTPBase": {"type": "http", "scheme": "Other"}}
},
}

View File

@@ -14,41 +14,6 @@ def read_current_user(credentials: HTTPAuthorizationCredentials = Security(secur
client = TestClient(app)
openapi_schema = {
"openapi": "3.0.2",
"info": {"title": "FastAPI", "version": "0.1.0"},
"paths": {
"/users/me": {
"get": {
"responses": {
"200": {
"description": "Successful Response",
"content": {"application/json": {"schema": {}}},
}
},
"summary": "Read Current User",
"operationId": "read_current_user_users_me_get",
"security": [{"HTTPBase": []}],
}
}
},
"components": {
"securitySchemes": {
"HTTPBase": {
"type": "http",
"scheme": "Other",
"description": "Other Security Scheme",
}
}
},
}
def test_openapi_schema():
response = client.get("/openapi.json")
assert response.status_code == 200, response.text
assert response.json() == openapi_schema
def test_security_http_base():
response = client.get("/users/me", headers={"Authorization": "Other foobar"})
@@ -60,3 +25,36 @@ def test_security_http_base_no_credentials():
response = client.get("/users/me")
assert response.status_code == 403, response.text
assert response.json() == {"detail": "Not authenticated"}
def test_openapi_schema():
response = client.get("/openapi.json")
assert response.status_code == 200, response.text
assert response.json() == {
"openapi": "3.0.2",
"info": {"title": "FastAPI", "version": "0.1.0"},
"paths": {
"/users/me": {
"get": {
"responses": {
"200": {
"description": "Successful Response",
"content": {"application/json": {"schema": {}}},
}
},
"summary": "Read Current User",
"operationId": "read_current_user_users_me_get",
"security": [{"HTTPBase": []}],
}
}
},
"components": {
"securitySchemes": {
"HTTPBase": {
"type": "http",
"scheme": "Other",
"description": "Other Security Scheme",
}
}
},
}

View File

@@ -20,35 +20,6 @@ def read_current_user(
client = TestClient(app)
openapi_schema = {
"openapi": "3.0.2",
"info": {"title": "FastAPI", "version": "0.1.0"},
"paths": {
"/users/me": {
"get": {
"responses": {
"200": {
"description": "Successful Response",
"content": {"application/json": {"schema": {}}},
}
},
"summary": "Read Current User",
"operationId": "read_current_user_users_me_get",
"security": [{"HTTPBase": []}],
}
}
},
"components": {
"securitySchemes": {"HTTPBase": {"type": "http", "scheme": "Other"}}
},
}
def test_openapi_schema():
response = client.get("/openapi.json")
assert response.status_code == 200, response.text
assert response.json() == openapi_schema
def test_security_http_base():
response = client.get("/users/me", headers={"Authorization": "Other foobar"})
@@ -60,3 +31,30 @@ def test_security_http_base_no_credentials():
response = client.get("/users/me")
assert response.status_code == 200, response.text
assert response.json() == {"msg": "Create an account first"}
def test_openapi_schema():
response = client.get("/openapi.json")
assert response.status_code == 200, response.text
assert response.json() == {
"openapi": "3.0.2",
"info": {"title": "FastAPI", "version": "0.1.0"},
"paths": {
"/users/me": {
"get": {
"responses": {
"200": {
"description": "Successful Response",
"content": {"application/json": {"schema": {}}},
}
},
"summary": "Read Current User",
"operationId": "read_current_user_users_me_get",
"security": [{"HTTPBase": []}],
}
}
},
"components": {
"securitySchemes": {"HTTPBase": {"type": "http", "scheme": "Other"}}
},
}

View File

@@ -19,35 +19,6 @@ def read_current_user(credentials: Optional[HTTPBasicCredentials] = Security(sec
client = TestClient(app)
openapi_schema = {
"openapi": "3.0.2",
"info": {"title": "FastAPI", "version": "0.1.0"},
"paths": {
"/users/me": {
"get": {
"responses": {
"200": {
"description": "Successful Response",
"content": {"application/json": {"schema": {}}},
}
},
"summary": "Read Current User",
"operationId": "read_current_user_users_me_get",
"security": [{"HTTPBasic": []}],
}
}
},
"components": {
"securitySchemes": {"HTTPBasic": {"type": "http", "scheme": "basic"}}
},
}
def test_openapi_schema():
response = client.get("/openapi.json")
assert response.status_code == 200, response.text
assert response.json() == openapi_schema
def test_security_http_basic():
response = client.get("/users/me", auth=("john", "secret"))
@@ -77,3 +48,30 @@ def test_security_http_basic_non_basic_credentials():
assert response.status_code == 401, response.text
assert response.headers["WWW-Authenticate"] == "Basic"
assert response.json() == {"detail": "Invalid authentication credentials"}
def test_openapi_schema():
response = client.get("/openapi.json")
assert response.status_code == 200, response.text
assert response.json() == {
"openapi": "3.0.2",
"info": {"title": "FastAPI", "version": "0.1.0"},
"paths": {
"/users/me": {
"get": {
"responses": {
"200": {
"description": "Successful Response",
"content": {"application/json": {"schema": {}}},
}
},
"summary": "Read Current User",
"operationId": "read_current_user_users_me_get",
"security": [{"HTTPBasic": []}],
}
}
},
"components": {
"securitySchemes": {"HTTPBasic": {"type": "http", "scheme": "basic"}}
},
}

View File

@@ -16,35 +16,6 @@ def read_current_user(credentials: HTTPBasicCredentials = Security(security)):
client = TestClient(app)
openapi_schema = {
"openapi": "3.0.2",
"info": {"title": "FastAPI", "version": "0.1.0"},
"paths": {
"/users/me": {
"get": {
"responses": {
"200": {
"description": "Successful Response",
"content": {"application/json": {"schema": {}}},
}
},
"summary": "Read Current User",
"operationId": "read_current_user_users_me_get",
"security": [{"HTTPBasic": []}],
}
}
},
"components": {
"securitySchemes": {"HTTPBasic": {"type": "http", "scheme": "basic"}}
},
}
def test_openapi_schema():
response = client.get("/openapi.json")
assert response.status_code == 200, response.text
assert response.json() == openapi_schema
def test_security_http_basic():
response = client.get("/users/me", auth=("john", "secret"))
@@ -75,3 +46,30 @@ def test_security_http_basic_non_basic_credentials():
assert response.status_code == 401, response.text
assert response.headers["WWW-Authenticate"] == 'Basic realm="simple"'
assert response.json() == {"detail": "Invalid authentication credentials"}
def test_openapi_schema():
response = client.get("/openapi.json")
assert response.status_code == 200, response.text
assert response.json() == {
"openapi": "3.0.2",
"info": {"title": "FastAPI", "version": "0.1.0"},
"paths": {
"/users/me": {
"get": {
"responses": {
"200": {
"description": "Successful Response",
"content": {"application/json": {"schema": {}}},
}
},
"summary": "Read Current User",
"operationId": "read_current_user_users_me_get",
"security": [{"HTTPBasic": []}],
}
}
},
"components": {
"securitySchemes": {"HTTPBasic": {"type": "http", "scheme": "basic"}}
},
}

View File

@@ -16,41 +16,6 @@ def read_current_user(credentials: HTTPBasicCredentials = Security(security)):
client = TestClient(app)
openapi_schema = {
"openapi": "3.0.2",
"info": {"title": "FastAPI", "version": "0.1.0"},
"paths": {
"/users/me": {
"get": {
"responses": {
"200": {
"description": "Successful Response",
"content": {"application/json": {"schema": {}}},
}
},
"summary": "Read Current User",
"operationId": "read_current_user_users_me_get",
"security": [{"HTTPBasic": []}],
}
}
},
"components": {
"securitySchemes": {
"HTTPBasic": {
"type": "http",
"scheme": "basic",
"description": "HTTPBasic scheme",
}
}
},
}
def test_openapi_schema():
response = client.get("/openapi.json")
assert response.status_code == 200, response.text
assert response.json() == openapi_schema
def test_security_http_basic():
response = client.get("/users/me", auth=("john", "secret"))
@@ -81,3 +46,36 @@ def test_security_http_basic_non_basic_credentials():
assert response.status_code == 401, response.text
assert response.headers["WWW-Authenticate"] == 'Basic realm="simple"'
assert response.json() == {"detail": "Invalid authentication credentials"}
def test_openapi_schema():
response = client.get("/openapi.json")
assert response.status_code == 200, response.text
assert response.json() == {
"openapi": "3.0.2",
"info": {"title": "FastAPI", "version": "0.1.0"},
"paths": {
"/users/me": {
"get": {
"responses": {
"200": {
"description": "Successful Response",
"content": {"application/json": {"schema": {}}},
}
},
"summary": "Read Current User",
"operationId": "read_current_user_users_me_get",
"security": [{"HTTPBasic": []}],
}
}
},
"components": {
"securitySchemes": {
"HTTPBasic": {
"type": "http",
"scheme": "basic",
"description": "HTTPBasic scheme",
}
}
},
}

Some files were not shown because too many files have changed in this diff Show More