Compare commits

...

61 Commits

Author SHA1 Message Date
Sebastián Ramírez
63a5ffcf57 🔖 Release version 0.87.0 2022-11-13 22:36:53 +01:00
Sebastián Ramírez
da1c67338f 🔖 Release version 0.87.0 2022-11-13 22:27:53 +01:00
Sebastián Ramírez
46a509649d 📝 Update release notes 2022-11-13 22:26:43 +01:00
github-actions
1d416c4c53 📝 Update release notes 2022-11-13 20:51:19 +00:00
Sebastián Ramírez
89095eba4f ⬆️ Upgrade and relax dependencies for extras "all" (#5634) 2022-11-13 21:50:43 +01:00
github-actions
5a63f660de 📝 Update release notes 2022-11-13 20:30:05 +00:00
Sebastián Ramírez
9c483505e3 ✏️ Tweak Help FastAPI from PR review after merging (#5633) 2022-11-13 20:29:23 +00:00
github-actions
ad0e923fed 📝 Update release notes 2022-11-13 20:29:15 +00:00
Pax
fc717f84ff ✏️ Clarify docs on CORS (#5627) 2022-11-13 21:28:37 +01:00
Sebastián Ramírez
b1204a9b62 📝 Update release notes 2022-11-13 21:23:22 +01:00
github-actions
16630bf54d 📝 Update release notes 2022-11-13 19:34:43 +00:00
Sebastián Ramírez
50ea75ae98 📝 Update Help FastAPI: Help Maintain FastAPI (#5632) 2022-11-13 20:34:09 +01:00
github-actions
a0852e2f53 📝 Update release notes 2022-11-13 18:19:43 +00:00
Sebastián Ramírez
fa74093440 Use Ruff for linting (#5630) 2022-11-13 19:19:04 +01:00
github-actions
bcd9ab95e1 📝 Update release notes 2022-11-13 16:11:29 +00:00
Sebastián Ramírez
d537ee93d7 Re-export Starlette's WebSocketException and add it to docs (#5629) 2022-11-13 17:10:54 +01:00
github-actions
1c93d5523a 📝 Update release notes 2022-11-13 15:20:44 +00:00
Sebastián Ramírez
f92f87d379 📝 Update references to Requests for tests to HTTPX, and add HTTPX to extras (#5628) 2022-11-13 16:20:05 +01:00
github-actions
57141ccac4 📝 Update release notes 2022-11-13 14:35:13 +00:00
github-actions
46b903f70b 📝 Update release notes 2022-11-13 14:26:43 +00:00
Paweł Rubin
fdbd48be5f ⬆ Upgrade Starlette to 0.21.0, including the new [TestClient based on HTTPX](https://github.com/encode/starlette/releases/tag/0.21.0) (#5471)
Co-authored-by: Paweł Rubin <pawel.rubin@ocado.com>
Co-authored-by: Sebastián Ramírez <tiangolo@gmail.com>
2022-11-13 15:26:09 +01:00
github-actions
5f67ac6fd6 📝 Update release notes 2022-11-13 14:04:27 +00:00
axel584
ba5310f731 🌐 Add French translation for docs/fr/docs/advanced/additional-status-code.md (#5477)
Co-authored-by: Julian Maurin <julian.maurin.perso@pm.me>
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Co-authored-by: Sebastián Ramírez <tiangolo@gmail.com>
2022-11-13 14:03:48 +00:00
github-actions
f57ccd8ef3 📝 Update release notes 2022-11-13 13:59:55 +00:00
Bruno Artur Torres Lopes Pereira
c040e3602a 🌐 Add Portuguese translation for docs/pt/docs/tutorial/request-forms-and-files.md (#5579)
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2022-11-13 14:59:16 +01:00
github-actions
86d4073632 📝 Update release notes 2022-11-13 13:59:09 +00:00
Ryusei Ishikawa
59208e4ddc 🌐 Add Japanese translation for docs/ja/docs/advanced/websockets.md (#4983)
Co-authored-by: tokusumi <41147016+tokusumi@users.noreply.github.com>
Co-authored-by: komtaki <39375566+komtaki@users.noreply.github.com>
2022-11-13 14:58:31 +01:00
nikkie
0781a91d19 🌐 Fix highlight lines for Japanese translation for docs/tutorial/query-params.md (#2969)
Co-authored-by: Sebastián Ramírez <tiangolo@gmail.com>
Fix https://github.com/tiangolo/fastapi/issues/2966
2022-11-13 14:57:52 +01:00
github-actions
41735d2de9 📝 Update release notes 2022-11-10 12:22:58 +00:00
Sebastián Ramírez
a0c717d784 🛠 Add Arabic issue number to Notify Translations GitHub Action (#5610) 2022-11-10 12:22:25 +00:00
github-actions
0ed9ca78bc 📝 Update release notes 2022-11-10 11:16:30 +00:00
dependabot[bot]
678d35994a ⬆ Bump dawidd6/action-download-artifact from 2.24.1 to 2.24.2 (#5609)
Bumps [dawidd6/action-download-artifact](https://github.com/dawidd6/action-download-artifact) from 2.24.1 to 2.24.2.
- [Release notes](https://github.com/dawidd6/action-download-artifact/releases)
- [Commits](https://github.com/dawidd6/action-download-artifact/compare/v2.24.1...v2.24.2)

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

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-11-10 12:15:55 +01:00
github-actions
a7edd0b80d 📝 Update release notes 2022-11-09 11:46:50 +00:00
dependabot[bot]
f36d2e2b2b ⬆ Bump dawidd6/action-download-artifact from 2.24.0 to 2.24.1 (#5603)
Bumps [dawidd6/action-download-artifact](https://github.com/dawidd6/action-download-artifact) from 2.24.0 to 2.24.1.
- [Release notes](https://github.com/dawidd6/action-download-artifact/releases)
- [Commits](https://github.com/dawidd6/action-download-artifact/compare/v2.24.0...v2.24.1)

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

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-11-09 12:46:14 +01:00
github-actions
5f0e095689 📝 Update release notes 2022-11-04 19:20:29 +00:00
Sebastián Ramírez
ccd242348f 🔖 Release version 0.86.0 2022-11-03 22:17:44 +01:00
Sebastián Ramírez
066cfae56e 📝 Update release notes 2022-11-03 22:16:37 +01:00
github-actions
51e768e85a 📝 Update release notes 2022-11-03 21:13:25 +00:00
Sebastián Ramírez
10fbfd6dc7 ⬆ Add Python 3.11 to the officially supported versions (#5587) 2022-11-03 21:12:43 +00:00
Sebastián Ramírez
85e602d7cc 📝 Update release notes 2022-11-03 22:05:26 +01:00
github-actions
fbc13d1f5b 📝 Update release notes 2022-11-03 21:01:09 +00:00
Vivek Ashokkumar
d4e2bdb33a ✏ Fix typo in docs/en/docs/tutorial/security/oauth2-jwt.md (#5584)
Co-authored-by: Sebastián Ramírez <tiangolo@gmail.com>
2022-11-03 21:00:28 +00:00
github-actions
8a5befd099 📝 Update release notes 2022-11-03 20:56:13 +00:00
Sebastián Ramírez
9a442c9730 👷 Update FastAPI People to exclude bots: pre-commit-ci, dependabot (#5586) 2022-11-03 20:55:32 +00:00
Sebastián Ramírez
4fa4965beb 📝 Update coverage badge to use Samuel Colvin's Smokeshow (#5585) 2022-11-03 21:55:00 +01:00
github-actions
5f4680201c 📝 Update release notes 2022-11-03 20:06:36 +00:00
Irfanuddin Shafi Ahmed
5cd99a9517 🎨 Format OpenAPI JSON in test_starlette_exception.py (#5379) 2022-11-03 21:06:00 +01:00
github-actions
b6ea8414a9 📝 Update release notes 2022-11-03 20:01:17 +00:00
Sebastián Ramírez
be3e29fb3c 👷 Switch from Codecov to Smokeshow plus pytest-cov to pure coverage for internal tests (#5583) 2022-11-03 21:00:29 +01:00
github-actions
cf730518bc 📝 Update release notes 2022-11-03 12:27:24 +00:00
Sebastián Ramírez
d62f5c1b28 Enable tests for Python 3.11 (#4881) 2022-11-03 13:26:48 +01:00
github-actions
4cf9075809 📝 Update release notes 2022-11-03 12:08:23 +00:00
ZHCai
a0c677ef0d 🌐 Update wording in Chinese translation for docs/zh/docs/python-types.md (#5416) 2022-11-03 13:07:49 +01:00
github-actions
54aa27ca07 📝 Update release notes 2022-11-03 12:07:29 +00:00
Adrian Garcia Badaracco
ac9f56ea5e 🐛 Close FormData (uploaded files) after the request is done (#5465)
Co-authored-by: Sebastián Ramírez <tiangolo@gmail.com>
2022-11-03 13:06:52 +01:00
github-actions
ed9425ef50 📝 Update release notes 2022-11-03 11:52:17 +00:00
Vladislav Kramorenko
9a85535e7f 🌐 Add Russian translation for docs/ru/docs/deployment/index.md (#5336)
Co-authored-by: Sebastián Ramírez <tiangolo@gmail.com>
2022-11-03 11:51:44 +00:00
github-actions
c9308cf070 📝 Update release notes 2022-11-03 11:51:26 +00:00
jaystone776
058cb6e88e 🌐 Update Chinese translation for docs/tutorial/security/oauth2-jwt.md (#3846)
Co-authored-by: Sebastián Ramírez <tiangolo@gmail.com>
2022-11-03 11:50:48 +00:00
github-actions
876ea7978f 📝 Update release notes 2022-11-03 11:45:26 +00:00
github-actions[bot]
da0bfd22aa 👥 Update FastAPI People (#5571)
Co-authored-by: github-actions <github-actions@github.com>
2022-11-03 12:44:51 +01:00
107 changed files with 1317 additions and 448 deletions

View File

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

View File

@@ -18,3 +18,4 @@ uz: 4883
sv: 5146
he: 5157
ta: 5434
ar: 3349

View File

@@ -433,7 +433,7 @@ if __name__ == "__main__":
)
authors = {**issue_authors, **pr_authors}
maintainers_logins = {"tiangolo"}
bot_names = {"codecov", "github-actions"}
bot_names = {"codecov", "github-actions", "pre-commit-ci", "dependabot"}
maintainers = []
for login in maintainers_logins:
user = authors[login]

View File

@@ -12,7 +12,7 @@ jobs:
steps:
- uses: actions/checkout@v3
- name: Download Artifact Docs
uses: dawidd6/action-download-artifact@v2.24.0
uses: dawidd6/action-download-artifact@v2.24.2
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
workflow: build-docs.yml

35
.github/workflows/smokeshow.yml vendored Normal file
View File

@@ -0,0 +1,35 @@
name: Smokeshow
on:
workflow_run:
workflows: [Test]
types: [completed]
permissions:
statuses: write
jobs:
smokeshow:
if: ${{ github.event.workflow_run.conclusion == 'success' }}
runs-on: ubuntu-latest
steps:
- uses: actions/setup-python@v4
with:
python-version: '3.9'
- run: pip install smokeshow
- uses: dawidd6/action-download-artifact@v2.24.2
with:
workflow: test.yml
commit: ${{ github.event.workflow_run.head_sha }}
- run: smokeshow upload coverage-html
env:
SMOKESHOW_GITHUB_STATUS_DESCRIPTION: Coverage {coverage-percentage}
SMOKESHOW_GITHUB_COVERAGE_THRESHOLD: 100
SMOKESHOW_GITHUB_CONTEXT: coverage
SMOKESHOW_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
SMOKESHOW_GITHUB_PR_HEAD_SHA: ${{ github.event.workflow_run.head_sha }}
SMOKESHOW_AUTH_KEY: ${{ secrets.SMOKESHOW_AUTH_KEY }}

View File

@@ -12,7 +12,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: ["3.7", "3.8", "3.9", "3.10"]
python-version: ["3.7", "3.8", "3.9", "3.10", "3.11"]
fail-fast: false
steps:
@@ -31,7 +31,43 @@ jobs:
run: pip install -e .[all,dev,doc,test]
- name: Lint
run: bash scripts/lint.sh
- run: mkdir coverage
- name: Test
run: bash scripts/test.sh
- name: Upload coverage
uses: codecov/codecov-action@v3
env:
COVERAGE_FILE: coverage/.coverage.${{ runner.os }}-py${{ matrix.python-version }}
CONTEXT: ${{ runner.os }}-py${{ matrix.python-version }}
- name: Store coverage files
uses: actions/upload-artifact@v3
with:
name: coverage
path: coverage
coverage-combine:
needs: [test]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-python@v4
with:
python-version: '3.8'
- name: Get coverage files
uses: actions/download-artifact@v3
with:
name: coverage
path: coverage
- run: pip install coverage[toml]
- run: ls -la coverage
- run: coverage combine coverage
- run: coverage report
- run: coverage html --show-contexts --title "Coverage for ${{ github.sha }}"
- name: Store coverage HTML
uses: actions/upload-artifact@v3
with:
name: coverage-html
path: htmlcov

View File

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

View File

@@ -8,8 +8,8 @@
<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://codecov.io/gh/tiangolo/fastapi" target="_blank">
<img src="https://img.shields.io/codecov/c/github/tiangolo/fastapi?color=%2334D058" alt="Coverage">
<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">
@@ -427,7 +427,7 @@ For a more complete example including more features, see the <a href="https://fa
* **GraphQL** integration with <a href="https://strawberry.rocks" class="external-link" target="_blank">Strawberry</a> and other libraries.
* Many extra features (thanks to Starlette) as:
* **WebSockets**
* extremely easy tests based on `requests` and `pytest`
* extremely easy tests based on HTTPX and `pytest`
* **CORS**
* **Cookie Sessions**
* ...and more.
@@ -447,7 +447,7 @@ Used by Pydantic:
Used by Starlette:
* <a href="https://requests.readthedocs.io" target="_blank"><code>requests</code></a> - Required if you want to use the `TestClient`.
* <a href="https://www.python-httpx.org" target="_blank"><code>httpx</code></a> - Required if you want to use the `TestClient`.
* <a href="https://jinja.palletsprojects.com" target="_blank"><code>jinja2</code></a> - Required if you want to use the default template configuration.
* <a href="https://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.

View File

@@ -446,7 +446,7 @@ Used by Pydantic:
Used by Starlette:
* <a href="https://requests.readthedocs.io" target="_blank"><code>requests</code></a> - Required if you want to use the `TestClient`.
* <a href="https://www.python-httpx.org" target="_blank"><code>httpx</code></a> - Required if you want to use the `TestClient`.
* <a href="https://jinja.palletsprojects.com" target="_blank"><code>jinja2</code></a> - Required if you want to use the default template configuration.
* <a href="https://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.

View File

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

View File

@@ -445,7 +445,7 @@ Used by Pydantic:
Used by Starlette:
* <a href="https://requests.readthedocs.io" target="_blank"><code>requests</code></a> - Required if you want to use the `TestClient`.
* <a href="https://www.python-httpx.org" target="_blank"><code>httpx</code></a> - Required if you want to use the `TestClient`.
* <a href="https://jinja.palletsprojects.com" target="_blank"><code>jinja2</code></a> - Required if you want to use the default template configuration.
* <a href="https://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.

View File

@@ -32,13 +32,13 @@ sponsors:
- login: VincentParedes
avatarUrl: https://avatars.githubusercontent.com/u/103889729?v=4
url: https://github.com/VincentParedes
- - login: getsentry
avatarUrl: https://avatars.githubusercontent.com/u/1396951?v=4
url: https://github.com/getsentry
- - login: InesIvanova
avatarUrl: https://avatars.githubusercontent.com/u/22920417?u=409882ec1df6dbd77455788bb383a8de223dbf6f&v=4
url: https://github.com/InesIvanova
- - login: zopyx
avatarUrl: https://avatars.githubusercontent.com/u/594239?u=8e5ce882664f47fd61002bed51718c78c3799d24&v=4
url: https://github.com/zopyx
- login: SendCloud
- - login: SendCloud
avatarUrl: https://avatars.githubusercontent.com/u/7831959?v=4
url: https://github.com/SendCloud
- login: mercedes-benz
@@ -62,21 +62,21 @@ sponsors:
- login: Trivie
avatarUrl: https://avatars.githubusercontent.com/u/8161763?v=4
url: https://github.com/Trivie
- login: wdwinslow
avatarUrl: https://avatars.githubusercontent.com/u/11562137?u=dc01daafb354135603a263729e3d26d939c0c452&v=4
url: https://github.com/wdwinslow
- - login: moellenbeck
avatarUrl: https://avatars.githubusercontent.com/u/169372?v=4
url: https://github.com/moellenbeck
- login: AccentDesign
avatarUrl: https://avatars.githubusercontent.com/u/2429332?v=4
url: https://github.com/AccentDesign
- login: RodneyU215
avatarUrl: https://avatars.githubusercontent.com/u/3329665?u=ec6a9adf8e7e8e306eed7d49687c398608d1604f&v=4
url: https://github.com/RodneyU215
- login: tizz98
avatarUrl: https://avatars.githubusercontent.com/u/5739698?u=f095a3659e3a8e7c69ccd822696990b521ea25f9&v=4
url: https://github.com/tizz98
- login: Vikka
- login: dorianturba
avatarUrl: https://avatars.githubusercontent.com/u/9381120?u=4bfc7032a824d1ed1994aa8256dfa597c8f187ad&v=4
url: https://github.com/Vikka
url: https://github.com/dorianturba
- login: jmaralc
avatarUrl: https://avatars.githubusercontent.com/u/21101214?u=b15a9f07b7cbf6c9dcdbcb6550bbd2c52f55aa50&v=4
url: https://github.com/jmaralc
@@ -89,13 +89,19 @@ sponsors:
- login: DelfinaCare
avatarUrl: https://avatars.githubusercontent.com/u/83734439?v=4
url: https://github.com/DelfinaCare
- - login: karelhusa
avatarUrl: https://avatars.githubusercontent.com/u/11407706?u=4f787cffc30ea198b15935c751940f0b48a26a17&v=4
url: https://github.com/karelhusa
- - login: povilasb
avatarUrl: https://avatars.githubusercontent.com/u/1213442?u=b11f58ed6ceea6e8297c9b310030478ebdac894d&v=4
url: https://github.com/povilasb
- login: primer-io
avatarUrl: https://avatars.githubusercontent.com/u/62146168?v=4
url: https://github.com/primer-io
- - login: A-Edge
- - login: DucNgn
avatarUrl: https://avatars.githubusercontent.com/u/43587865?u=a9f9f61569aebdc0ce74df85b8cc1a219a7ab570&v=4
url: https://github.com/DucNgn
- login: A-Edge
avatarUrl: https://avatars.githubusercontent.com/u/59514131?v=4
url: https://github.com/A-Edge
- - login: Kludex
@@ -113,6 +119,9 @@ sponsors:
- login: kamalgill
avatarUrl: https://avatars.githubusercontent.com/u/133923?u=0df9181d97436ce330e9acf90ab8a54b7022efe7&v=4
url: https://github.com/kamalgill
- login: gazpachoking
avatarUrl: https://avatars.githubusercontent.com/u/187133?v=4
url: https://github.com/gazpachoking
- login: dekoza
avatarUrl: https://avatars.githubusercontent.com/u/210980?u=c03c78a8ae1039b500dfe343665536ebc51979b2&v=4
url: https://github.com/dekoza
@@ -170,15 +179,15 @@ sponsors:
- login: zsinx6
avatarUrl: https://avatars.githubusercontent.com/u/3532625?u=ba75a5dc744d1116ccfeaaf30d41cb2fe81fe8dd&v=4
url: https://github.com/zsinx6
- login: bauyrzhanospan
avatarUrl: https://avatars.githubusercontent.com/u/3536037?u=25c86201d0212497aefcc1688cccf509057a1dc4&v=4
url: https://github.com/bauyrzhanospan
- login: MarekBleschke
avatarUrl: https://avatars.githubusercontent.com/u/3616870?v=4
url: https://github.com/MarekBleschke
- login: aacayaco
avatarUrl: https://avatars.githubusercontent.com/u/3634801?u=eaadda178c964178fcb64886f6c732172c8f8219&v=4
url: https://github.com/aacayaco
- login: anomaly
avatarUrl: https://avatars.githubusercontent.com/u/3654837?v=4
url: https://github.com/anomaly
- login: peterHoburg
avatarUrl: https://avatars.githubusercontent.com/u/3860655?u=f55f47eb2d6a9b495e806ac5a044e3ae01ccc1fa&v=4
url: https://github.com/peterHoburg
@@ -194,15 +203,15 @@ sponsors:
- login: oliverxchen
avatarUrl: https://avatars.githubusercontent.com/u/4471774?u=534191f25e32eeaadda22dfab4b0a428733d5489&v=4
url: https://github.com/oliverxchen
- login: CINOAdam
avatarUrl: https://avatars.githubusercontent.com/u/4728508?u=76ef23f06ae7c604e009873bc27cf0ea9ba738c9&v=4
url: https://github.com/CINOAdam
- login: ScrimForever
avatarUrl: https://avatars.githubusercontent.com/u/5040124?u=091ec38bfe16d6e762099e91309b59f248616a65&v=4
url: https://github.com/ScrimForever
- login: ennui93
avatarUrl: https://avatars.githubusercontent.com/u/5300907?u=5b5452725ddb391b2caaebf34e05aba873591c3a&v=4
url: https://github.com/ennui93
- login: ternaus
avatarUrl: https://avatars.githubusercontent.com/u/5481618?u=fabc8d75c921b3380126adb5a931c5da6e7db04f&v=4
url: https://github.com/ternaus
- login: Yaleesa
avatarUrl: https://avatars.githubusercontent.com/u/6135475?v=4
url: https://github.com/Yaleesa
@@ -216,7 +225,7 @@ sponsors:
avatarUrl: https://avatars.githubusercontent.com/u/6347418?u=98f5918b32e214a168a2f5d59b0b8ebdf57dca0d&v=4
url: https://github.com/pkucmus
- login: s3ich4n
avatarUrl: https://avatars.githubusercontent.com/u/6926298?u=ba3025d698e1c986655e776ae383a3d60d9d578e&v=4
avatarUrl: https://avatars.githubusercontent.com/u/6926298?u=6690c5403bc1d9a1837886defdc5256e9a43b1db&v=4
url: https://github.com/s3ich4n
- login: Rehket
avatarUrl: https://avatars.githubusercontent.com/u/7015688?u=3afb0ba200feebbc7f958950e92db34df2a3c172&v=4
@@ -227,6 +236,9 @@ sponsors:
- login: Shackelford-Arden
avatarUrl: https://avatars.githubusercontent.com/u/7362263?v=4
url: https://github.com/Shackelford-Arden
- login: wdwinslow
avatarUrl: https://avatars.githubusercontent.com/u/11562137?u=dc01daafb354135603a263729e3d26d939c0c452&v=4
url: https://github.com/wdwinslow
- login: Ge0f3
avatarUrl: https://avatars.githubusercontent.com/u/11887760?u=ccd80f1ac36dcb8517ef5c4e702e8cc5a80cad2f&v=4
url: https://github.com/Ge0f3
@@ -239,6 +251,9 @@ sponsors:
- login: dannywade
avatarUrl: https://avatars.githubusercontent.com/u/13680237?u=418ee985bd41577b20fde81417fb2d901e875e8a&v=4
url: https://github.com/dannywade
- login: khadrawy
avatarUrl: https://avatars.githubusercontent.com/u/13686061?u=59f25ef42ecf04c22657aac4238ce0e2d3d30304&v=4
url: https://github.com/khadrawy
- login: pablonnaoji
avatarUrl: https://avatars.githubusercontent.com/u/15187159?u=afc15bd5a4ba9c5c7206bbb1bcaeef606a0932e0&v=4
url: https://github.com/pablonnaoji
@@ -302,20 +317,17 @@ sponsors:
- login: rafsaf
avatarUrl: https://avatars.githubusercontent.com/u/51059348?u=f8f0d6d6e90fac39fa786228158ba7f013c74271&v=4
url: https://github.com/rafsaf
- login: yezz123
avatarUrl: https://avatars.githubusercontent.com/u/52716203?u=636b4f79645176df4527dd45c12d5dbb5a4193cf&v=4
url: https://github.com/yezz123
- login: dudikbender
avatarUrl: https://avatars.githubusercontent.com/u/53487583?u=494f85229115076121b3639a3806bbac1c6ae7f6&v=4
url: https://github.com/dudikbender
- login: llamington
avatarUrl: https://avatars.githubusercontent.com/u/54869395?u=42ea59b76f49449f41a4d106bb65a130797e8d7c&v=4
url: https://github.com/llamington
- login: dazeddd
avatarUrl: https://avatars.githubusercontent.com/u/59472056?u=7a1b668449bf8b448db13e4c575576d24d7d658b&v=4
url: https://github.com/dazeddd
- login: yakkonaut
avatarUrl: https://avatars.githubusercontent.com/u/60633704?u=90a71fd631aa998ba4a96480788f017c9904e07b&v=4
url: https://github.com/yakkonaut
- login: patsatsia
avatarUrl: https://avatars.githubusercontent.com/u/61111267?u=419516384f798a35e9d9e2dd81282cc46c151b58&v=4
avatarUrl: https://avatars.githubusercontent.com/u/61111267?u=3271b85f7a37b479c8d0ae0a235182e83c166edf&v=4
url: https://github.com/patsatsia
- login: predictionmachine
avatarUrl: https://avatars.githubusercontent.com/u/63719559?v=4
@@ -332,9 +344,15 @@ sponsors:
- login: dotlas
avatarUrl: https://avatars.githubusercontent.com/u/88832003?v=4
url: https://github.com/dotlas
- login: programvx
avatarUrl: https://avatars.githubusercontent.com/u/96057906?v=4
url: https://github.com/programvx
- login: pyt3h
avatarUrl: https://avatars.githubusercontent.com/u/99658549?v=4
url: https://github.com/pyt3h
- login: Dagmaara
avatarUrl: https://avatars.githubusercontent.com/u/115501964?v=4
url: https://github.com/Dagmaara
- - login: linux-china
avatarUrl: https://avatars.githubusercontent.com/u/46711?v=4
url: https://github.com/linux-china
@@ -419,6 +437,9 @@ sponsors:
- login: pawamoy
avatarUrl: https://avatars.githubusercontent.com/u/3999221?u=b030e4c89df2f3a36bc4710b925bdeb6745c9856&v=4
url: https://github.com/pawamoy
- login: nikeee
avatarUrl: https://avatars.githubusercontent.com/u/4068864?u=63f8eee593f25138e0f1032ef442e9ad24907d4c&v=4
url: https://github.com/nikeee
- login: Alisa-lisa
avatarUrl: https://avatars.githubusercontent.com/u/4137964?u=e7e393504f554f4ff15863a1e01a5746863ef9ce&v=4
url: https://github.com/Alisa-lisa
@@ -426,7 +447,7 @@ sponsors:
avatarUrl: https://avatars.githubusercontent.com/u/4472301?v=4
url: https://github.com/danielunderwood
- login: unredundant
avatarUrl: https://avatars.githubusercontent.com/u/5607577?u=57dd0023365bec03f4fc566df6b81bc0a264a47d&v=4
avatarUrl: https://avatars.githubusercontent.com/u/5607577?u=1ffbf39f5bb8736b75c0d235707d6e8f803725c5&v=4
url: https://github.com/unredundant
- login: Baghdady92
avatarUrl: https://avatars.githubusercontent.com/u/5708590?v=4
@@ -434,6 +455,9 @@ sponsors:
- login: holec
avatarUrl: https://avatars.githubusercontent.com/u/6438041?u=f5af71ec85b3a9d7b8139cb5af0512b02fa9ab1e&v=4
url: https://github.com/holec
- login: mattwelke
avatarUrl: https://avatars.githubusercontent.com/u/7719209?u=5d963ead289969257190b133250653bd99df06ba&v=4
url: https://github.com/mattwelke
- login: hcristea
avatarUrl: https://avatars.githubusercontent.com/u/7814406?u=61d7a4fcf846983a4606788eac25e1c6c1209ba8&v=4
url: https://github.com/hcristea
@@ -470,9 +494,6 @@ sponsors:
- login: logan-connolly
avatarUrl: https://avatars.githubusercontent.com/u/16244943?u=8ae66dfbba936463cc8aa0dd7a6d2b4c0cc757eb&v=4
url: https://github.com/logan-connolly
- login: sanghunka
avatarUrl: https://avatars.githubusercontent.com/u/16280020?u=7d8fafd8bfe6d7900bb1e52d5a5d5da0c02bc164&v=4
url: https://github.com/sanghunka
- login: stevenayers
avatarUrl: https://avatars.githubusercontent.com/u/16361214?u=098b797d8d48afb8cd964b717847943b61d24a6d&v=4
url: https://github.com/stevenayers
@@ -494,6 +515,12 @@ sponsors:
- login: fstau
avatarUrl: https://avatars.githubusercontent.com/u/24669867?u=60e7c8c09f8dafabee8fc3edcd6f9e19abbff918&v=4
url: https://github.com/fstau
- login: pers0n4
avatarUrl: https://avatars.githubusercontent.com/u/24864600?u=444441027bc2c9f9db68e8047d65ff23d25699cf&v=4
url: https://github.com/pers0n4
- login: SebTota
avatarUrl: https://avatars.githubusercontent.com/u/25122511?v=4
url: https://github.com/SebTota
- login: mertguvencli
avatarUrl: https://avatars.githubusercontent.com/u/29762151?u=16a906d90df96c8cff9ea131a575c4bc171b1523&v=4
url: https://github.com/mertguvencli
@@ -563,12 +590,12 @@ sponsors:
- login: pondDevThai
avatarUrl: https://avatars.githubusercontent.com/u/71592181?u=08af9a59bccfd8f6b101de1005aa9822007d0a44&v=4
url: https://github.com/pondDevThai
- - login: mattwelke
avatarUrl: https://avatars.githubusercontent.com/u/7719209?u=5d963ead289969257190b133250653bd99df06ba&v=4
url: https://github.com/mattwelke
- login: cesarfreire
avatarUrl: https://avatars.githubusercontent.com/u/21126103?u=5d428f77f9b63c741f0e9ca5e15a689017b66fe8&v=4
url: https://github.com/cesarfreire
- login: marlonmartins2
avatarUrl: https://avatars.githubusercontent.com/u/87719558?u=d07ffecfdabf4fd9aca987f8b5cd9128c65e598e&v=4
url: https://github.com/marlonmartins2
- - login: 2niuhe
avatarUrl: https://avatars.githubusercontent.com/u/13382324?u=ac918d72e0329c87ba29b3bf85e03e98a4ee9427&v=4
url: https://github.com/2niuhe
- login: gabrielmbmb
avatarUrl: https://avatars.githubusercontent.com/u/29572918?u=6d1e00b5d558e96718312ff910a2318f47cc3145&v=4
url: https://github.com/gabrielmbmb
@@ -578,6 +605,6 @@ sponsors:
- login: Moises6669
avatarUrl: https://avatars.githubusercontent.com/u/66188523?u=96af25b8d5be9f983cb96e9dd7c605c716caf1f5&v=4
url: https://github.com/Moises6669
- login: zahariev-webbersof
avatarUrl: https://avatars.githubusercontent.com/u/68993494?u=b341c94a8aa0624e05e201bcf8ae5b2697e3be2f&v=4
url: https://github.com/zahariev-webbersof
- login: su-shubham
avatarUrl: https://avatars.githubusercontent.com/u/75021117?v=4
url: https://github.com/su-shubham

View File

@@ -1,12 +1,12 @@
maintainers:
- login: tiangolo
answers: 1271
prs: 338
answers: 1315
prs: 342
avatarUrl: https://avatars.githubusercontent.com/u/1326112?u=740f11212a731f56798f558ceddb0bd07642afa7&v=4
url: https://github.com/tiangolo
experts:
- login: Kludex
count: 364
count: 367
avatarUrl: https://avatars.githubusercontent.com/u/7353520?u=62adc405ef418f4b6c8caa93d3eb8ab107bc4927&v=4
url: https://github.com/Kludex
- login: dmontagu
@@ -21,14 +21,14 @@ experts:
count: 207
avatarUrl: https://avatars.githubusercontent.com/u/1405026?v=4
url: https://github.com/Mause
- login: JarroVGIT
count: 187
avatarUrl: https://avatars.githubusercontent.com/u/13659033?u=e8bea32d07a5ef72f7dde3b2079ceb714923ca05&v=4
url: https://github.com/JarroVGIT
- login: euri10
count: 166
avatarUrl: https://avatars.githubusercontent.com/u/1104190?u=321a2e953e6645a7d09b732786c7a8061e0f8a8b&v=4
url: https://github.com/euri10
- login: JarroVGIT
count: 163
avatarUrl: https://avatars.githubusercontent.com/u/13659033?u=e8bea32d07a5ef72f7dde3b2079ceb714923ca05&v=4
url: https://github.com/JarroVGIT
- login: phy25
count: 130
avatarUrl: https://avatars.githubusercontent.com/u/331403?v=4
@@ -37,20 +37,20 @@ experts:
count: 77
avatarUrl: https://avatars.githubusercontent.com/u/10202690?u=e6f86f5c0c3026a15d6b51792fa3e532b12f1371&v=4
url: https://github.com/raphaelauv
- login: iudeen
count: 76
avatarUrl: https://avatars.githubusercontent.com/u/10519440?u=2843b3303282bff8b212dcd4d9d6689452e4470c&v=4
url: https://github.com/iudeen
- login: ArcLightSlavik
count: 71
avatarUrl: https://avatars.githubusercontent.com/u/31127044?u=b0f2c37142f4b762e41ad65dc49581813422bd71&v=4
url: https://github.com/ArcLightSlavik
- login: iudeen
count: 65
avatarUrl: https://avatars.githubusercontent.com/u/10519440?u=2843b3303282bff8b212dcd4d9d6689452e4470c&v=4
url: https://github.com/iudeen
- login: falkben
count: 58
avatarUrl: https://avatars.githubusercontent.com/u/653031?u=0c8d8f33d87f1aa1a6488d3f02105e9abc838105&v=4
url: https://github.com/falkben
- login: sm-Fifteen
count: 49
count: 50
avatarUrl: https://avatars.githubusercontent.com/u/516999?u=437c0c5038558c67e887ccd863c1ba0f846c03da&v=4
url: https://github.com/sm-Fifteen
- login: insomnes
@@ -58,11 +58,11 @@ experts:
avatarUrl: https://avatars.githubusercontent.com/u/16958893?u=f8be7088d5076d963984a21f95f44e559192d912&v=4
url: https://github.com/insomnes
- login: jgould22
count: 45
count: 46
avatarUrl: https://avatars.githubusercontent.com/u/4335847?u=ed77f67e0bb069084639b24d812dbb2a2b1dc554&v=4
url: https://github.com/jgould22
- login: Dustyposa
count: 43
count: 45
avatarUrl: https://avatars.githubusercontent.com/u/27180793?u=5cf2877f50b3eb2bc55086089a78a36f07042889&v=4
url: https://github.com/Dustyposa
- login: adriangb
@@ -89,6 +89,10 @@ experts:
count: 31
avatarUrl: https://avatars.githubusercontent.com/u/1144727?u=85c025e3fcc7bd79a5665c63ee87cdf8aae13374&v=4
url: https://github.com/frankie567
- login: acidjunk
count: 31
avatarUrl: https://avatars.githubusercontent.com/u/685002?u=b5094ab4527fc84b006c0ac9ff54367bdebb2267&v=4
url: https://github.com/acidjunk
- login: krishnardt
count: 31
avatarUrl: https://avatars.githubusercontent.com/u/31960541?u=47f4829c77f4962ab437ffb7995951e41eeebe9b&v=4
@@ -101,10 +105,6 @@ experts:
count: 29
avatarUrl: https://avatars.githubusercontent.com/u/41326348?u=ba2fda6b30110411ecbf406d187907e2b420ac19&v=4
url: https://github.com/panla
- login: acidjunk
count: 27
avatarUrl: https://avatars.githubusercontent.com/u/685002?u=b5094ab4527fc84b006c0ac9ff54367bdebb2267&v=4
url: https://github.com/acidjunk
- login: ghandic
count: 25
avatarUrl: https://avatars.githubusercontent.com/u/23500353?u=e2e1d736f924d9be81e8bfc565b6d8836ba99773&v=4
@@ -117,6 +117,10 @@ experts:
count: 24
avatarUrl: https://avatars.githubusercontent.com/u/9435877?u=719327b7d2c4c62212456d771bfa7c6b8dbb9eac&v=4
url: https://github.com/SirTelemak
- login: odiseo0
count: 23
avatarUrl: https://avatars.githubusercontent.com/u/87550035?u=16f9255804161c6ff3c8b7ef69848f0126bcd405&v=4
url: https://github.com/odiseo0
- login: acnebs
count: 22
avatarUrl: https://avatars.githubusercontent.com/u/9054108?u=c27e50269f1ef8ea950cc6f0268c8ec5cebbe9c9&v=4
@@ -129,10 +133,6 @@ experts:
count: 21
avatarUrl: https://avatars.githubusercontent.com/u/565544?v=4
url: https://github.com/chris-allnutt
- login: odiseo0
count: 21
avatarUrl: https://avatars.githubusercontent.com/u/87550035?u=ab724eae71c3fe1cf81e8dc76e73415da926ef7d&v=4
url: https://github.com/odiseo0
- login: retnikt
count: 19
avatarUrl: https://avatars.githubusercontent.com/u/24581770?v=4
@@ -161,6 +161,10 @@ experts:
count: 16
avatarUrl: https://avatars.githubusercontent.com/u/39515546?u=ec35139777597cdbbbddda29bf8b9d4396b429a9&v=4
url: https://github.com/waynerv
- login: yinziyan1206
count: 16
avatarUrl: https://avatars.githubusercontent.com/u/37829370?v=4
url: https://github.com/yinziyan1206
- login: dstlny
count: 16
avatarUrl: https://avatars.githubusercontent.com/u/41964673?u=9f2174f9d61c15c6e3a4c9e3aeee66f711ce311f&v=4
@@ -177,10 +181,6 @@ experts:
count: 13
avatarUrl: https://avatars.githubusercontent.com/u/58201?u=dd40d99a3e1935d0b768f122bfe2258d6ea53b2b&v=4
url: https://github.com/haizaar
- login: yinziyan1206
count: 13
avatarUrl: https://avatars.githubusercontent.com/u/37829370?v=4
url: https://github.com/yinziyan1206
- login: valentin994
count: 13
avatarUrl: https://avatars.githubusercontent.com/u/42819267?u=fdeeaa9242a59b243f8603496b00994f6951d5a2&v=4
@@ -193,35 +193,27 @@ experts:
count: 12
avatarUrl: https://avatars.githubusercontent.com/u/2964996?v=4
url: https://github.com/n8sty
- login: zoliknemet
- login: mbroton
count: 12
avatarUrl: https://avatars.githubusercontent.com/u/22326718?u=31ba446ac290e23e56eea8e4f0c558aaf0b40779&v=4
url: https://github.com/zoliknemet
avatarUrl: https://avatars.githubusercontent.com/u/50829834?u=a48610bf1bffaa9c75d03228926e2eb08a2e24ee&v=4
url: https://github.com/mbroton
last_month_active:
- login: JarroVGIT
count: 27
count: 18
avatarUrl: https://avatars.githubusercontent.com/u/13659033?u=e8bea32d07a5ef72f7dde3b2079ceb714923ca05&v=4
url: https://github.com/JarroVGIT
- login: iudeen
count: 16
count: 8
avatarUrl: https://avatars.githubusercontent.com/u/10519440?u=2843b3303282bff8b212dcd4d9d6689452e4470c&v=4
url: https://github.com/iudeen
- login: mbroton
count: 6
count: 4
avatarUrl: https://avatars.githubusercontent.com/u/50829834?u=a48610bf1bffaa9c75d03228926e2eb08a2e24ee&v=4
url: https://github.com/mbroton
- login: csrgxtu
count: 6
avatarUrl: https://avatars.githubusercontent.com/u/5053620?u=9655a3e9661492fcdaaf99193eb16d5cbcc3849e&v=4
url: https://github.com/csrgxtu
- login: Kludex
count: 4
avatarUrl: https://avatars.githubusercontent.com/u/7353520?u=62adc405ef418f4b6c8caa93d3eb8ab107bc4927&v=4
url: https://github.com/Kludex
- login: jgould22
- login: yinziyan1206
count: 3
avatarUrl: https://avatars.githubusercontent.com/u/4335847?u=ed77f67e0bb069084639b24d812dbb2a2b1dc554&v=4
url: https://github.com/jgould22
avatarUrl: https://avatars.githubusercontent.com/u/37829370?v=4
url: https://github.com/yinziyan1206
top_contributors:
- login: waynerv
count: 25
@@ -243,6 +235,10 @@ top_contributors:
count: 15
avatarUrl: https://avatars.githubusercontent.com/u/7353520?u=62adc405ef418f4b6c8caa93d3eb8ab107bc4927&v=4
url: https://github.com/Kludex
- login: dependabot
count: 14
avatarUrl: https://avatars.githubusercontent.com/in/29110?v=4
url: https://github.com/apps/dependabot
- login: euri10
count: 13
avatarUrl: https://avatars.githubusercontent.com/u/1104190?u=321a2e953e6645a7d09b732786c7a8061e0f8a8b&v=4
@@ -255,10 +251,6 @@ top_contributors:
count: 10
avatarUrl: https://avatars.githubusercontent.com/u/16785985?v=4
url: https://github.com/Smlep
- login: dependabot
count: 9
avatarUrl: https://avatars.githubusercontent.com/in/29110?v=4
url: https://github.com/apps/dependabot
- login: Serrones
count: 8
avatarUrl: https://avatars.githubusercontent.com/u/22691749?u=4795b880e13ca33a73e52fc0ef7dc9c60c8fce47&v=4
@@ -271,6 +263,14 @@ top_contributors:
count: 7
avatarUrl: https://avatars.githubusercontent.com/u/9651103?u=95db33927bbff1ed1c07efddeb97ac2ff33068ed&v=4
url: https://github.com/hard-coders
- login: rjNemo
count: 7
avatarUrl: https://avatars.githubusercontent.com/u/56785022?u=d5c3a02567c8649e146fcfc51b6060ccaf8adef8&v=4
url: https://github.com/rjNemo
- login: pre-commit-ci
count: 6
avatarUrl: https://avatars.githubusercontent.com/in/68672?v=4
url: https://github.com/apps/pre-commit-ci
- login: wshayes
count: 5
avatarUrl: https://avatars.githubusercontent.com/u/365303?u=07ca03c5ee811eb0920e633cc3c3db73dbec1aa5&v=4
@@ -285,12 +285,16 @@ top_contributors:
url: https://github.com/Attsun1031
- login: ComicShrimp
count: 5
avatarUrl: https://avatars.githubusercontent.com/u/43503750?u=b3e4d9a14d9a65d429ce62c566aef73178b7111d&v=4
avatarUrl: https://avatars.githubusercontent.com/u/43503750?u=f440bc9062afb3c43b9b9c6cdfdcfe31d58699ef&v=4
url: https://github.com/ComicShrimp
- login: jekirl
count: 4
avatarUrl: https://avatars.githubusercontent.com/u/2546697?u=a027452387d85bd4a14834e19d716c99255fb3b7&v=4
url: https://github.com/jekirl
- login: samuelcolvin
count: 4
avatarUrl: https://avatars.githubusercontent.com/u/4039449?u=807390ba9cfe23906c3bf8a0d56aaca3cf2bfa0d&v=4
url: https://github.com/samuelcolvin
- login: jfunez
count: 4
avatarUrl: https://avatars.githubusercontent.com/u/805749?v=4
@@ -307,10 +311,6 @@ top_contributors:
count: 4
avatarUrl: https://avatars.githubusercontent.com/u/3360631?u=5fa1f475ad784d64eb9666bdd43cc4d285dcc773&v=4
url: https://github.com/hitrust
- login: rjNemo
count: 4
avatarUrl: https://avatars.githubusercontent.com/u/56785022?u=d5c3a02567c8649e146fcfc51b6060ccaf8adef8&v=4
url: https://github.com/rjNemo
- login: lsglucas
count: 4
avatarUrl: https://avatars.githubusercontent.com/u/61513630?u=320e43fe4dc7bc6efc64e9b8f325f8075634fd20&v=4
@@ -319,19 +319,23 @@ top_contributors:
count: 4
avatarUrl: https://avatars.githubusercontent.com/u/79563565?u=1741703bd6c8f491503354b363a86e879b4c1cab&v=4
url: https://github.com/NinaHwang
- login: pre-commit-ci
- login: batlopes
count: 4
avatarUrl: https://avatars.githubusercontent.com/in/68672?v=4
url: https://github.com/apps/pre-commit-ci
avatarUrl: https://avatars.githubusercontent.com/u/33462923?u=0fb3d7acb316764616f11e4947faf080e49ad8d9&v=4
url: https://github.com/batlopes
top_reviewers:
- login: Kludex
count: 101
count: 108
avatarUrl: https://avatars.githubusercontent.com/u/7353520?u=62adc405ef418f4b6c8caa93d3eb8ab107bc4927&v=4
url: https://github.com/Kludex
- login: BilalAlpaslan
count: 64
count: 65
avatarUrl: https://avatars.githubusercontent.com/u/47563997?u=63ed66e304fe8d765762c70587d61d9196e5c82d&v=4
url: https://github.com/BilalAlpaslan
- login: yezz123
count: 56
avatarUrl: https://avatars.githubusercontent.com/u/52716203?u=636b4f79645176df4527dd45c12d5dbb5a4193cf&v=4
url: https://github.com/yezz123
- login: tokusumi
count: 50
avatarUrl: https://avatars.githubusercontent.com/u/41147016?u=55010621aece725aa702270b54fed829b6a1fe60&v=4
@@ -344,10 +348,6 @@ top_reviewers:
count: 47
avatarUrl: https://avatars.githubusercontent.com/u/59285379?v=4
url: https://github.com/Laineyzhang55
- login: yezz123
count: 46
avatarUrl: https://avatars.githubusercontent.com/u/52716203?u=636b4f79645176df4527dd45c12d5dbb5a4193cf&v=4
url: https://github.com/yezz123
- login: ycd
count: 45
avatarUrl: https://avatars.githubusercontent.com/u/62724709?u=fa40e037060d62bf82e16b505d870a2866725f38&v=4
@@ -356,6 +356,10 @@ top_reviewers:
count: 41
avatarUrl: https://avatars.githubusercontent.com/u/24587499?u=e772190a051ab0eaa9c8542fcff1892471638f2b&v=4
url: https://github.com/cikay
- login: JarroVGIT
count: 34
avatarUrl: https://avatars.githubusercontent.com/u/13659033?u=e8bea32d07a5ef72f7dde3b2079ceb714923ca05&v=4
url: https://github.com/JarroVGIT
- login: AdrianDeAnda
count: 33
avatarUrl: https://avatars.githubusercontent.com/u/1024932?u=b2ea249c6b41ddf98679c8d110d0f67d4a3ebf93&v=4
@@ -364,16 +368,16 @@ top_reviewers:
count: 31
avatarUrl: https://avatars.githubusercontent.com/u/31127044?u=b0f2c37142f4b762e41ad65dc49581813422bd71&v=4
url: https://github.com/ArcLightSlavik
- login: JarroVGIT
count: 31
avatarUrl: https://avatars.githubusercontent.com/u/13659033?u=e8bea32d07a5ef72f7dde3b2079ceb714923ca05&v=4
url: https://github.com/JarroVGIT
- login: iudeen
count: 26
avatarUrl: https://avatars.githubusercontent.com/u/10519440?u=2843b3303282bff8b212dcd4d9d6689452e4470c&v=4
url: https://github.com/iudeen
- login: cassiobotaro
count: 25
avatarUrl: https://avatars.githubusercontent.com/u/3127847?u=b0a652331da17efeb85cd6e3a4969182e5004804&v=4
url: https://github.com/cassiobotaro
- login: lsglucas
count: 24
count: 25
avatarUrl: https://avatars.githubusercontent.com/u/61513630?u=320e43fe4dc7bc6efc64e9b8f325f8075634fd20&v=4
url: https://github.com/lsglucas
- login: dmontagu
@@ -392,6 +396,14 @@ top_reviewers:
count: 19
avatarUrl: https://avatars.githubusercontent.com/u/63915557?u=47debaa860fd52c9b98c97ef357ddcec3b3fb399&v=4
url: https://github.com/0417taehyun
- login: rjNemo
count: 17
avatarUrl: https://avatars.githubusercontent.com/u/56785022?u=d5c3a02567c8649e146fcfc51b6060ccaf8adef8&v=4
url: https://github.com/rjNemo
- login: Smlep
count: 17
avatarUrl: https://avatars.githubusercontent.com/u/16785985?v=4
url: https://github.com/Smlep
- login: zy7y
count: 17
avatarUrl: https://avatars.githubusercontent.com/u/67154681?u=5d634834cc514028ea3f9115f7030b99a1f4d5a4&v=4
@@ -404,14 +416,6 @@ top_reviewers:
count: 16
avatarUrl: https://avatars.githubusercontent.com/u/52768429?u=6a3aa15277406520ad37f6236e89466ed44bc5b8&v=4
url: https://github.com/SwftAlpc
- login: rjNemo
count: 16
avatarUrl: https://avatars.githubusercontent.com/u/56785022?u=d5c3a02567c8649e146fcfc51b6060ccaf8adef8&v=4
url: https://github.com/rjNemo
- login: Smlep
count: 16
avatarUrl: https://avatars.githubusercontent.com/u/16785985?v=4
url: https://github.com/Smlep
- login: DevDae
count: 16
avatarUrl: https://avatars.githubusercontent.com/u/87962045?u=08e10fa516e844934f4b3fc7c38b33c61697e4a1&v=4
@@ -424,10 +428,10 @@ top_reviewers:
count: 15
avatarUrl: https://avatars.githubusercontent.com/u/63476957?u=6c86e59b48e0394d4db230f37fc9ad4d7e2c27c7&v=4
url: https://github.com/delhi09
- login: iudeen
- login: odiseo0
count: 14
avatarUrl: https://avatars.githubusercontent.com/u/10519440?u=2843b3303282bff8b212dcd4d9d6689452e4470c&v=4
url: https://github.com/iudeen
avatarUrl: https://avatars.githubusercontent.com/u/87550035?u=16f9255804161c6ff3c8b7ef69848f0126bcd405&v=4
url: https://github.com/odiseo0
- login: sh0nk
count: 13
avatarUrl: https://avatars.githubusercontent.com/u/6478810?u=af15d724875cec682ed8088a86d36b2798f981c0&v=4
@@ -436,10 +440,6 @@ top_reviewers:
count: 12
avatarUrl: https://avatars.githubusercontent.com/u/31848542?u=efb5b45b55584450507834f279ce48d4d64dea2f&v=4
url: https://github.com/RunningIkkyu
- login: odiseo0
count: 12
avatarUrl: https://avatars.githubusercontent.com/u/87550035?u=ab724eae71c3fe1cf81e8dc76e73415da926ef7d&v=4
url: https://github.com/odiseo0
- login: LorhanSohaky
count: 11
avatarUrl: https://avatars.githubusercontent.com/u/16273730?u=095b66f243a2cd6a0aadba9a095009f8aaf18393&v=4
@@ -462,7 +462,7 @@ top_reviewers:
url: https://github.com/maoyibo
- login: ComicShrimp
count: 10
avatarUrl: https://avatars.githubusercontent.com/u/43503750?u=b3e4d9a14d9a65d429ce62c566aef73178b7111d&v=4
avatarUrl: https://avatars.githubusercontent.com/u/43503750?u=f440bc9062afb3c43b9b9c6cdfdcfe31d58699ef&v=4
url: https://github.com/ComicShrimp
- login: graingert
count: 9

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -8,8 +8,8 @@
<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://codecov.io/gh/tiangolo/fastapi" target="_blank">
<img src="https://img.shields.io/codecov/c/github/tiangolo/fastapi?color=%2334D058" alt="Coverage">
<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">
@@ -424,7 +424,7 @@ For a more complete example including more features, see the <a href="https://fa
* **GraphQL** integration with <a href="https://strawberry.rocks" class="external-link" target="_blank">Strawberry</a> and other libraries.
* Many extra features (thanks to Starlette) as:
* **WebSockets**
* extremely easy tests based on `requests` and `pytest`
* extremely easy tests based on HTTPX and `pytest`
* **CORS**
* **Cookie Sessions**
* ...and more.
@@ -444,7 +444,7 @@ Used by Pydantic:
Used by Starlette:
* <a href="https://requests.readthedocs.io" target="_blank"><code>requests</code></a> - Required if you want to use the `TestClient`.
* <a href="https://www.python-httpx.org" target="_blank"><code>httpx</code></a> - Required if you want to use the `TestClient`.
* <a href="https://jinja.palletsprojects.com" target="_blank"><code>jinja2</code></a> - Required if you want to use the default template configuration.
* <a href="https://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.

View File

@@ -3,6 +3,73 @@
## Latest Changes
## 0.87.0
Highlights of this release:
* [Upgraded Starlette](https://github.com/encode/starlette/releases/tag/0.21.0)
* Now the `TestClient` is based on HTTPX instead of Requests. 🚀
* There are some possible **breaking changes** in the `TestClient` usage, but [@Kludex](https://github.com/Kludex) built [bump-testclient](https://github.com/Kludex/bump-testclient) to help you automatize migrating your tests. Make sure you are using Git and that you can undo any unnecessary changes (false positive changes, etc) before using `bump-testclient`.
* New [WebSocketException (and docs)](https://fastapi.tiangolo.com/advanced/websockets/#using-depends-and-others), re-exported from Starlette.
* Upgraded and relaxed dependencies for package extras `all` (including new Uvicorn version), when you install `"fastapi[all]"`.
* New docs about how to [**Help Maintain FastAPI**](https://fastapi.tiangolo.com/help-fastapi/#help-maintain-fastapi).
### Features
* ⬆️ Upgrade and relax dependencies for extras "all". PR [#5634](https://github.com/tiangolo/fastapi/pull/5634) by [@tiangolo](https://github.com/tiangolo).
* ✨ Re-export Starlette's `WebSocketException` and add it to docs. PR [#5629](https://github.com/tiangolo/fastapi/pull/5629) by [@tiangolo](https://github.com/tiangolo).
* 📝 Update references to Requests for tests to HTTPX, and add HTTPX to extras. PR [#5628](https://github.com/tiangolo/fastapi/pull/5628) by [@tiangolo](https://github.com/tiangolo).
* ⬆ Upgrade Starlette to `0.21.0`, including the new [`TestClient` based on HTTPX](https://github.com/encode/starlette/releases/tag/0.21.0). PR [#5471](https://github.com/tiangolo/fastapi/pull/5471) by [@pawelrubin](https://github.com/pawelrubin).
### Docs
* ✏️ Tweak Help FastAPI from PR review after merging. PR [#5633](https://github.com/tiangolo/fastapi/pull/5633) by [@tiangolo](https://github.com/tiangolo).
* ✏️ Clarify docs on CORS. PR [#5627](https://github.com/tiangolo/fastapi/pull/5627) by [@paxcodes](https://github.com/paxcodes).
* 📝 Update Help FastAPI: Help Maintain FastAPI. PR [#5632](https://github.com/tiangolo/fastapi/pull/5632) by [@tiangolo](https://github.com/tiangolo).
### Translations
* 🌐 Fix highlight lines for Japanese translation for `docs/tutorial/query-params.md`. PR [#2969](https://github.com/tiangolo/fastapi/pull/2969) by [@ftnext](https://github.com/ftnext).
* 🌐 Add French translation for `docs/fr/docs/advanced/additional-status-code.md`. PR [#5477](https://github.com/tiangolo/fastapi/pull/5477) by [@axel584](https://github.com/axel584).
* 🌐 Add Portuguese translation for `docs/pt/docs/tutorial/request-forms-and-files.md`. PR [#5579](https://github.com/tiangolo/fastapi/pull/5579) by [@batlopes](https://github.com/batlopes).
* 🌐 Add Japanese translation for `docs/ja/docs/advanced/websockets.md`. PR [#4983](https://github.com/tiangolo/fastapi/pull/4983) by [@xryuseix](https://github.com/xryuseix).
### Internal
* ✨ Use Ruff for linting. PR [#5630](https://github.com/tiangolo/fastapi/pull/5630) by [@tiangolo](https://github.com/tiangolo).
* 🛠 Add Arabic issue number to Notify Translations GitHub Action. PR [#5610](https://github.com/tiangolo/fastapi/pull/5610) by [@tiangolo](https://github.com/tiangolo).
* ⬆ Bump dawidd6/action-download-artifact from 2.24.1 to 2.24.2. PR [#5609](https://github.com/tiangolo/fastapi/pull/5609) by [@dependabot[bot]](https://github.com/apps/dependabot).
* ⬆ Bump dawidd6/action-download-artifact from 2.24.0 to 2.24.1. PR [#5603](https://github.com/tiangolo/fastapi/pull/5603) by [@dependabot[bot]](https://github.com/apps/dependabot).
* 📝 Update coverage badge to use Samuel Colvin's Smokeshow. PR [#5585](https://github.com/tiangolo/fastapi/pull/5585) by [@tiangolo](https://github.com/tiangolo).
## 0.86.0
### Features
* ⬆ Add Python 3.11 to the officially supported versions. PR [#5587](https://github.com/tiangolo/fastapi/pull/5587) by [@tiangolo](https://github.com/tiangolo).
* ✅ Enable tests for Python 3.11. PR [#4881](https://github.com/tiangolo/fastapi/pull/4881) by [@tiangolo](https://github.com/tiangolo).
### Fixes
* 🐛 Close FormData (uploaded files) after the request is done. PR [#5465](https://github.com/tiangolo/fastapi/pull/5465) by [@adriangb](https://github.com/adriangb).
### Docs
* ✏ Fix typo in `docs/en/docs/tutorial/security/oauth2-jwt.md`. PR [#5584](https://github.com/tiangolo/fastapi/pull/5584) by [@vivekashok1221](https://github.com/vivekashok1221).
### Translations
* 🌐 Update wording in Chinese translation for `docs/zh/docs/python-types.md`. PR [#5416](https://github.com/tiangolo/fastapi/pull/5416) by [@supercaizehua](https://github.com/supercaizehua).
* 🌐 Add Russian translation for `docs/ru/docs/deployment/index.md`. PR [#5336](https://github.com/tiangolo/fastapi/pull/5336) by [@Xewus](https://github.com/Xewus).
* 🌐 Update Chinese translation for `docs/tutorial/security/oauth2-jwt.md`. PR [#3846](https://github.com/tiangolo/fastapi/pull/3846) by [@jaystone776](https://github.com/jaystone776).
### Internal
* 👷 Update FastAPI People to exclude bots: pre-commit-ci, dependabot. PR [#5586](https://github.com/tiangolo/fastapi/pull/5586) by [@tiangolo](https://github.com/tiangolo).
* 🎨 Format OpenAPI JSON in `test_starlette_exception.py`. PR [#5379](https://github.com/tiangolo/fastapi/pull/5379) by [@iudeen](https://github.com/iudeen).
* 👷 Switch from Codecov to Smokeshow plus pytest-cov to pure coverage for internal tests. PR [#5583](https://github.com/tiangolo/fastapi/pull/5583) by [@tiangolo](https://github.com/tiangolo).
* 👥 Update FastAPI People. PR [#5571](https://github.com/tiangolo/fastapi/pull/5571) by [@github-actions[bot]](https://github.com/apps/github-actions).
## 0.85.2
### Docs

View File

@@ -57,7 +57,7 @@ The following arguments are supported:
* `allow_origins` - A list of origins that should be permitted to make cross-origin requests. E.g. `['https://example.org', 'https://www.example.org']`. You can use `['*']` to allow any origin.
* `allow_origin_regex` - A regex string to match against origins that should be permitted to make cross-origin requests. e.g. `'https://.*\.example\.org'`.
* `allow_methods` - A list of HTTP methods that should be allowed for cross-origin requests. Defaults to `['GET']`. You can use `['*']` to allow all standard methods.
* `allow_headers` - A list of HTTP request headers that should be supported for cross-origin requests. Defaults to `[]`. You can use `['*']` to allow all headers. The `Accept`, `Accept-Language`, `Content-Language` and `Content-Type` headers are always allowed for CORS requests.
* `allow_headers` - A list of HTTP request headers that should be supported for cross-origin requests. Defaults to `[]`. You can use `['*']` to allow all headers. The `Accept`, `Accept-Language`, `Content-Language` and `Content-Type` headers are always allowed for <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS#simple_requests" class="external-link" rel="noopener" target="_blank">simple CORS requests</a>.
* `allow_credentials` - Indicate that cookies should be supported for cross-origin requests. Defaults to `False`. Also, `allow_origins` cannot be set to `['*']` for credentials to be allowed, origins must be specified.
* `expose_headers` - Indicate any response headers that should be made accessible to the browser. Defaults to `[]`.
* `max_age` - Sets a maximum time in seconds for browsers to cache CORS responses. Defaults to `600`.

View File

@@ -257,7 +257,7 @@ Call the endpoint `/users/me/`, you will get the response as:
<img src="/img/tutorial/security/image09.png">
If you open the developer tools, you could see how the data sent and only includes the token, the password is only sent in the first request to authenticate the user and get that access token, but not afterwards:
If you open the developer tools, you could see how the data sent only includes the token, the password is only sent in the first request to authenticate the user and get that access token, but not afterwards:
<img src="/img/tutorial/security/image10.png">

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -426,7 +426,7 @@ For a more complete example including more features, see the <a href="https://fa
* Many extra features (thanks to Starlette) as:
* **WebSockets**
* **GraphQL**
* extremely easy tests based on `requests` and `pytest`
* extremely easy tests based on HTTPX and `pytest`
* **CORS**
* **Cookie Sessions**
* ...and more.
@@ -446,7 +446,7 @@ Used by Pydantic:
Used by Starlette:
* <a href="https://requests.readthedocs.io" target="_blank"><code>requests</code></a> - Required if you want to use the `TestClient`.
* <a href="https://www.python-httpx.org" target="_blank"><code>HTTPX</code></a> - Required if you want to use the `TestClient`.
* <a href="https://jinja.palletsprojects.com" target="_blank"><code>jinja2</code></a> - Required if you want to use the default template configuration.
* <a href="https://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.

View File

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

View File

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

View File

@@ -426,7 +426,7 @@ For a more complete example including more features, see the <a href="https://fa
* Many extra features (thanks to Starlette) as:
* **WebSockets**
* **GraphQL**
* extremely easy tests based on `requests` and `pytest`
* extremely easy tests based on HTTPX and `pytest`
* **CORS**
* **Cookie Sessions**
* ...and more.
@@ -446,7 +446,7 @@ Used by Pydantic:
Used by Starlette:
* <a href="https://requests.readthedocs.io" target="_blank"><code>requests</code></a> - Required if you want to use the `TestClient`.
* <a href="https://www.python-httpx.org" target="_blank"><code>httpx</code></a> - Required if you want to use the `TestClient`.
* <a href="https://jinja.palletsprojects.com" target="_blank"><code>jinja2</code></a> - Required if you want to use the default template configuration.
* <a href="https://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.

View File

@@ -423,7 +423,7 @@ For a more complete example including more features, see the <a href="https://fa
* Many extra features (thanks to Starlette) as:
* **WebSockets**
* **GraphQL**
* extremely easy tests based on `requests` and `pytest`
* extremely easy tests based on HTTPX and `pytest`
* **CORS**
* **Cookie Sessions**
* ...and more.
@@ -443,7 +443,7 @@ Used by Pydantic:
Used by Starlette:
* <a href="https://requests.readthedocs.io" target="_blank"><code>requests</code></a> - Required if you want to use the `TestClient`.
* <a href="https://www.python-httpx.org" target="_blank"><code>httpx</code></a> - Required if you want to use the `TestClient`.
* <a href="https://jinja.palletsprojects.com" target="_blank"><code>jinja2</code></a> - Required if you want to use the default template configuration.
* <a href="https://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.

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -429,7 +429,7 @@ For a more complete example including more features, see the <a href="https://fa
* **GraphQL** integration with <a href="https://strawberry.rocks" class="external-link" target="_blank">Strawberry</a> and other libraries.
* Many extra features (thanks to Starlette) as:
* **WebSockets**
* extremely easy tests based on `requests` and `pytest`
* extremely easy tests based on HTTPX and `pytest`
* **CORS**
* **Cookie Sessions**
* ...and more.
@@ -449,7 +449,7 @@ Used by Pydantic:
Used by Starlette:
* <a href="https://requests.readthedocs.io" target="_blank"><code>requests</code></a> - Required if you want to use the `TestClient`.
* <a href="https://www.python-httpx.org" target="_blank"><code>httpx</code></a> - Required if you want to use the `TestClient`.
* <a href="https://jinja.palletsprojects.com" target="_blank"><code>jinja2</code></a> - Required if you want to use the default template configuration.
* <a href="https://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.

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,21 @@
# Развёртывание - Введение
Развернуть приложение **FastAPI** довольно просто.
## Да что такое это ваше - "развёртывание"?!
Термин **развёртывание** (приложения) означает выполнение необходимых шагов, чтобы сделать приложение **доступным для пользователей**.
Обычно **веб-приложения** размещают на удалённом компьютере с серверной программой, которая обеспечивает хорошую производительность, стабильность и т. д., Чтобы ваши пользователи могли эффективно, беспрерывно и беспроблемно обращаться к приложению.
Это отличется от **разработки**, когда вы постоянно меняете код, делаете в нём намеренные ошибки и исправляете их, останавливаете и перезапускаете сервер разработки и т. д.
## Стратегии развёртывания
В зависимости от вашего конкретного случая, есть несколько способов сделать это.
Вы можете **развернуть сервер** самостоятельно, используя различные инструменты. Например, можно использовать **облачный сервис**, который выполнит часть работы за вас. Также возможны и другие варианты.
В этом блоке я покажу вам некоторые из основных концепций, которые вы, вероятно, должны иметь в виду при развертывании приложения **FastAPI** (хотя большинство из них применимо к любому другому типу веб-приложений).
В последующих разделах вы узнаете больше деталей и методов, необходимых для этого. ✨

View File

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

View File

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

View File

@@ -59,13 +59,15 @@ nav:
- uk: /uk/
- zh: /zh/
- features.md
- fastapi-people.md
- python-types.md
- Учебник - руководство пользователя:
- tutorial/background-tasks.md
- external-links.md
- async.md
- Развёртывание:
- deployment/index.md
- deployment/versions.md
- external-links.md
markdown_extensions:
- toc:
permalink: true

View File

@@ -426,7 +426,7 @@ For a more complete example including more features, see the <a href="https://fa
* Many extra features (thanks to Starlette) as:
* **WebSockets**
* **GraphQL**
* extremely easy tests based on `requests` and `pytest`
* extremely easy tests based on HTTPX and `pytest`
* **CORS**
* **Cookie Sessions**
* ...and more.
@@ -446,7 +446,7 @@ Used by Pydantic:
Used by Starlette:
* <a href="https://requests.readthedocs.io" target="_blank"><code>requests</code></a> - Required if you want to use the `TestClient`.
* <a href="https://www.python-httpx.org" target="_blank"><code>httpx</code></a> - Required if you want to use the `TestClient`.
* <a href="https://jinja.palletsprojects.com" target="_blank"><code>jinja2</code></a> - Required if you want to use the default template configuration.
* <a href="https://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.

View File

@@ -429,7 +429,7 @@ For a more complete example including more features, see the <a href="https://fa
* **GraphQL** integration with <a href="https://strawberry.rocks" class="external-link" target="_blank">Strawberry</a> and other libraries.
* Many extra features (thanks to Starlette) as:
* **WebSockets**
* extremely easy tests based on `requests` and `pytest`
* extremely easy tests based on HTTPX and `pytest`
* **CORS**
* **Cookie Sessions**
* ...and more.
@@ -449,7 +449,7 @@ Used by Pydantic:
Used by Starlette:
* <a href="https://requests.readthedocs.io" target="_blank"><code>requests</code></a> - Required if you want to use the `TestClient`.
* <a href="https://www.python-httpx.org" target="_blank"><code>httpx</code></a> - Required if you want to use the `TestClient`.
* <a href="https://jinja.palletsprojects.com" target="_blank"><code>jinja2</code></a> - Required if you want to use the default template configuration.
* <a href="https://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.

View File

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

View File

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

View File

@@ -426,7 +426,7 @@ For a more complete example including more features, see the <a href="https://fa
* Many extra features (thanks to Starlette) as:
* **WebSockets**
* **GraphQL**
* extremely easy tests based on `requests` and `pytest`
* extremely easy tests based on HTTPX and `pytest`
* **CORS**
* **Cookie Sessions**
* ...and more.
@@ -446,7 +446,7 @@ Used by Pydantic:
Used by Starlette:
* <a href="https://requests.readthedocs.io" target="_blank"><code>requests</code></a> - Required if you want to use the `TestClient`.
* <a href="https://www.python-httpx.org" target="_blank"><code>httpx</code></a> - Required if you want to use the `TestClient`.
* <a href="https://jinja.palletsprojects.com" target="_blank"><code>jinja2</code></a> - Required if you want to use the default template configuration.
* <a href="https://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.

View File

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

View File

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

View File

@@ -194,7 +194,7 @@ John Doe
这表示:
* 变量 `items_t` 是一个 `tuple`,其中的个元素都是 `int` 类型。
* 变量 `items_t` 是一个 `tuple`,其中的前两个元素都是 `int` 类型, 最后一个元素是 `str` 类型。
* 变量 `items_s` 是一个 `set`,其中的每个元素都是 `bytes` 类型。
#### 字典

View File

@@ -1,34 +1,34 @@
# 使用(哈希)密码和 JWT Bearer 令牌的 OAuth2
# OAuth2 实现密码哈希与 Bearer JWT 令牌验证
既然我们已经了所有安全流程,就让我们来使用 <abbr title="JSON Web Tokens">JWT</abbr> 令牌和安全哈希密码让应用程序真正安全
至此,我们已经编写了所有安全流,本章学习如何使用 <abbr title="JSON Web Tokens">JWT</abbr> 令牌Token和安全密码哈希Hash实现真正安全机制
你可以在应用程序中真正地使用这些代码,在数据库中保存密码哈希值,等等
本章的示例代码真正实现了在应用的数据库中保存哈希密码等功能
我们将从上一章结束的位置开始,然后对示例进行扩充
接下来,我们紧接上一章,继续完善安全机制
## 关于 JWT
## JWT 简介
JWT 表示 「JSON Web Tokens
JWT 即**JSON 网络令牌**JSON Web Tokens
是一将 JSON 对象编码为密集且没有空格的长字符串的标准。字符串看起来像这样
JWT 是一将 JSON 对象编码为没有空格,且难以理解的长字符串的标准。JWT 的内容如下所示
```
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
```
没有加密,因此任何人都可以从字符串内容中还原数据
JWT 字符串没有加密,任何人都能用它恢复原始信息
它经过了签名。因此,当你收到一个由你发出的令牌时,可以校验令牌是否真的由你发出
JWT 使用了签名机制。接受令牌时,可以用签名校验令牌。
通过这种方式,你可以创建一个有效期为 1 周的令牌。然后当用户第二天使用令牌重新访问时,你知道该用户仍然处于登入状态。
使用 JWT 创建有效期为周的令牌。第二天,用户持令牌再次访问时,仍为登录状态。
一周后令牌将会过期,用户将不会通过认证,必须再次登录才能获得一个新令牌。而且如果用户(或第三方)试图修改令牌以篡改过期时间,你将因为签名不匹配而能够发觉
令牌于一周后过期,届时,用户身份验证就会失败。只有再次登录才能获得新令牌。如果用户(或第三方)篡改令牌的过期时间,因为签名不匹配会导致身份验证失败
果你想上手体验 JWT 令牌了解工作方式,可访问 <a href="https://jwt.io/" class="external-link" target="_blank">https://jwt.io</a>。
需深入了解 JWT 令牌了解它的工作方式,请参阅 <a href="https://jwt.io/" class="external-link" target="_blank">https://jwt.io</a>。
## 安装 `python-jose`
我们需要安装 `python-jose`在 Python 中生成和校验 JWT 令牌:
安装 `python-jose`在 Python 中生成和校验 JWT 令牌:
<div class="termy">
@@ -40,38 +40,39 @@ $ pip install python-jose[cryptography]
</div>
<a href="https://github.com/mpdavis/python-jose" class="external-link" target="_blank">Python-jose</a> 需要一个额外的加密后端。
<a href="https://github.com/mpdavis/python-jose" class="external-link" target="_blank">Python-jose</a> 需要安装配套的加密后端。
这里我们使用的是推荐的后端:<a href="https://cryptography.io/" class="external-link" target="_blank">pyca/cryptography</a>。
本教程推荐的后端<a href="https://cryptography.io/" class="external-link" target="_blank">pyca/cryptography</a>。
!!! tip
本教程曾经使用过 <a href="https://pyjwt.readthedocs.io/" class="external-link" target="_blank">PyJWT</a>。
!!! tip "提示"
但是后来更新为使用 Python-jose因为它提供了 PyJWT 的所有功能,以及之后与其他工具进行集成时你可能需要的一些其他功能
本教程以前使用 <a href="https://pyjwt.readthedocs.io/" class="external-link" target="_blank">PyJWT</a>
## 哈希密码
但后来换成了 Python-jose因为 Python-jose 支持 PyJWT 的所有功能,还支持与其它工具集成时可能会用到的一些其它功能。
「哈希」的意思是:将某些内容(在本例中为密码)转换为看起来像乱码的字节序列(只是一个字符串)。
## 密码哈希
每次你传入完全相同的内容(完全相同的密码)时,你都会得到完全相同的乱码
**哈希**是指把特定内容(本例中为密码)转换为乱码形式的字节序列(其实就是字符串)
但是你不能从乱码转换回密码。
每次传入完全相同的内容时(比如,完全相同的密码),返回的都是完全相同的乱码。
### 为什么使用哈希密码
但这个乱码无法转换回传入的密码
如果你的数据库被盗,小偷将无法获得用户的明文密码,只能拿到哈希值。
### 为什么使用密码哈希
因此,小偷将无法尝试在另一个系统中使用这些相同的密码(由于许多用户在任何地方都使用相同的密码,因此这很危险)
原因很简单,假如数据库被盗,窃贼无法获取用户的明文密码,得到的只是哈希值
这样一来,窃贼就无法在其它应用中使用窃取的密码,要知道,很多用户在所有系统中都使用相同的密码,风险超大)。
## 安装 `passlib`
PassLib 是一个用于处理哈希密码的很棒的 Python 包。
Passlib 是处理密码哈希的 Python 包。
它支持多安全哈希算法及配合算法使用的实用程序
它支持多安全哈希算法及配套工具
推荐的算法是 Bcrypt
本教程推荐的算法是 **Bcrypt**
因此,安装附带 Bcrypt 的 PassLib
因此,请先安装附带 Bcrypt 的 PassLib
<div class="termy">
@@ -83,46 +84,49 @@ $ pip install passlib[bcrypt]
</div>
!!! tip
使用 `passlib`,你甚至可以将其配置为能够读取 DjangoFlask 的安全扩展或许多其他工具创建的密码。
!!! tip "提示"
因此,你将能够,举个例子,将数据库中来自 Django 应用的数据共享给一个 FastAPI 应用。或者使用同一数据库但逐渐将应用从 Django 迁移到 FastAPI
`passlib` 甚至可以读取 Django、Flask 的安全插件等工具创建的密码
而你的用户将能够同时从 Django 应用 FastAPI 应用登录
例如,把 Django 应用的数据共享给 FastAPI 应用的数据库。或利用同一个数据库,可以逐步把应用从 Django 迁移到 FastAPI
## 哈希并校验密码
并且,用户可以同时从 Django 应用或 FastAPI 应用登录。
`passlib` 导入我们需要的工具。
## 密码哈希与校验
创建一个 PassLib 「上下文」。这将用于哈希和校验密码
`passlib` 导入所需工具
!!! tip
PassLib 上下文还具有使用不同哈希算法的功能,包括仅允许用于校验的已弃用的旧算法等。
创建用于密码哈希和身份校验的 PassLib **上下文**
例如你可以使用它来读取和校验由另一个系统例如Django生成的密码但是使用其他算法例如 Bcrypt 生成新的密码哈希值。
!!! tip "提示"
并同时兼容所有的这些功能
PassLib 上下文还支持使用不同哈希算法的功能,包括只能校验的已弃用旧算法等
创建一个工具函数以哈希来自用户的密码。
例如,用它读取和校验其它系统(如 Django生成的密码但要使用其它算法如 Bcrypt生成新的哈希密码。
然后创建另一个工具函数,用于校验接收的密码是否与存储的哈希值匹配
同时,这些功能都是兼容的
再创建另一个工具函数用于认证并返回用户
接下来,创建三个工具函数,其中一个函数用于哈希用户的密码
第一个函数用于校验接收的密码是否匹配存储的哈希值。
第三个函数用于身份验证,并返回用户。
```Python hl_lines="7 48 55-56 59-60 69-75"
{!../../../docs_src/security/tutorial004.py!}
```
!!! note
如果你查看新的(伪)数据库 `fake_users_db`,你将看到哈希后的密码现在的样子:`"$2b$12$EixZaYVK1fsbw1ZfbX3OXePaWxn96p36WQoeG6Lruj3vjPGga31lW"`。
!!! note "笔记"
查看新的(伪)数据库 `fake_users_db`,就能看到哈希后的密码:`"$2b$12$EixZaYVK1fsbw1ZfbX3OXePaWxn96p36WQoeG6Lruj3vjPGga31lW"`。
## 处理 JWT 令牌
导入已安装的模块。
创建一个随机密钥,该密钥将用于 JWT 令牌进行签名。
创建用于 JWT 令牌签名的随机密钥
要生成一个安全的随机密钥,可使用以下命令:
使用以下命令,生成安全的随机密钥
<div class="termy">
@@ -134,15 +138,15 @@ $ openssl rand -hex 32
</div>
然后将输出复制到变量SECRET_KEY」 中(不要使用示例中的这个)
然后,把生成的密钥复制到变量**SECRET_KEY**,注意,不要使用本例所示的密钥
创建用于设定 JWT 令牌签名算法的变量 ALGORITHM」,并将其设置为 `"HS256"`。
创建定 JWT 令牌签名算法的变量 **ALGORITHM**,本例中的值为 `"HS256"`。
创建一个设置令牌过期时间的变量。
创建设置令牌过期时间的变量。
定义一个将在令牌端点中用于响应的 Pydantic 模型。
定义令牌端点响应的 Pydantic 模型。
创建一个生成新的访问令牌的工具函数。
创建生成新的访问令牌的工具函数。
```Python hl_lines="6 12-14 28-30 78-86"
{!../../../docs_src/security/tutorial004.py!}
@@ -150,11 +154,11 @@ $ openssl rand -hex 32
## 更新依赖项
更新 `get_current_user` 以接收与之前相同的令牌,但这次使用的是 JWT 令牌。
更新 `get_current_user` 以接收与之前相同的令牌,但这用的是 JWT 令牌。
解码接收到的令牌,对其进行校验,然后返回当前用户。
解码并校验接收到的令牌,然后返回当前用户。
如果令牌无效,立即返回一个 HTTP 错误。
如果令牌无效,则直接返回 HTTP 错误。
```Python hl_lines="89-106"
{!../../../docs_src/security/tutorial004.py!}
@@ -162,57 +166,57 @@ $ openssl rand -hex 32
## 更新 `/token` *路径操作*
使用令牌过期时间创建一个 `timedelta` 对象。
用令牌过期时间创建 `timedelta` 对象。
创建一个真实的 JWT 访问令牌并返回它
创建并返回真正的 JWT 访问令牌。
```Python hl_lines="115-128"
{!../../../docs_src/security/tutorial004.py!}
```
### 关于 JWT 「主题」 `sub` 的技术细节
### JWT `sub` 的技术细节
JWT 规范中提到有一个 `sub` 键,值为该令牌的主题。
JWT 规范还包括 `sub` 键,值令牌的主题。
使用它并不是必须的,但这是你放置用户标识的地方,所以我们在示例中使用了
该键是可选的,但要把用户标识放在这个键里,所以本例使用了该键
除了识别用户并允许他们直接在你的 API 上执行操作之外JWT 还可用于其事情。
除了识别用户与许可用户在 API 上直接执行操作之外JWT 还可用于其事情。
例如,你可以识别一个 「汽车」 或 「博客文章」
例如,识别**汽车**或**博客**
然后你可以添加关于该实体的权限,比如「驾驶」(汽车)或「编辑」(博客)。
接着,为实体添加权限,比如**驾驶**(汽车)或**编辑**(博客)。
然后,你可以将 JWT 令牌交给用户(或机器人),他们可以使用它来执行这些操作(驾驶汽车,或编辑博客文章),甚至不需要有一个账户,只需使用你的 API 为其生成的 JWT 令牌。
然后, JWT 令牌交给用户(或机器人),他们可以执行驾驶汽车,或编辑博客等操作。无需注册账户,只要有 API 生成的 JWT 令牌就可以
使用这样的思路JWT 可以用于更复杂的场景。
同理JWT 可以用于更复杂的场景。
在这些情况下,个实体可能相同的 ID,比如说 `foo`(一个用户 `foo`一辆车 `foo`一篇博客文章 `foo`
在这些情况下,个实体的 ID 可能相同的,以 ID `foo` 为例,用户的 ID 是 `foo`,车的 ID 是 `foo`博客的 ID 也是 `foo`。
因此,为了避免 ID 冲突,当为用户创建 JWT 令牌时,可以 `sub` 键的值加上前缀,例如 `username:`。所以,在这个例子中,`sub` 的值可以是:`username:johndoe`。
为了避免 ID 冲突,在给用户创建 JWT 令牌时,可以 `sub` 键的值加上前缀,例如 `username:`。因此,在本例中,`sub` 的值可以是:`username:johndoe`。
要记住的重点`sub` 键在整个应用程序中应该有一个唯一的标识符,而且应该是一个字符串。
注意,划重点,`sub` 键在整个应用中应该有一个唯一的标识符,而且应该是字符串。
## 检查效果
## 检查
运行服务器并访问文档: <a href="http://127.0.0.1:8000/docs" class="external-link" target="_blank">http://127.0.0.1:8000/docs</a>。
你会看到如下用户界面:
可以看到如下用户界面:
<img src="https://fastapi.tiangolo.com/img/tutorial/security/image07.png">
像以前一样对应用程序进行认证
用与上一章同样的方式实现应用授权
使用如下凭证:
用户名: `johndoe`
密码: `secret`
用户名: `johndoe` 密码: `secret`
!!! check
请注意,代码中没有任何地方记录了明文密码 「`secret`」,我们只保存了其哈希值。
!!! check "检查"
注意,代码中没有明文密码**`secret`**,只保存了它的哈希值。
<img src="https://fastapi.tiangolo.com/img/tutorial/security/image08.png">
访问 `/users/me/` 端点,你将获得如下响应:
调用 `/users/me/` 端点,收到下面的响应:
```JSON
{
@@ -225,41 +229,42 @@ JWT 的规范中提到有一个 `sub` 键,值为该令牌的主题。
<img src="https://fastapi.tiangolo.com/img/tutorial/security/image09.png">
如果你打开开发者工具,将看到数据是如何发送的并且其中仅包含了令牌,只有第一个请求发送密码以校验用户身份并获取访问令牌,但之后不会再发送密码:
打开浏览器的开发者工具,查看数据是怎么发送的,而且数据里只包含了令牌,只有验证用户的第一个请求发送密码并获取访问令牌,但之后不会再发送密码:
<img src="https://fastapi.tiangolo.com/img/tutorial/security/image10.png">
!!! note
注意请求中的 `Authorization` 首部,其值以 `Bearer` 开头。
!!! note "笔记"
## 使用 `scopes` 的进阶用法
注意,请求中 `Authorization` 响应头的值以 `Bearer` 开头。
OAuth2 具有「作用域」的概念。
## `scopes` 高级用法
你可以使用它们向 JWT 令牌添加一组特定的权限
OAuth2 支持**`scopes`**(作用域)
然后,你可以将此令牌直接提供给用户或第三方,使其在一些限制下与你的 API 进行交互
**`scopes`**为 JWT 令牌添加指定权限
你可以在之后的**进阶用户指南**中了解如何使用它们以及如何将它们集成到 **FastAPI** 中
让持有令牌的用户或第三方在指定限制条件下与 API 交互
## 总结
**高级用户指南**中将介绍如何使用 `scopes`,及如何把 `scopes` 集成至 **FastAPI**。
通过目前你所看到的,你可以使用像 OAuth2 和 JWT 这样的标准来构建一个安全的 **FastAPI** 应用程序。
## 小结
在几乎所有的框架中,处理安全性问题都很容易成为一个相当复杂的话题
至此,您可以使用 OAuth2 和 JWT 等标准配置安全的 **FastAPI** 应用
许多高度简化了安全流程的软件包不得不在数据模型、数据库和可用功能上做出很多妥协。而这些过于简化流程的软件包中,有些其实隐含了安全漏洞
几乎在所有框架中,处理安全问题很快都会变得非常复杂
有些包为了简化安全流,不得不在数据模型、数据库和功能上做出妥协。而有些过于简化的软件包其实存在了安全隐患。
---
**FastAPI** 不任何数据库、数据模型或工具做任何妥协。
**FastAPI** 不任何数据库、数据模型或工具做妥协。
它给了你所有的灵活性来选择最适合项目的前者
开发者可以灵活选择最适合项目的安全机制
可以直接使用许多维护良好且使用广泛的包,如 `passlib` 和 `python-jose`因为 **FastAPI** 不需要任何复杂机制集成外部包。
可以直接使用 `passlib` 和 `python-jose` 等维护良好、使用广泛的包,这是因为 **FastAPI** 不需要任何复杂机制,就能集成外部包。
但它为你提供了一些工具,在不影响灵活性、健壮性和安全的前提下,尽可能地简化这个过程
而且,**FastAPI** 还提供了一些工具,在不影响灵活、稳定和安全的前提下,尽可能地简化安全机制
而且你可以用相对简单的方式使用和实现安全、标准的协议,比如 OAuth2
**FastAPI** 还支持以相对简单的方式使用 OAuth2 等安全、标准的协议
你可以在**进阶用户指南**中了解更多关于如何使用 OAuth2 「作用域」的信息,以实现更精的权限系统,并同样遵循这些标准。带有作用域的 OAuth2 是很多大的认证提供商使用的机制,比如 Facebook、Google、GitHub、微软、Twitter 等,授权第三方应用代表用户与他们的 API 进行交互。
**高级用户指南**中详细介绍了 OAuth2**`scopes`**的内容,遵循同样的标准,实现更精的权限系统。OAuth2 的作用域是脸书、谷歌、GitHub、微软、推特等第三方身份验证应用使用的机制让用户授权第三方应用与 API 交互。

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,6 +1,6 @@
"""FastAPI framework, high performance, easy to learn, fast to code, ready for production"""
__version__ = "0.85.2"
__version__ = "0.87.0"
from starlette import status as status
@@ -8,6 +8,7 @@ from .applications import FastAPI as FastAPI
from .background import BackgroundTasks as BackgroundTasks
from .datastructures import UploadFile as UploadFile
from .exceptions import HTTPException as HTTPException
from .exceptions import WebSocketException as WebSocketException
from .param_functions import Body as Body
from .param_functions import Cookie as Cookie
from .param_functions import Depends as Depends

View File

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

View File

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

View File

@@ -3,6 +3,7 @@ import dataclasses
import email.message
import inspect
import json
from contextlib import AsyncExitStack
from enum import Enum, IntEnum
from typing import (
Any,
@@ -190,6 +191,9 @@ def get_request_handler(
if body_field:
if is_body_form:
body = await request.form()
stack = request.scope.get("fastapi_astack")
assert isinstance(stack, AsyncExitStack)
stack.push_async_callback(body.close)
else:
body_bytes = await request.body()
if body_bytes:
@@ -697,7 +701,7 @@ class APIRouter(routing.Router):
), "A path prefix must not end with '/', as the routes will start with '/'"
else:
for r in router.routes:
path = getattr(r, "path")
path = getattr(r, "path") # noqa: B009
name = getattr(r, "name", "unknown")
if path is not None and not path:
raise Exception(

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -34,11 +34,12 @@ classifiers = [
"Programming Language :: Python :: 3.8",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Topic :: Internet :: WWW/HTTP :: HTTP Servers",
"Topic :: Internet :: WWW/HTTP",
]
dependencies = [
"starlette==0.20.4",
"starlette==0.21.0",
"pydantic >=1.6.2,!=1.7,!=1.7.1,!=1.7.2,!=1.7.3,!=1.8,!=1.8.1,<2.0.0",
]
dynamic = ["version"]
@@ -50,12 +51,11 @@ Documentation = "https://fastapi.tiangolo.com/"
[project.optional-dependencies]
test = [
"pytest >=7.1.3,<8.0.0",
"pytest-cov >=2.12.0,<5.0.0",
"coverage[toml] >= 6.5.0,<7.0",
"mypy ==0.982",
"flake8 >=3.8.3,<6.0.0",
"ruff ==0.0.114",
"black == 22.8.0",
"isort >=5.0.6,<6.0.0",
"requests >=2.24.0,<3.0.0",
"httpx >=0.23.0,<0.24.0",
"email_validator >=1.1.1,<2.0.0",
# TODO: once removing databases from tutorial, upgrade SQLAlchemy
@@ -87,21 +87,20 @@ doc = [
"pyyaml >=5.3.1,<7.0.0",
]
dev = [
"autoflake >=1.4.0,<2.0.0",
"flake8 >=3.8.3,<6.0.0",
"ruff ==0.0.114",
"uvicorn[standard] >=0.12.0,<0.19.0",
"pre-commit >=2.17.0,<3.0.0",
]
all = [
"requests >=2.24.0,<3.0.0",
"jinja2 >=2.11.2,<4.0.0",
"python-multipart >=0.0.5,<0.0.6",
"itsdangerous >=1.1.0,<3.0.0",
"pyyaml >=5.3.1,<7.0.0",
"ujson >=4.0.1,!=4.0.2,!=4.1.0,!=4.2.0,!=4.3.0,!=5.0.0,!=5.1.0,<6.0.0",
"orjson >=3.2.1,<4.0.0",
"email_validator >=1.1.1,<2.0.0",
"uvicorn[standard] >=0.12.0,<0.19.0",
"httpx >=0.23.0",
"jinja2 >=2.11.2",
"python-multipart >=0.0.5",
"itsdangerous >=1.1.0",
"pyyaml >=5.3.1",
"ujson >=4.0.1,!=4.0.2,!=4.1.0,!=4.2.0,!=4.3.0,!=5.0.0,!=5.1.0",
"orjson >=3.2.1",
"email_validator >=1.1.1",
"uvicorn[standard] >=0.12.0",
]
[tool.hatch.version]
@@ -136,9 +135,50 @@ filterwarnings = [
# TODO: needed by asyncio in Python 3.9.7 https://bugs.python.org/issue45097, try to remove on 3.9.8
'ignore:The loop argument is deprecated since Python 3\.8, and scheduled for removal in Python 3\.10:DeprecationWarning:asyncio',
'ignore:starlette.middleware.wsgi is deprecated and will be removed in a future release\..*:DeprecationWarning:starlette',
# TODO: remove after upgrading HTTPX to a version newer than 0.23.0
# Including PR: https://github.com/encode/httpx/pull/2309
"ignore:'cgi' is deprecated:DeprecationWarning",
# For passlib
"ignore:'crypt' is deprecated and slated for removal in Python 3.13:DeprecationWarning",
# see https://trio.readthedocs.io/en/stable/history.html#trio-0-22-0-2022-09-28
"ignore:You seem to already have a custom.*:RuntimeWarning:trio",
"ignore::trio.TrioDeprecationWarning",
# TODO remove pytest-cov
'ignore::pytest.PytestDeprecationWarning:pytest_cov',
]
[tool.coverage.run]
parallel = true
source = [
"docs_src",
"tests",
"fastapi"
]
context = '${CONTEXT}'
[tool.ruff]
select = [
"E", # pycodestyle errors
"W", # pycodestyle warnings
"F", # pyflakes
# "I", # isort
"C", # flake8-comprehensions
"B", # flake8-bugbear
]
ignore = [
"E501", # line too long, handled by black
"B008", # do not perform function calls in argument defaults
]
[tool.ruff.per-file-ignores]
"__init__.py" = ["F401"]
"docs_src/dependencies/tutorial007.py" = ["F821"]
"docs_src/dependencies/tutorial008.py" = ["F821"]
"docs_src/dependencies/tutorial009.py" = ["F821"]
"docs_src/dependencies/tutorial010.py" = ["F821"]
"docs_src/custom_response/tutorial007.py" = ["B007"]
"docs_src/dataclasses/tutorial003.py" = ["I001"]
[tool.ruff.isort]
known-third-party = ["fastapi", "pydantic", "starlette"]

View File

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

View File

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

View File

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

View File

@@ -3,4 +3,7 @@
set -e
set -x
bash scripts/test.sh --cov-report=html ${@}
bash scripts/test.sh ${@}
coverage combine
coverage report --show-missing
coverage html

View File

@@ -6,4 +6,4 @@ set -x
# Check README.md is up to date
python ./scripts/docs.py verify-readme
export PYTHONPATH=./docs_src
pytest --cov=fastapi --cov=tests --cov=docs_src --cov-report=term-missing:skip-covered --cov-report=xml tests ${@}
coverage run -m pytest tests ${@}

View File

@@ -110,6 +110,6 @@ def test_route_classes():
for r in app.router.routes:
assert isinstance(r, Route)
routes[r.path] = r
assert getattr(routes["/a/"], "x_type") == "A"
assert getattr(routes["/a/b/"], "x_type") == "B"
assert getattr(routes["/a/b/c/"], "x_type") == "C"
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

View File

@@ -1,6 +1,10 @@
from pathlib import Path
from typing import List
import pytest
from fastapi import UploadFile
from fastapi import FastAPI, UploadFile
from fastapi.datastructures import Default
from fastapi.testclient import TestClient
def test_upload_file_invalid():
@@ -20,3 +24,25 @@ def test_default_placeholder_bool():
placeholder_b = Default("")
assert placeholder_a
assert not placeholder_b
def test_upload_file_is_closed(tmp_path: Path):
path = tmp_path / "test.txt"
path.write_bytes(b"<file content>")
app = FastAPI()
testing_file_store: List[UploadFile] = []
@app.post("/uploadfile/")
def create_upload_file(file: UploadFile):
testing_file_store.append(file)
return {"filename": file.filename}
client = TestClient(app)
with path.open("rb") as file:
response = client.post("/uploadfile/", files={"file": file})
assert response.status_code == 200, response.text
assert response.json() == {"filename": "test.txt"}
assert testing_file_store
assert testing_file_store[0].file.closed

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -47,10 +47,10 @@ openapi_schema = {
"responses": {
"200": {
"content": {"application/json": {"schema": {}}},
"description": "Successful " "Response",
"description": "Successful Response",
}
},
"summary": "No Body " "Status " "Code " "Exception",
"summary": "No Body Status Code Exception",
}
},
"/http-no-body-statuscode-with-detail-exception": {
@@ -59,7 +59,7 @@ openapi_schema = {
"responses": {
"200": {
"content": {"application/json": {"schema": {}}},
"description": "Successful " "Response",
"description": "Successful Response",
}
},
"summary": "No Body Status Code With Detail Exception",

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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