diff --git a/.github/actions/notify-translations/Dockerfile b/.github/actions/notify-translations/Dockerfile new file mode 100644 index 0000000000000..fa4197e6a88d1 --- /dev/null +++ b/.github/actions/notify-translations/Dockerfile @@ -0,0 +1,7 @@ +FROM python:3.7 + +RUN pip install httpx PyGithub "pydantic==1.5.1" "pyyaml>=5.3.1,<6.0.0" + +COPY ./app /app + +CMD ["python", "/app/main.py"] diff --git a/.github/actions/notify-translations/action.yml b/.github/actions/notify-translations/action.yml new file mode 100644 index 0000000000000..c3579977c53d9 --- /dev/null +++ b/.github/actions/notify-translations/action.yml @@ -0,0 +1,10 @@ +name: "Notify Translations" +description: "Notify in the issue for a translation when there's a new PR available" +author: "Sebastián Ramírez " +inputs: + token: + description: 'Token, to read the GitHub API. Can be passed in using {{ secrets.GITHUB_TOKEN }}' + required: true +runs: + using: 'docker' + image: 'Dockerfile' diff --git a/.github/actions/notify-translations/app/main.py b/.github/actions/notify-translations/app/main.py new file mode 100644 index 0000000000000..79850513aaba9 --- /dev/null +++ b/.github/actions/notify-translations/app/main.py @@ -0,0 +1,87 @@ +import logging +from pathlib import Path +from typing import Dict, Optional + +import yaml +from github import Github +from pydantic import BaseModel, BaseSettings, SecretStr + +awaiting_label = "awaiting review" +lang_all_label = "lang-all" +approved_label = "approved-2" +translations_path = Path(__file__).parent / "translations.yml" + + +class Settings(BaseSettings): + github_repository: str + input_token: SecretStr + github_event_path: Path + github_event_name: Optional[str] = None + input_debug: Optional[bool] = False + + +class PartialGitHubEventIssue(BaseModel): + number: int + + +class PartialGitHubEvent(BaseModel): + pull_request: PartialGitHubEventIssue + + +if __name__ == "__main__": + settings = Settings() + if settings.input_debug: + logging.basicConfig(level=logging.DEBUG) + else: + logging.basicConfig(level=logging.INFO) + logging.debug(f"Using config: {settings.json()}") + g = Github(settings.input_token.get_secret_value()) + repo = g.get_repo(settings.github_repository) + if not settings.github_event_path.is_file(): + raise RuntimeError( + f"No github event file available at: {settings.github_event_path}" + ) + contents = settings.github_event_path.read_text() + github_event = PartialGitHubEvent.parse_raw(contents) + translations_map: Dict[str, int] = yaml.safe_load(translations_path.read_text()) + logging.debug(f"Using translations map: {translations_map}") + pr = repo.get_pull(github_event.pull_request.number) + logging.debug(f"Processing PR: {pr.number}") + if pr.state == "open": + logging.debug(f"PR is open: {pr.number}") + label_strs = set([label.name for label in pr.get_labels()]) + if lang_all_label in label_strs and awaiting_label in label_strs: + logging.info( + f"This PR seems to be a language translation and awaiting reviews: {pr.number}" + ) + if approved_label in label_strs: + message = ( + f"It seems this PR already has the approved label: {pr.number}" + ) + logging.error(message) + raise RuntimeError(message) + langs = [] + for label in label_strs: + if label.startswith("lang-") and not label == lang_all_label: + langs.append(label[5:]) + for lang in langs: + if lang in translations_map: + num = translations_map[lang] + logging.info(f"Found a translation issue for language: {lang} in issue: {num}") + issue = repo.get_issue(num) + message = f"Good news everyone! 😉 There's a new translation PR to be reviewed: #{pr.number} 🎉" + already_notified = False + logging.info(f"Checking current comments in issue: {num} to see if already notified about this PR: {pr.number}") + for comment in issue.get_comments(): + if message in comment.body: + already_notified = True + if not already_notified: + logging.info(f"Writing comment in issue: {num} about PR: {pr.number}") + issue.create_comment(message) + else: + logging.info(f"Issue: {num} was already notified of PR: {pr.number}") + else: + logging.info( + f"Changing labels in a closed PR doesn't trigger comments, PR: {pr.number}" + ) + logging.info("Finished") diff --git a/.github/actions/notify-translations/app/translations.yml b/.github/actions/notify-translations/app/translations.yml new file mode 100644 index 0000000000000..3f53adb6580b3 --- /dev/null +++ b/.github/actions/notify-translations/app/translations.yml @@ -0,0 +1,14 @@ +pt: 1211 +es: 1218 +zh: 1228 +ru: 1362 +it: 1556 +ja: 1572 +uk: 1748 +tr: 1892 +fr: 1972 +ko: 2017 +sq: 2041 +pl: 3169 +de: 3716 +id: 3717 diff --git a/.github/actions/people/app/main.py b/.github/actions/people/app/main.py index 9630d55bccb02..dc0bbc4c0a0d0 100644 --- a/.github/actions/people/app/main.py +++ b/.github/actions/people/app/main.py @@ -476,21 +476,16 @@ def get_top_users( ) tiers = get_individual_sponsors(settings=settings) - sponsors_50 = [] - for login, sponsor in tiers[50].items(): - sponsors_50.append( - {"login": login, "avatarUrl": sponsor.avatarUrl, "url": sponsor.url} - ) keys = list(tiers.keys()) keys.sort(reverse=True) sponsors = [] for key in keys: - if key >= 50: - continue + sponsor_group = [] for login, sponsor in tiers[key].items(): - sponsors.append( + sponsor_group.append( {"login": login, "avatarUrl": sponsor.avatarUrl, "url": sponsor.url} ) + sponsors.append(sponsor_group) people = { "maintainers": maintainers, @@ -498,16 +493,21 @@ def get_top_users( "last_month_active": last_month_active, "top_contributors": top_contributors, "top_reviewers": top_reviewers, - "sponsors_50": sponsors_50, + } + github_sponsors = { "sponsors": sponsors, } people_path = Path("./docs/en/data/people.yml") + github_sponsors_path = Path("./docs/en/data/github_sponsors.yml") people_old_content = people_path.read_text(encoding="utf-8") - new_content = yaml.dump(people, sort_keys=False, width=200, allow_unicode=True) - if people_old_content == new_content: + github_sponsors_old_content = github_sponsors_path.read_text(encoding="utf-8") + new_people_content = yaml.dump(people, sort_keys=False, width=200, allow_unicode=True) + new_github_sponsors_content = yaml.dump(github_sponsors, sort_keys=False, width=200, allow_unicode=True) + if people_old_content == new_people_content and github_sponsors_old_content == new_github_sponsors_content: logging.info("The FastAPI People data hasn't changed, finishing.") sys.exit(0) - people_path.write_text(new_content, encoding="utf-8") + people_path.write_text(new_people_content, encoding="utf-8") + github_sponsors_path.write_text(new_github_sponsors_content, encoding="utf-8") logging.info("Setting up GitHub Actions git user") subprocess.run(["git", "config", "user.name", "github-actions"], check=True) subprocess.run( diff --git a/.github/workflows/notify-translations.yml b/.github/workflows/notify-translations.yml new file mode 100644 index 0000000000000..7e414ab95e27a --- /dev/null +++ b/.github/workflows/notify-translations.yml @@ -0,0 +1,21 @@ +name: Notify Translations + +on: + pull_request_target: + types: + - labeled + +jobs: + notify-translations: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + # Allow debugging with tmate + - name: Setup tmate session + uses: mxschmitt/action-tmate@v3 + if: ${{ github.event_name == 'workflow_dispatch' && github.event.inputs.debug_enabled }} + with: + limit-access-to-actor: true + - uses: ./.github/actions/notify-translations + with: + token: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index f346cab6be363..1867cbb00bcbb 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -10,9 +10,9 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - python-version: [3.6, 3.7, 3.8] + python-version: [3.6, 3.7, 3.8, 3.9] fail-fast: false - + steps: - uses: actions/checkout@v2 - name: Set up Python diff --git a/README.md b/README.md index 0fb7cc704f476..bdc8c3b070ff1 100644 --- a/README.md +++ b/README.md @@ -44,11 +44,12 @@ The key features are: - + + diff --git a/docs/de/docs/features.md b/docs/de/docs/features.md new file mode 100644 index 0000000000000..e5b38616f47a4 --- /dev/null +++ b/docs/de/docs/features.md @@ -0,0 +1,204 @@ +# Merkmale + +## FastAPI Merkmale + +**FastAPI** ermöglicht Ihnen folgendes: + +### Basiert auf offenen Standards + +* OpenAPI für API-Erstellung, zusammen mit Deklarationen von Pfad Operationen, Parameter, Nachrichtenrumpf-Anfragen (englisch: body request), Sicherheit, etc. +* Automatische Dokumentation der Datenentitäten mit dem JSON Schema (OpenAPI basiert selber auf dem JSON Schema). +* Entworfen auf Grundlage dieser Standards nach einer sorgfältigen Studie, statt einer nachträglichen Schicht über diesen Standards. +* Dies ermöglicht automatische **Quellcode-Generierung auf Benutzerebene** in vielen Sprachen. + +### Automatische Dokumentation + +Mit einer interaktiven API-Dokumentation und explorativen webbasierten Benutzerschnittstellen. Da FastAPI auf OpenAPI basiert, gibt es hierzu mehrere Optionen, wobei zwei standartmäßig vorhanden sind. + +* Swagger UI, bietet interaktive Exploration: testen und rufen Sie ihre API direkt vom Webbrowser auf. + +![Swagger UI Interaktion](https://fastapi.tiangolo.com/img/index/index-03-swagger-02.png) + +* Alternative API-Dokumentation mit ReDoc. + +![ReDoc](https://fastapi.tiangolo.com/img/index/index-06-redoc-02.png) + +### Nur modernes Python + +Alles basiert auf **Python 3.6 Typ**-Deklarationen (dank Pydantic). Es muss keine neue Syntax gelernt werden, nur standardisiertes modernes Python. + + + +Wenn Sie eine kurze, zweiminütige, Auffrischung in der Benutzung von Python Typ-Deklarationen benötigen (auch wenn Sie FastAPI nicht nutzen), schauen Sie sich diese kurze Einführung an (Englisch): Python Types{.internal-link target=_blank}. + +Sie schreiben Standard-Python mit Typ-Deklarationen: + +```Python +from typing import List, Dict +from datetime import date + +from pydantic import BaseModel + +# Deklariere eine Variable als str +# und bekomme Editor-Unterstütung innerhalb der Funktion +def main(user_id: str): + return user_id + + +# Ein Pydantic model +class User(BaseModel): + id: int + name: str + joined: date +``` + +Dies kann nun wiefolgt benutzt werden: + +```Python +my_user: User = User(id=3, name="John Doe", joined="2018-07-19") + +second_user_data = { + "id": 4, + "name": "Mary", + "joined": "2018-11-30", +} + +my_second_user: User = User(**second_user_data) +``` + +!!! info + `**second_user_data` bedeutet: + + Übergebe die Schlüssel und die zugehörigen Werte des `second_user_data` Datenwörterbuches direkt als Schlüssel-Wert Argumente, äquivalent zu: `User(id=4, name="Mary", joined="2018-11-30")` + +### Editor Unterstützung + +FastAPI wurde so entworfen, dass es einfach und intuitiv zu benutzen ist; alle Entscheidungen wurden auf mehreren Editoren getestet (sogar vor der eigentlichen Implementierung), um so eine best mögliche Entwicklererfahrung zu gewährleisten. + +In der letzen Python Entwickler Umfrage stellte sich heraus, dass die meist genutzte Funktion die "Autovervollständigung" ist. + +Die gesamte Struktur von **FastAPI** soll dem gerecht werden. Autovervollständigung funktioniert überall. + +Sie müssen selten in die Dokumentation schauen. + +So kann ihr Editor Sie unterstützen: + +* in Visual Studio Code: + +![editor support](https://fastapi.tiangolo.com/img/vscode-completion.png) + +* in PyCharm: + +![editor support](https://fastapi.tiangolo.com/img/pycharm-completion.png) + +Sie bekommen Autovervollständigung an Stellen, an denen Sie dies vorher nicht für möglich gehalten hätten. Zum Beispiel der `price` Schlüssel aus einem JSON Datensatz (dieser könnte auch verschachtelt sein) aus einer Anfrage. + +Hierdurch werden Sie nie wieder einen falschen Schlüsselnamen benutzen und sparen sich lästiges Suchen in der Dokumentation, um beispielsweise herauszufinden ob Sie `username` oder `user_name` als Schlüssel verwenden. + +### Kompakt + +FastAPI nutzt für alles sensible **Standard-Einstellungen**, welche optional überall konfiguriert werden können. Alle Parameter können ganz genau an Ihre Bedürfnisse angepasst werden, sodass sie genau die API definieren können, die sie brachen. + +Aber standartmäßig, **"funktioniert einfach"** alles. + +### Validierung + +* Validierung für die meisten (oder alle?) Python **Datentypen**, hierzu gehören: + * JSON Objekte (`dict`). + * JSON Listen (`list`), die den Typ ihrer Elemente definieren. + * Zeichenketten (`str`), mit definierter minimaler und maximaler Länge. + * Zahlen (`int`, `float`) mit minimaler und maximaler Größe, usw. + +* Validierung für ungewögnliche Typen, wie: + * URL. + * Email. + * UUID. + * ... und andere. + +Die gesamte Validierung übernimmt das etablierte und robuste **Pydantic**. + +### Sicherheit und Authentifizierung + +Sicherheit und Authentifizierung integriert. Ohne einen Kompromiss aufgrund einer Datenbank oder den Datenentitäten. + +Unterstützt alle von OpenAPI definierten Sicherheitsschemata, hierzu gehören: + +* HTTP Basis Authentifizierung. +* **OAuth2** (auch mit **JWT Zugriffstokens**). Schauen Sie sich hierzu dieses Tutorial an: [OAuth2 mit JWT](tutorial/security/oauth2-jwt.md){.internal-link target=_blank}. +* API Schlüssel in: + * Kopfzeile (HTTP Header). + * Anfrageparametern. + * Cookies, etc. + +Zusätzlich gibt es alle Sicherheitsfunktionen von Starlette (auch **session cookies**). + +Alles wurde als wiederverwendbare Werkzeuge und Komponenten geschaffen, die einfach in ihre Systeme, Datenablagen, relationale und nicht-relationale Datenbanken, ..., integriert werden können. + +### Einbringen von Abhängigkeiten (meist: Dependency Injection) + +FastAPI enthält ein extrem einfaches, aber extrem mächtiges Dependency Injection System. + +* Selbst Abhängigkeiten können Abhängigkeiten haben, woraus eine Hierachie oder ein **"Graph" von Abhängigkeiten** entsteht. +* **Automatische Umsetzung** durch FastAPI. +* Alle abhängigen Komponenten könnten Daten von Anfragen, **Erweiterungen der Pfadoperations-**Einschränkungen und der automatisierten Dokumentation benötigen. +* **Automatische Validierung** selbst für *Pfadoperationen*-Parameter, die in den Abhängigkeiten definiert wurden. +* Unterstütz komplexe Benutzerauthentifizierungssysteme, mit **Datenbankverbindungen**, usw. +* **Keine Kompromisse** bei Datenbanken, Eingabemasken, usw. Sondern einfache Integration von allen. + +### Unbegrenzte Erweiterungen + +Oder mit anderen Worten, sie werden nicht benötigt. Importieren und nutzen Sie Quellcode nach Bedarf. + +Jede Integration wurde so entworfen, dass sie einfach zu nutzen ist (mit Abhängigkeiten), sodass Sie eine Erweiterung für Ihre Anwendung mit nur zwei Zeilen an Quellcode implementieren können. Hierbei nutzen Sie die selbe Struktur und Syntax, wie bei Pfadoperationen. + +### Getestet + +* 100% Testabdeckung. +* 100% Typen annotiert. +* Verwendet in Produktionsanwendungen. + +## Starlette's Merkmale + +**FastAPI** ist vollkommen kompatibel (und basiert auf) Starlette. Das bedeutet, auch ihr eigner Starlett Quellcode funktioniert. + +`FastAPI` ist eigentlich eine Unterklasse von `Starlette`. Wenn sie also bereits Starlette kennen oder benutzen, können Sie das meiste Ihres Wissen direkt anwenden. + +Mit **FastAPI** bekommen Sie viele von **Starlette**'s Funktionen (da FastAPI nur Starlette auf Steroiden ist): + +* Stark beeindruckende Performanz. Es ist eines der schnellsten Python frameworks, auf Augenhöhe mit **NodeJS** und **Go**. +* **WebSocket**-Unterstützung. +* **GraphQL**-Unterstützung. +* Hintergrundaufgaben im selben Prozess. +* Ereignisse für das Starten und Herunterfahren. +* Testclient basierend auf `requests`. +* **CORS**, GZip, statische Dateien, Antwortfluss. +* **Sitzungs und Cookie** Unterstützung. +* 100% Testabdeckung. +* 100% Typen annotiert. + +## Pydantic's Merkmale + +**FastAPI** ist vollkommen kompatibel (und basiert auf) Pydantic. Das bedeutet, auch jeder zusätzliche Pydantic Quellcode funktioniert. + +Verfügbar sind ebenso externe auf Pydantic basierende Bibliotheken, wie ORMs, ODMs für Datenbanken. + +Daher können Sie in vielen Fällen das Objekt einer Anfrage **direkt zur Datenbank** schicken, weil alles automatisch validiert wird. + +Das selbe gilt auch für die andere Richtung: Sie können jedes Objekt aus der Datenbank **direkt zum Klienten** schicken. + +Mit **FastAPI** bekommen Sie alle Funktionen von **Pydantic** (da FastAPI für die gesamte Datenverarbeitung Pydantic nutzt): + +* **Kein Kopfzerbrechen**: + * Sie müssen keine neue Schemadefinitionssprache lernen. + * Wenn Sie mit Python's Typisierung arbeiten können, können Sie auch mit Pydantic arbeiten. +* Gutes Zusammenspiel mit Ihrer/Ihrem **IDE/linter/Gehirn**: + * Weil Datenstrukturen von Pydantic einfach nur Instanzen ihrer definierten Klassen sind, sollten Autovervollständigung, Linting, mypy und ihre Intuition einwandfrei funktionieren. +* **Schnell**: + * In Vergleichen ist Pydantic schneller als jede andere getestete Bibliothek. +* Validierung von **komplexen Strukturen**: + * Benutzung von hierachischen Pydantic Schemata, Python `typing`’s `List` und `Dict`, etc. + * Validierungen erlauben klare und einfache Datenschemadefinition, überprüft und dokumentiert als JSON Schema. + * Sie können stark **verschachtelte JSON** Objekte haben und diese sind trotzdem validiert und annotiert. +* **Erweiterbar**: + * Pydantic erlaubt die Definition von eigenen Datentypen oder sie können die Validierung mit einer `validator` dekorierten Methode erweitern.. +* 100% Testabdeckung. diff --git a/docs/de/mkdocs.yml b/docs/de/mkdocs.yml index 033326486308c..77882db0327c3 100644 --- a/docs/de/mkdocs.yml +++ b/docs/de/mkdocs.yml @@ -20,6 +20,7 @@ theme: features: - search.suggest - search.highlight + - content.tabs.link icon: repo: fontawesome/brands/github-alt logo: https://fastapi.tiangolo.com/img/icon-white.svg @@ -53,12 +54,13 @@ nav: - tr: /tr/ - uk: /uk/ - zh: /zh/ +- features.md markdown_extensions: - toc: permalink: true - markdown.extensions.codehilite: guess_lang: false -- markdown_include.include: +- mdx_include: base_path: docs - admonition - codehilite @@ -67,7 +69,7 @@ markdown_extensions: custom_fences: - name: mermaid class: mermaid - format: !!python/name:pymdownx.superfences.fence_div_format '' + format: !!python/name:pymdownx.superfences.fence_code_format '' - pymdownx.tabbed extra: social: @@ -120,6 +122,5 @@ extra_css: - https://fastapi.tiangolo.com/css/termynal.css - https://fastapi.tiangolo.com/css/custom.css extra_javascript: -- https://unpkg.com/mermaid@8.4.6/dist/mermaid.min.js - https://fastapi.tiangolo.com/js/termynal.js - https://fastapi.tiangolo.com/js/custom.js diff --git a/docs/en/data/github_sponsors.yml b/docs/en/data/github_sponsors.yml new file mode 100644 index 0000000000000..162a8dbe28d38 --- /dev/null +++ b/docs/en/data/github_sponsors.yml @@ -0,0 +1,379 @@ +sponsors: +- - login: jina-ai + avatarUrl: https://avatars.githubusercontent.com/u/60539444?v=4 + url: https://github.com/jina-ai +- - login: mikeckennedy + avatarUrl: https://avatars.githubusercontent.com/u/2035561?v=4 + url: https://github.com/mikeckennedy + - login: RodneyU215 + avatarUrl: https://avatars.githubusercontent.com/u/3329665?u=ec6a9adf8e7e8e306eed7d49687c398608d1604f&v=4 + url: https://github.com/RodneyU215 + - login: Trivie + avatarUrl: https://avatars.githubusercontent.com/u/8161763?v=4 + url: https://github.com/Trivie + - login: deta + avatarUrl: https://avatars.githubusercontent.com/u/47275976?v=4 + url: https://github.com/deta + - login: investsuite + avatarUrl: https://avatars.githubusercontent.com/u/73833632?v=4 + url: https://github.com/investsuite + - login: vimsoHQ + avatarUrl: https://avatars.githubusercontent.com/u/77627231?v=4 + url: https://github.com/vimsoHQ +- - login: newrelic + avatarUrl: https://avatars.githubusercontent.com/u/31739?v=4 + url: https://github.com/newrelic + - login: qaas + avatarUrl: https://avatars.githubusercontent.com/u/8503759?u=10a6b4391ad6ab4cf9487ce54e3fcb61322d1efc&v=4 + url: https://github.com/qaas +- - login: johnadjei + avatarUrl: https://avatars.githubusercontent.com/u/767860?v=4 + url: https://github.com/johnadjei + - login: wdwinslow + avatarUrl: https://avatars.githubusercontent.com/u/11562137?u=dc01daafb354135603a263729e3d26d939c0c452&v=4 + url: https://github.com/wdwinslow +- - login: kamalgill + avatarUrl: https://avatars.githubusercontent.com/u/133923?u=0df9181d97436ce330e9acf90ab8a54b7022efe7&v=4 + url: https://github.com/kamalgill + - login: grillazz + avatarUrl: https://avatars.githubusercontent.com/u/3415861?u=16d7d0ffa5dfb99f8834f8f76d90e138ba09b94a&v=4 + url: https://github.com/grillazz + - login: tizz98 + avatarUrl: https://avatars.githubusercontent.com/u/5739698?u=f095a3659e3a8e7c69ccd822696990b521ea25f9&v=4 + url: https://github.com/tizz98 + - login: jmaralc + avatarUrl: https://avatars.githubusercontent.com/u/21101214?u=b15a9f07b7cbf6c9dcdbcb6550bbd2c52f55aa50&v=4 + url: https://github.com/jmaralc + - login: AlexandruSimion + avatarUrl: https://avatars.githubusercontent.com/u/71321732?v=4 + url: https://github.com/AlexandruSimion +- - login: samuelcolvin + avatarUrl: https://avatars.githubusercontent.com/u/4039449?u=807390ba9cfe23906c3bf8a0d56aaca3cf2bfa0d&v=4 + url: https://github.com/samuelcolvin + - login: jokull + avatarUrl: https://avatars.githubusercontent.com/u/701?u=0532b62166893d5160ef795c4c8b7512d971af05&v=4 + url: https://github.com/jokull + - login: wshayes + avatarUrl: https://avatars.githubusercontent.com/u/365303?u=07ca03c5ee811eb0920e633cc3c3db73dbec1aa5&v=4 + url: https://github.com/wshayes + - login: koxudaxi + avatarUrl: https://avatars.githubusercontent.com/u/630670?u=507d8577b4b3670546b449c4c2ccbc5af40d72f7&v=4 + url: https://github.com/koxudaxi + - login: falkben + avatarUrl: https://avatars.githubusercontent.com/u/653031?u=0c8d8f33d87f1aa1a6488d3f02105e9abc838105&v=4 + url: https://github.com/falkben + - login: jqueguiner + avatarUrl: https://avatars.githubusercontent.com/u/690878?u=e4835b2a985a0f2d52018e4926cb5a58c26a62e8&v=4 + url: https://github.com/jqueguiner + - login: Mazyod + avatarUrl: https://avatars.githubusercontent.com/u/860511?v=4 + url: https://github.com/Mazyod + - login: ltieman + avatarUrl: https://avatars.githubusercontent.com/u/1084689?u=e69b17de17cb3ca141a17daa7ccbe173ceb1eb17&v=4 + url: https://github.com/ltieman + - login: mrmattwright + avatarUrl: https://avatars.githubusercontent.com/u/1277725?v=4 + url: https://github.com/mrmattwright + - login: westonsteimel + avatarUrl: https://avatars.githubusercontent.com/u/1593939?u=0f2c0e3647f916fe295d62fa70da7a4c177115e3&v=4 + url: https://github.com/westonsteimel + - login: timdrijvers + avatarUrl: https://avatars.githubusercontent.com/u/1694939?v=4 + url: https://github.com/timdrijvers + - login: mrgnw + avatarUrl: https://avatars.githubusercontent.com/u/2504532?u=7ec43837a6d0afa80f96f0788744ea6341b89f97&v=4 + url: https://github.com/mrgnw + - login: madisonmay + avatarUrl: https://avatars.githubusercontent.com/u/2645393?u=f22b93c6ea345a4d26a90a3834dfc7f0789fcb63&v=4 + url: https://github.com/madisonmay + - login: saivarunk + avatarUrl: https://avatars.githubusercontent.com/u/2976867?u=71f4385e781e9a9e871a52f2d4686f9a8d69ba2f&v=4 + url: https://github.com/saivarunk + - login: andre1sk + avatarUrl: https://avatars.githubusercontent.com/u/3148093?v=4 + url: https://github.com/andre1sk + - login: Shark009 + avatarUrl: https://avatars.githubusercontent.com/u/3163309?v=4 + url: https://github.com/Shark009 + - login: peterHoburg + avatarUrl: https://avatars.githubusercontent.com/u/3860655?u=f55f47eb2d6a9b495e806ac5a044e3ae01ccc1fa&v=4 + url: https://github.com/peterHoburg + - login: jaredtrog + avatarUrl: https://avatars.githubusercontent.com/u/4381365?v=4 + url: https://github.com/jaredtrog + - login: CINOAdam + avatarUrl: https://avatars.githubusercontent.com/u/4728508?u=34c3d58cb900fed475d0172b436c66a94ad739ed&v=4 + url: https://github.com/CINOAdam + - login: dudil + avatarUrl: https://avatars.githubusercontent.com/u/4785835?u=58b7ea39123e0507f3b2996448a27256b16fd697&v=4 + url: https://github.com/dudil + - login: ennui93 + avatarUrl: https://avatars.githubusercontent.com/u/5300907?u=5b5452725ddb391b2caaebf34e05aba873591c3a&v=4 + url: https://github.com/ennui93 + - login: MacroPower + avatarUrl: https://avatars.githubusercontent.com/u/5648814?u=e13991efd1e03c44c911f919872e750530ded633&v=4 + url: https://github.com/MacroPower + - login: ginomempin + avatarUrl: https://avatars.githubusercontent.com/u/6091865?v=4 + url: https://github.com/ginomempin + - login: iwpnd + avatarUrl: https://avatars.githubusercontent.com/u/6152183?u=b2286006daafff5f991557344fee20b5da59639a&v=4 + url: https://github.com/iwpnd + - login: s3ich4n + avatarUrl: https://avatars.githubusercontent.com/u/6926298?u=ba3025d698e1c986655e776ae383a3d60d9d578e&v=4 + url: https://github.com/s3ich4n + - login: Rehket + avatarUrl: https://avatars.githubusercontent.com/u/7015688?u=3afb0ba200feebbc7f958950e92db34df2a3c172&v=4 + url: https://github.com/Rehket + - login: christippett + avatarUrl: https://avatars.githubusercontent.com/u/7218120?u=434b9d29287d7de25772d94ddc74a9bd6d969284&v=4 + url: https://github.com/christippett + - login: Kludex + avatarUrl: https://avatars.githubusercontent.com/u/7353520?u=cf8455cb899806b774a3a71073f88583adec99f6&v=4 + url: https://github.com/Kludex + - login: Shackelford-Arden + avatarUrl: https://avatars.githubusercontent.com/u/7362263?v=4 + url: https://github.com/Shackelford-Arden + - login: cristeaadrian + avatarUrl: https://avatars.githubusercontent.com/u/9112724?v=4 + url: https://github.com/cristeaadrian + - login: otivvormes + avatarUrl: https://avatars.githubusercontent.com/u/11317418?u=6de1edefb6afd0108c0ad2816bd6efc4464a9c44&v=4 + url: https://github.com/otivvormes + - login: iambobmae + avatarUrl: https://avatars.githubusercontent.com/u/12390270?u=c9a35c2ee5092a9b4135ebb1f91b7f521c467031&v=4 + url: https://github.com/iambobmae + - login: ronaldnwilliams + avatarUrl: https://avatars.githubusercontent.com/u/13632749?u=ac41a086d0728bf66a9d2bee9e5e377041ff44a4&v=4 + url: https://github.com/ronaldnwilliams + - login: pablonnaoji + avatarUrl: https://avatars.githubusercontent.com/u/15187159?u=afc15bd5a4ba9c5c7206bbb1bcaeef606a0932e0&v=4 + url: https://github.com/pablonnaoji + - login: natenka + avatarUrl: https://avatars.githubusercontent.com/u/15850513?u=00d1083c980d0b4ce32835dc07eee7f43f34fd2f&v=4 + url: https://github.com/natenka + - login: la-mar + avatarUrl: https://avatars.githubusercontent.com/u/16618300?u=7755c0521d2bb0d704f35a51464b15c1e2e6c4da&v=4 + url: https://github.com/la-mar + - login: robintully + avatarUrl: https://avatars.githubusercontent.com/u/17059673?u=862b9bb01513f5acd30df97433cb97a24dbfb772&v=4 + url: https://github.com/robintully + - login: ShaulAb + avatarUrl: https://avatars.githubusercontent.com/u/18129076?u=2c8d48e47f2dbee15c3f89c3d17d4c356504386c&v=4 + url: https://github.com/ShaulAb + - login: wedwardbeck + avatarUrl: https://avatars.githubusercontent.com/u/19333237?u=1de4ae2bf8d59eb4c013f21d863cbe0f2010575f&v=4 + url: https://github.com/wedwardbeck + - login: linusg + avatarUrl: https://avatars.githubusercontent.com/u/19366641?u=125e390abef8fff3b3b0d370c369cba5d7fd4c67&v=4 + url: https://github.com/linusg + - login: RedCarpetUp + avatarUrl: https://avatars.githubusercontent.com/u/20360440?v=4 + url: https://github.com/RedCarpetUp + - login: Filimoa + avatarUrl: https://avatars.githubusercontent.com/u/21352040?u=75e02d102d2ee3e3d793e555fa5c63045913ccb0&v=4 + url: https://github.com/Filimoa + - login: raminsj13 + avatarUrl: https://avatars.githubusercontent.com/u/24259406?u=d51f2a526312ebba150a06936ed187ca0727d329&v=4 + url: https://github.com/raminsj13 + - login: comoelcometa + avatarUrl: https://avatars.githubusercontent.com/u/25950317?u=c6751efa038561b9bc5fa56d1033d5174e10cd65&v=4 + url: https://github.com/comoelcometa + - login: veprimk + avatarUrl: https://avatars.githubusercontent.com/u/29689749?u=f8cb5a15a286e522e5b189bc572d5a1a90217fb2&v=4 + url: https://github.com/veprimk + - login: orihomie + avatarUrl: https://avatars.githubusercontent.com/u/29889683?u=6bc2135a52fcb3a49e69e7d50190796618185fda&v=4 + url: https://github.com/orihomie + - login: SaltyCoco + avatarUrl: https://avatars.githubusercontent.com/u/31451104?u=6ee4e17c07d21b7054f54a12fa9cc377a1b24ff9&v=4 + url: https://github.com/SaltyCoco + - login: mauroalejandrojm + avatarUrl: https://avatars.githubusercontent.com/u/31569442?u=cdada990a1527926a36e95f62c30a8b48bbc49a1&v=4 + url: https://github.com/mauroalejandrojm + - login: bulkw4r3 + avatarUrl: https://avatars.githubusercontent.com/u/35562532?u=0b812a14a02de14bf73d05fb2b2760a67bacffc2&v=4 + url: https://github.com/bulkw4r3 + - login: ybressler + avatarUrl: https://avatars.githubusercontent.com/u/40807730?u=6621dc9ab53b697912ab2a32211bb29ae90a9112&v=4 + url: https://github.com/ybressler + - login: dbanty + avatarUrl: https://avatars.githubusercontent.com/u/43723790?u=0cf33e4f40efc2ea206a1189fd63a11344eb88ed&v=4 + url: https://github.com/dbanty + - login: dudikbender + avatarUrl: https://avatars.githubusercontent.com/u/53487583?u=494f85229115076121b3639a3806bbac1c6ae7f6&v=4 + url: https://github.com/dudikbender + - login: primer-io + avatarUrl: https://avatars.githubusercontent.com/u/62146168?v=4 + url: https://github.com/primer-io + - login: tkrestiankova + avatarUrl: https://avatars.githubusercontent.com/u/67013045?v=4 + url: https://github.com/tkrestiankova + - login: daverin + avatarUrl: https://avatars.githubusercontent.com/u/70378377?u=6d1814195c0de7162820eaad95a25b423a3869c0&v=4 + url: https://github.com/daverin + - login: anthonycepeda + avatarUrl: https://avatars.githubusercontent.com/u/72019805?u=892f700c79f9732211bd5221bf16eec32356a732&v=4 + url: https://github.com/anthonycepeda + - login: an-tho-ny + avatarUrl: https://avatars.githubusercontent.com/u/74874159?v=4 + url: https://github.com/an-tho-ny +- - login: linux-china + avatarUrl: https://avatars.githubusercontent.com/u/46711?v=4 + url: https://github.com/linux-china + - login: jhb + avatarUrl: https://avatars.githubusercontent.com/u/142217?v=4 + url: https://github.com/jhb + - login: yourkin + avatarUrl: https://avatars.githubusercontent.com/u/178984?v=4 + url: https://github.com/yourkin + - login: jmagnusson + avatarUrl: https://avatars.githubusercontent.com/u/190835?v=4 + url: https://github.com/jmagnusson + - login: sakti + avatarUrl: https://avatars.githubusercontent.com/u/196178?u=0110be74c4270244546f1b610334042cd16bb8ad&v=4 + url: https://github.com/sakti + - login: slafs + avatarUrl: https://avatars.githubusercontent.com/u/210173?v=4 + url: https://github.com/slafs + - login: adamghill + avatarUrl: https://avatars.githubusercontent.com/u/317045?u=f1349d5ffe84a19f324e204777859fbf69ddf633&v=4 + url: https://github.com/adamghill + - login: eteq + avatarUrl: https://avatars.githubusercontent.com/u/346587?v=4 + url: https://github.com/eteq + - login: dmig + avatarUrl: https://avatars.githubusercontent.com/u/388564?v=4 + url: https://github.com/dmig + - login: hongqn + avatarUrl: https://avatars.githubusercontent.com/u/405587?u=470b4c04832e45141fd5264d3354845cc9fc6466&v=4 + url: https://github.com/hongqn + - login: rinckd + avatarUrl: https://avatars.githubusercontent.com/u/546002?u=1fcc7e664dc86524a0af6837a0c222829c3fd4e5&v=4 + url: https://github.com/rinckd + - login: hardbyte + avatarUrl: https://avatars.githubusercontent.com/u/855189?u=aa29e92f34708814d6b67fcd47ca4cf2ce1c04ed&v=4 + url: https://github.com/hardbyte + - login: Pytlicek + avatarUrl: https://avatars.githubusercontent.com/u/1430522?u=169dba3bfbc04ed214a914640ff435969f19ddb3&v=4 + url: https://github.com/Pytlicek + - login: okken + avatarUrl: https://avatars.githubusercontent.com/u/1568356?u=0a991a21bdc62e2bea9ad311652f2c45f453dc84&v=4 + url: https://github.com/okken + - login: cbonoz + avatarUrl: https://avatars.githubusercontent.com/u/2351087?u=fd3e8030b2cc9fbfbb54a65e9890c548a016f58b&v=4 + url: https://github.com/cbonoz + - login: Abbe98 + avatarUrl: https://avatars.githubusercontent.com/u/2631719?u=8a064aba9a710229ad28c616549d81a24191a5df&v=4 + url: https://github.com/Abbe98 + - login: rglsk + avatarUrl: https://avatars.githubusercontent.com/u/2768101?u=e349c88673f2155fe021331377c656a9d74bcc25&v=4 + url: https://github.com/rglsk + - login: Atem18 + avatarUrl: https://avatars.githubusercontent.com/u/2875254?v=4 + url: https://github.com/Atem18 + - login: paul121 + avatarUrl: https://avatars.githubusercontent.com/u/3116995?u=6e2d8691cc345e63ee02e4eb4d7cef82b1fcbedc&v=4 + url: https://github.com/paul121 + - login: igorcorrea + avatarUrl: https://avatars.githubusercontent.com/u/3438238?u=c57605077c31a8f7b2341fc4912507f91b4a5621&v=4 + url: https://github.com/igorcorrea + - login: anthcor + avatarUrl: https://avatars.githubusercontent.com/u/3477132?v=4 + url: https://github.com/anthcor + - login: zsinx6 + avatarUrl: https://avatars.githubusercontent.com/u/3532625?u=ba75a5dc744d1116ccfeaaf30d41cb2fe81fe8dd&v=4 + url: https://github.com/zsinx6 + - login: pawamoy + avatarUrl: https://avatars.githubusercontent.com/u/3999221?u=b030e4c89df2f3a36bc4710b925bdeb6745c9856&v=4 + url: https://github.com/pawamoy + - login: spyker77 + avatarUrl: https://avatars.githubusercontent.com/u/4953435?u=03c724c6f8fbab5cd6575b810c0c91c652fa4f79&v=4 + url: https://github.com/spyker77 + - login: JonasKs + avatarUrl: https://avatars.githubusercontent.com/u/5310116?u=98a049f3e1491bffb91e1feb7e93def6881a9389&v=4 + url: https://github.com/JonasKs + - login: holec + avatarUrl: https://avatars.githubusercontent.com/u/6438041?u=f5af71ec85b3a9d7b8139cb5af0512b02fa9ab1e&v=4 + url: https://github.com/holec + - login: BartlomiejRasztabiga + avatarUrl: https://avatars.githubusercontent.com/u/8852711?u=ed213d60f7a423df31ceb1004aa3ec60e612cb98&v=4 + url: https://github.com/BartlomiejRasztabiga + - login: davanstrien + avatarUrl: https://avatars.githubusercontent.com/u/8995957?u=fb2aad2b52bb4e7b56db6d7c8ecc9ae1eac1b984&v=4 + url: https://github.com/davanstrien + - login: and-semakin + avatarUrl: https://avatars.githubusercontent.com/u/9129071?u=ea77ddf7de4bc375d546bf2825ed420eaddb7666&v=4 + url: https://github.com/and-semakin + - login: VivianSolide + avatarUrl: https://avatars.githubusercontent.com/u/9358572?u=ffb2e2ec522a15dcd3f0af1f9fd1df4afe418afa&v=4 + url: https://github.com/VivianSolide + - login: hard-coders + avatarUrl: https://avatars.githubusercontent.com/u/9651103?u=f2d3d2038c55d86d7f9348f4e6c5e30191e4ee8b&v=4 + url: https://github.com/hard-coders + - login: satwikkansal + avatarUrl: https://avatars.githubusercontent.com/u/10217535?u=b12d6ef74ea297de9e46da6933b1a5b7ba9e6a61&v=4 + url: https://github.com/satwikkansal + - login: pheanex + avatarUrl: https://avatars.githubusercontent.com/u/10408624?u=5b6bab6ee174aa6e991333e06eb29f628741013d&v=4 + url: https://github.com/pheanex + - login: wotori + avatarUrl: https://avatars.githubusercontent.com/u/10486621?u=0044c295b91694b8c9bccc0a805681f794250f7b&v=4 + url: https://github.com/wotori + - login: JimFawkes + avatarUrl: https://avatars.githubusercontent.com/u/12075115?u=dc58ecfd064d72887c34bf500ddfd52592509acd&v=4 + url: https://github.com/JimFawkes + - login: logan-connolly + avatarUrl: https://avatars.githubusercontent.com/u/16244943?u=8ae66dfbba936463cc8aa0dd7a6d2b4c0cc757eb&v=4 + url: https://github.com/logan-connolly + - login: iPr0ger + avatarUrl: https://avatars.githubusercontent.com/u/19322290?v=4 + url: https://github.com/iPr0ger + - login: ghandic + avatarUrl: https://avatars.githubusercontent.com/u/23500353?u=e2e1d736f924d9be81e8bfc565b6d8836ba99773&v=4 + url: https://github.com/ghandic + - login: MoronVV + avatarUrl: https://avatars.githubusercontent.com/u/24293616?v=4 + url: https://github.com/MoronVV + - login: fstau + avatarUrl: https://avatars.githubusercontent.com/u/24669867?u=60e7c8c09f8dafabee8fc3edcd6f9e19abbff918&v=4 + url: https://github.com/fstau + - login: mertguvencli + avatarUrl: https://avatars.githubusercontent.com/u/29762151?u=16a906d90df96c8cff9ea131a575c4bc171b1523&v=4 + url: https://github.com/mertguvencli + - login: rgreen32 + avatarUrl: https://avatars.githubusercontent.com/u/35779241?u=c9d64ad1ab364b6a1ec8e3d859da9ca802d681d8&v=4 + url: https://github.com/rgreen32 + - login: askurihin + avatarUrl: https://avatars.githubusercontent.com/u/37978981?v=4 + url: https://github.com/askurihin + - login: JitPackJoyride + avatarUrl: https://avatars.githubusercontent.com/u/40203625?u=9638bfeacfa5940358188f8205ce662bba022b53&v=4 + url: https://github.com/JitPackJoyride + - login: es3n1n + avatarUrl: https://avatars.githubusercontent.com/u/40367813?u=e881a3880f1e342d19a1ea7c8e1b6d76c52dc294&v=4 + url: https://github.com/es3n1n + - login: ilias-ant + avatarUrl: https://avatars.githubusercontent.com/u/42189572?u=a2d6121bac4d125d92ec207460fa3f1842d37e66&v=4 + url: https://github.com/ilias-ant + - login: arrrrrmin + avatarUrl: https://avatars.githubusercontent.com/u/43553423?u=05600727f1cfe75f440bb3fddd49bfea84b1e894&v=4 + url: https://github.com/arrrrrmin + - login: akanz1 + avatarUrl: https://avatars.githubusercontent.com/u/51492342?u=2280f57134118714645e16b535c1a37adf6b369b&v=4 + url: https://github.com/akanz1 +- - login: leogregianin + avatarUrl: https://avatars.githubusercontent.com/u/1684053?u=94ddd387601bd1805034dbe83e6eba0491c15323&v=4 + url: https://github.com/leogregianin + - login: sadikkuzu + avatarUrl: https://avatars.githubusercontent.com/u/23168063?u=765ed469c44c004560079210ccdad5b29938eaa9&v=4 + url: https://github.com/sadikkuzu + - login: gabrielmbmb + avatarUrl: https://avatars.githubusercontent.com/u/29572918?u=92084ed7242160dee4d20aece923a10c59758ee5&v=4 + url: https://github.com/gabrielmbmb + - login: starhype + avatarUrl: https://avatars.githubusercontent.com/u/36908028?u=6df41f7b62f0f673f1ecbc87e9cbadaa4fcb0767&v=4 + url: https://github.com/starhype + - login: pixel365 + avatarUrl: https://avatars.githubusercontent.com/u/53819609?u=9e0309c5420ec4624aececd3ca2d7105f7f68133&v=4 + url: https://github.com/pixel365 diff --git a/docs/en/data/people.yml b/docs/en/data/people.yml index 6356a5073e1ce..1e4ee30ce676e 100644 --- a/docs/en/data/people.yml +++ b/docs/en/data/people.yml @@ -1,12 +1,12 @@ maintainers: - login: tiangolo - answers: 1225 - prs: 232 + answers: 1229 + prs: 250 avatarUrl: https://avatars.githubusercontent.com/u/1326112?u=05f95ca7fdead36edd9c86be46b4ef6c3c71f876&v=4 url: https://github.com/tiangolo experts: - login: Kludex - count: 267 + count: 278 avatarUrl: https://avatars.githubusercontent.com/u/7353520?u=cf8455cb899806b774a3a71073f88583adec99f6&v=4 url: https://github.com/Kludex - login: dmontagu @@ -14,11 +14,11 @@ experts: avatarUrl: https://avatars.githubusercontent.com/u/35119617?u=58ed2a45798a4339700e2f62b2e12e6e54bf0396&v=4 url: https://github.com/dmontagu - login: ycd - count: 216 + count: 217 avatarUrl: https://avatars.githubusercontent.com/u/62724709?u=826f228edf0bab0d19ad1d5c4ba4df1047ccffef&v=4 url: https://github.com/ycd - login: Mause - count: 182 + count: 202 avatarUrl: https://avatars.githubusercontent.com/u/1405026?v=4 url: https://github.com/Mause - login: euri10 @@ -30,35 +30,35 @@ experts: avatarUrl: https://avatars.githubusercontent.com/u/331403?v=4 url: https://github.com/phy25 - login: ArcLightSlavik - count: 64 + count: 66 avatarUrl: https://avatars.githubusercontent.com/u/31127044?u=81a84af39c89b898b0fbc5a04e8834f60f23e55a&v=4 url: https://github.com/ArcLightSlavik - login: falkben - count: 56 + count: 57 avatarUrl: https://avatars.githubusercontent.com/u/653031?u=0c8d8f33d87f1aa1a6488d3f02105e9abc838105&v=4 url: https://github.com/falkben +- login: sm-Fifteen + count: 48 + avatarUrl: https://avatars.githubusercontent.com/u/516999?u=437c0c5038558c67e887ccd863c1ba0f846c03da&v=4 + url: https://github.com/sm-Fifteen - login: raphaelauv count: 47 avatarUrl: https://avatars.githubusercontent.com/u/10202690?u=e6f86f5c0c3026a15d6b51792fa3e532b12f1371&v=4 url: https://github.com/raphaelauv -- login: sm-Fifteen - count: 46 - avatarUrl: https://avatars.githubusercontent.com/u/516999?u=437c0c5038558c67e887ccd863c1ba0f846c03da&v=4 - url: https://github.com/sm-Fifteen - login: includeamin count: 38 avatarUrl: https://avatars.githubusercontent.com/u/11836741?u=8bd5ef7e62fe6a82055e33c4c0e0a7879ff8cfb6&v=4 url: https://github.com/includeamin +- login: Dustyposa + count: 38 + avatarUrl: https://avatars.githubusercontent.com/u/27180793?u=5cf2877f50b3eb2bc55086089a78a36f07042889&v=4 + url: https://github.com/Dustyposa - login: prostomarkeloff count: 33 avatarUrl: https://avatars.githubusercontent.com/u/28061158?u=72309cc1f2e04e40fa38b29969cb4e9d3f722e7b&v=4 url: https://github.com/prostomarkeloff -- login: Dustyposa - count: 32 - avatarUrl: https://avatars.githubusercontent.com/u/27180793?u=5cf2877f50b3eb2bc55086089a78a36f07042889&v=4 - url: https://github.com/Dustyposa - login: krishnardt - count: 30 + count: 31 avatarUrl: https://avatars.githubusercontent.com/u/31960541?u=47f4829c77f4962ab437ffb7995951e41eeebe9b&v=4 url: https://github.com/krishnardt - login: insomnes @@ -78,7 +78,7 @@ experts: avatarUrl: https://avatars.githubusercontent.com/u/9435877?u=719327b7d2c4c62212456d771bfa7c6b8dbb9eac&v=4 url: https://github.com/SirTelemak - login: chbndrhnns - count: 22 + count: 23 avatarUrl: https://avatars.githubusercontent.com/u/7534547?v=4 url: https://github.com/chbndrhnns - login: acnebs @@ -91,7 +91,7 @@ experts: url: https://github.com/nsidnev - login: frankie567 count: 21 - avatarUrl: https://avatars.githubusercontent.com/u/1144727?u=72adf1cb1d29787305c99700d669561952cea0af&v=4 + avatarUrl: https://avatars.githubusercontent.com/u/1144727?u=85c025e3fcc7bd79a5665c63ee87cdf8aae13374&v=4 url: https://github.com/frankie567 - login: chris-allnutt count: 21 @@ -114,13 +114,25 @@ experts: avatarUrl: https://avatars.githubusercontent.com/u/28262306?u=66ee21316275ef356081c2efc4ed7a4572e690dc&v=4 url: https://github.com/nkhitrov - login: waynerv - count: 15 + count: 16 avatarUrl: https://avatars.githubusercontent.com/u/39515546?u=ec35139777597cdbbbddda29bf8b9d4396b429a9&v=4 url: https://github.com/waynerv +- login: adriangb + count: 14 + avatarUrl: https://avatars.githubusercontent.com/u/1755071?u=81f0262df34e1460ca546fbd0c211169c2478532&v=4 + url: https://github.com/adriangb +- login: panla + count: 14 + avatarUrl: https://avatars.githubusercontent.com/u/41326348?u=ba2fda6b30110411ecbf406d187907e2b420ac19&v=4 + url: https://github.com/panla - login: haizaar count: 13 avatarUrl: https://avatars.githubusercontent.com/u/58201?u=4f1f9843d69433ca0d380d95146cfe119e5fdac4&v=4 url: https://github.com/haizaar +- login: STeveShary + count: 13 + avatarUrl: https://avatars.githubusercontent.com/u/5167622?u=de8f597c81d6336fcebc37b32dfd61a3f877160c&v=4 + url: https://github.com/STeveShary - login: acidjunk count: 11 avatarUrl: https://avatars.githubusercontent.com/u/685002?u=b5094ab4527fc84b006c0ac9ff54367bdebb2267&v=4 @@ -129,6 +141,10 @@ experts: count: 11 avatarUrl: https://avatars.githubusercontent.com/u/40475662?u=e58ef61034e8d0d6a312cc956fb09b9c3332b449&v=4 url: https://github.com/zamiramir +- login: David-Lor + count: 11 + avatarUrl: https://avatars.githubusercontent.com/u/17401854?u=474680c02b94cba810cb9032fb7eb787d9cc9d22&v=4 + url: https://github.com/David-Lor - login: juntatalor count: 11 avatarUrl: https://avatars.githubusercontent.com/u/8134632?v=4 @@ -145,39 +161,47 @@ experts: count: 10 avatarUrl: https://avatars.githubusercontent.com/u/20441825?u=ee1e59446b98f8ec2363caeda4c17164d0d9cc7d&v=4 url: https://github.com/stefanondisponibile +- login: hellocoldworld + count: 10 + avatarUrl: https://avatars.githubusercontent.com/u/47581948?v=4 + url: https://github.com/hellocoldworld last_month_active: +- login: Mause + count: 10 + avatarUrl: https://avatars.githubusercontent.com/u/1405026?v=4 + url: https://github.com/Mause - login: Kludex - count: 12 + count: 9 avatarUrl: https://avatars.githubusercontent.com/u/7353520?u=cf8455cb899806b774a3a71073f88583adec99f6&v=4 url: https://github.com/Kludex -- login: frankie567 - count: 9 - avatarUrl: https://avatars.githubusercontent.com/u/1144727?u=72adf1cb1d29787305c99700d669561952cea0af&v=4 - url: https://github.com/frankie567 -- login: Mause +- login: panla + count: 7 + avatarUrl: https://avatars.githubusercontent.com/u/41326348?u=ba2fda6b30110411ecbf406d187907e2b420ac19&v=4 + url: https://github.com/panla +- login: adriangb count: 6 - avatarUrl: https://avatars.githubusercontent.com/u/1405026?v=4 - url: https://github.com/Mause -- login: ArcLightSlavik + avatarUrl: https://avatars.githubusercontent.com/u/1755071?u=81f0262df34e1460ca546fbd0c211169c2478532&v=4 + url: https://github.com/adriangb +- login: Dustyposa count: 6 - avatarUrl: https://avatars.githubusercontent.com/u/31127044?u=81a84af39c89b898b0fbc5a04e8834f60f23e55a&v=4 - url: https://github.com/ArcLightSlavik -- login: gyKa - count: 4 - avatarUrl: https://avatars.githubusercontent.com/u/1000842?v=4 - url: https://github.com/gyKa -- login: ricky-sb + avatarUrl: https://avatars.githubusercontent.com/u/27180793?u=5cf2877f50b3eb2bc55086089a78a36f07042889&v=4 + url: https://github.com/Dustyposa +- login: STeveShary + count: 5 + avatarUrl: https://avatars.githubusercontent.com/u/5167622?u=de8f597c81d6336fcebc37b32dfd61a3f877160c&v=4 + url: https://github.com/STeveShary +- login: Cosmicoppai count: 3 - avatarUrl: https://avatars.githubusercontent.com/u/10700079?v=4 - url: https://github.com/ricky-sb -- login: captainCapitalism + avatarUrl: https://avatars.githubusercontent.com/u/63765823?u=b97c98ddd6eaf06cd5a0d312db36f97996ac5b23&v=4 + url: https://github.com/Cosmicoppai +- login: AlexanderPodorov count: 3 - avatarUrl: https://avatars.githubusercontent.com/u/32553875?v=4 - url: https://github.com/captainCapitalism -- login: acidjunk + avatarUrl: https://avatars.githubusercontent.com/u/54511144?v=4 + url: https://github.com/AlexanderPodorov +- login: SamuelHeath count: 3 - avatarUrl: https://avatars.githubusercontent.com/u/685002?u=b5094ab4527fc84b006c0ac9ff54367bdebb2267&v=4 - url: https://github.com/acidjunk + avatarUrl: https://avatars.githubusercontent.com/u/12291364?v=4 + url: https://github.com/SamuelHeath top_contributors: - login: waynerv count: 25 @@ -196,9 +220,13 @@ top_contributors: avatarUrl: https://avatars.githubusercontent.com/u/1104190?u=321a2e953e6645a7d09b732786c7a8061e0f8a8b&v=4 url: https://github.com/euri10 - login: mariacamilagl - count: 9 + count: 12 avatarUrl: https://avatars.githubusercontent.com/u/11489395?u=4adb6986bf3debfc2b8216ae701f2bd47d73da7d&v=4 url: https://github.com/mariacamilagl +- login: jaystone776 + count: 9 + avatarUrl: https://avatars.githubusercontent.com/u/11191137?u=299205a95e9b6817a43144a48b643346a5aac5cc&v=4 + url: https://github.com/jaystone776 - login: RunningIkkyu count: 7 avatarUrl: https://avatars.githubusercontent.com/u/31848542?u=706e1ee3f248245f2d68b976d149d06fd5a2010d&v=4 @@ -207,6 +235,10 @@ top_contributors: count: 7 avatarUrl: https://avatars.githubusercontent.com/u/22691749?u=4795b880e13ca33a73e52fc0ef7dc9c60c8fce47&v=4 url: https://github.com/Serrones +- login: Kludex + count: 6 + avatarUrl: https://avatars.githubusercontent.com/u/7353520?u=cf8455cb899806b774a3a71073f88583adec99f6&v=4 + url: https://github.com/Kludex - login: hard-coders count: 6 avatarUrl: https://avatars.githubusercontent.com/u/9651103?u=f2d3d2038c55d86d7f9348f4e6c5e30191e4ee8b&v=4 @@ -225,11 +257,15 @@ top_contributors: url: https://github.com/Attsun1031 - login: jekirl count: 4 - avatarUrl: https://avatars.githubusercontent.com/u/2546697?v=4 + avatarUrl: https://avatars.githubusercontent.com/u/2546697?u=a027452387d85bd4a14834e19d716c99255fb3b7&v=4 url: https://github.com/jekirl +- login: jfunez + count: 4 + avatarUrl: https://avatars.githubusercontent.com/u/805749?v=4 + url: https://github.com/jfunez - login: komtaki count: 4 - avatarUrl: https://avatars.githubusercontent.com/u/39375566?u=5a44657c0544111ee3c132d9bb9951c2804f7969&v=4 + avatarUrl: https://avatars.githubusercontent.com/u/39375566?u=260ad6b1a4b34c07dbfa728da5e586f16f6d1824&v=4 url: https://github.com/komtaki - login: Smlep count: 4 @@ -237,25 +273,25 @@ top_contributors: url: https://github.com/Smlep top_reviewers: - login: Kludex - count: 80 + count: 85 avatarUrl: https://avatars.githubusercontent.com/u/7353520?u=cf8455cb899806b774a3a71073f88583adec99f6&v=4 url: https://github.com/Kludex -- login: tokusumi - count: 44 - avatarUrl: https://avatars.githubusercontent.com/u/41147016?u=55010621aece725aa702270b54fed829b6a1fe60&v=4 - url: https://github.com/tokusumi - login: Laineyzhang55 - count: 42 + count: 47 avatarUrl: https://avatars.githubusercontent.com/u/59285379?v=4 url: https://github.com/Laineyzhang55 +- login: tokusumi + count: 46 + avatarUrl: https://avatars.githubusercontent.com/u/41147016?u=55010621aece725aa702270b54fed829b6a1fe60&v=4 + url: https://github.com/tokusumi +- login: waynerv + count: 43 + avatarUrl: https://avatars.githubusercontent.com/u/39515546?u=ec35139777597cdbbbddda29bf8b9d4396b429a9&v=4 + url: https://github.com/waynerv - login: ycd count: 41 avatarUrl: https://avatars.githubusercontent.com/u/62724709?u=826f228edf0bab0d19ad1d5c4ba4df1047ccffef&v=4 url: https://github.com/ycd -- login: waynerv - count: 38 - avatarUrl: https://avatars.githubusercontent.com/u/39515546?u=ec35139777597cdbbbddda29bf8b9d4396b429a9&v=4 - url: https://github.com/waynerv - login: AdrianDeAnda count: 28 avatarUrl: https://avatars.githubusercontent.com/u/1024932?u=bb7f8a0d6c9de4e9d0320a9f271210206e202250&v=4 @@ -266,14 +302,14 @@ top_reviewers: url: https://github.com/dmontagu - login: komtaki count: 21 - avatarUrl: https://avatars.githubusercontent.com/u/39375566?u=5a44657c0544111ee3c132d9bb9951c2804f7969&v=4 + avatarUrl: https://avatars.githubusercontent.com/u/39375566?u=260ad6b1a4b34c07dbfa728da5e586f16f6d1824&v=4 url: https://github.com/komtaki - login: ArcLightSlavik count: 20 avatarUrl: https://avatars.githubusercontent.com/u/31127044?u=81a84af39c89b898b0fbc5a04e8834f60f23e55a&v=4 url: https://github.com/ArcLightSlavik - login: cassiobotaro - count: 16 + count: 18 avatarUrl: https://avatars.githubusercontent.com/u/3127847?u=b0a652331da17efeb85cd6e3a4969182e5004804&v=4 url: https://github.com/cassiobotaro - login: yanever @@ -292,18 +328,26 @@ top_reviewers: count: 15 avatarUrl: https://avatars.githubusercontent.com/u/63476957?u=6c86e59b48e0394d4db230f37fc9ad4d7e2c27c7&v=4 url: https://github.com/delhi09 +- login: lsglucas + count: 13 + avatarUrl: https://avatars.githubusercontent.com/u/61513630?u=320e43fe4dc7bc6efc64e9b8f325f8075634fd20&v=4 + url: https://github.com/lsglucas - login: RunningIkkyu count: 12 avatarUrl: https://avatars.githubusercontent.com/u/31848542?u=706e1ee3f248245f2d68b976d149d06fd5a2010d&v=4 url: https://github.com/RunningIkkyu +- login: sh0nk + count: 12 + avatarUrl: https://avatars.githubusercontent.com/u/6478810?u=af15d724875cec682ed8088a86d36b2798f981c0&v=4 + url: https://github.com/sh0nk +- login: rjNemo + count: 12 + avatarUrl: https://avatars.githubusercontent.com/u/56785022?u=d5c3a02567c8649e146fcfc51b6060ccaf8adef8&v=4 + url: https://github.com/rjNemo - login: hard-coders count: 12 avatarUrl: https://avatars.githubusercontent.com/u/9651103?u=f2d3d2038c55d86d7f9348f4e6c5e30191e4ee8b&v=4 url: https://github.com/hard-coders -- login: sh0nk - count: 11 - avatarUrl: https://avatars.githubusercontent.com/u/6478810?u=af15d724875cec682ed8088a86d36b2798f981c0&v=4 - url: https://github.com/sh0nk - login: mariacamilagl count: 10 avatarUrl: https://avatars.githubusercontent.com/u/11489395?u=4adb6986bf3debfc2b8216ae701f2bd47d73da7d&v=4 @@ -316,6 +360,10 @@ top_reviewers: count: 10 avatarUrl: https://avatars.githubusercontent.com/u/7887703?v=4 url: https://github.com/maoyibo +- login: Smlep + count: 9 + avatarUrl: https://avatars.githubusercontent.com/u/16785985?v=4 + url: https://github.com/Smlep - login: PandaHun count: 9 avatarUrl: https://avatars.githubusercontent.com/u/13096845?u=646eba44db720e37d0dbe8e98e77ab534ea78a20&v=4 @@ -324,14 +372,6 @@ top_reviewers: count: 9 avatarUrl: https://avatars.githubusercontent.com/u/69092910?u=4ac58eab99bd37d663f3d23551df96d4fbdbf760&v=4 url: https://github.com/bezaca -- login: lsglucas - count: 8 - avatarUrl: https://avatars.githubusercontent.com/u/61513630?u=320e43fe4dc7bc6efc64e9b8f325f8075634fd20&v=4 - url: https://github.com/lsglucas -- login: rjNemo - count: 8 - avatarUrl: https://avatars.githubusercontent.com/u/56785022?u=d5c3a02567c8649e146fcfc51b6060ccaf8adef8&v=4 - url: https://github.com/rjNemo - login: blt232018 count: 8 avatarUrl: https://avatars.githubusercontent.com/u/43393471?u=172b0e0391db1aa6c1706498d6dfcb003c8a4857&v=4 @@ -348,22 +388,22 @@ top_reviewers: count: 7 avatarUrl: https://avatars.githubusercontent.com/u/10202690?u=e6f86f5c0c3026a15d6b51792fa3e532b12f1371&v=4 url: https://github.com/raphaelauv +- login: graingert + count: 7 + avatarUrl: https://avatars.githubusercontent.com/u/413772?v=4 + url: https://github.com/graingert - login: NastasiaSaby count: 7 avatarUrl: https://avatars.githubusercontent.com/u/8245071?u=b3afd005f9e4bf080c219ef61a592b3a8004b764&v=4 url: https://github.com/NastasiaSaby -- login: Smlep +- login: Mause count: 7 - avatarUrl: https://avatars.githubusercontent.com/u/16785985?v=4 - url: https://github.com/Smlep + avatarUrl: https://avatars.githubusercontent.com/u/1405026?v=4 + url: https://github.com/Mause - login: jovicon count: 6 avatarUrl: https://avatars.githubusercontent.com/u/21287303?u=b049eac3e51a4c0473c2efe66b4d28a7d8f2b572&v=4 url: https://github.com/jovicon -- login: Mause - count: 6 - avatarUrl: https://avatars.githubusercontent.com/u/1405026?v=4 - url: https://github.com/Mause - login: nimctl count: 5 avatarUrl: https://avatars.githubusercontent.com/u/49960770?u=e39b11d47188744ee07b2a1c7ce1a1bdf3c80760&v=4 @@ -376,10 +416,22 @@ top_reviewers: count: 5 avatarUrl: https://avatars.githubusercontent.com/u/63564282?u=0078826509dbecb2fdb543f4e881c9cd06157893&v=4 url: https://github.com/SnkSynthesis +- login: solomein-sv + count: 5 + avatarUrl: https://avatars.githubusercontent.com/u/46193920?u=46acfb4aeefb1d7b9fdc5a8cbd9eb8744683c47a&v=4 + url: https://github.com/solomein-sv - login: anthonycepeda count: 5 avatarUrl: https://avatars.githubusercontent.com/u/72019805?u=892f700c79f9732211bd5221bf16eec32356a732&v=4 url: https://github.com/anthonycepeda +- login: ComicShrimp + count: 5 + avatarUrl: https://avatars.githubusercontent.com/u/43503750?u=b3e4d9a14d9a65d429ce62c566aef73178b7111d&v=4 + url: https://github.com/ComicShrimp +- login: oandersonmagalhaes + count: 5 + avatarUrl: https://avatars.githubusercontent.com/u/83456692?v=4 + url: https://github.com/oandersonmagalhaes - login: euri10 count: 4 avatarUrl: https://avatars.githubusercontent.com/u/1104190?u=321a2e953e6645a7d09b732786c7a8061e0f8a8b&v=4 @@ -392,6 +444,10 @@ top_reviewers: count: 4 avatarUrl: https://avatars.githubusercontent.com/u/41201924?u=6883cc4fc13a7b2e60d4deddd4be06f9c5287880&v=4 url: https://github.com/aviramha +- login: Cajuteq + count: 4 + avatarUrl: https://avatars.githubusercontent.com/u/26676532?u=8ee0422981810e51480855de1c0d67b6b79cd3f2&v=4 + url: https://github.com/Cajuteq - login: Zxilly count: 4 avatarUrl: https://avatars.githubusercontent.com/u/31370133?v=4 @@ -400,353 +456,3 @@ top_reviewers: count: 4 avatarUrl: https://avatars.githubusercontent.com/u/38372706?u=c9d28aff15958d6ebf1971148bfb3154ff943c4f&v=4 url: https://github.com/Bluenix2 -sponsors_50: -- login: johnadjei - avatarUrl: https://avatars.githubusercontent.com/u/767860?v=4 - url: https://github.com/johnadjei -- login: wdwinslow - avatarUrl: https://avatars.githubusercontent.com/u/11562137?u=dc01daafb354135603a263729e3d26d939c0c452&v=4 - url: https://github.com/wdwinslow -- login: bingwu-chime - avatarUrl: https://avatars.githubusercontent.com/u/67026650?u=603a6b345f25c20c6706a8a6c7f71ae688d649a5&v=4 - url: https://github.com/bingwu-chime -sponsors: -- login: kamalgill - avatarUrl: https://avatars.githubusercontent.com/u/133923?u=0df9181d97436ce330e9acf90ab8a54b7022efe7&v=4 - url: https://github.com/kamalgill -- login: grillazz - avatarUrl: https://avatars.githubusercontent.com/u/3415861?u=16d7d0ffa5dfb99f8834f8f76d90e138ba09b94a&v=4 - url: https://github.com/grillazz -- login: tizz98 - avatarUrl: https://avatars.githubusercontent.com/u/5739698?u=f095a3659e3a8e7c69ccd822696990b521ea25f9&v=4 - url: https://github.com/tizz98 -- login: jmaralc - avatarUrl: https://avatars.githubusercontent.com/u/21101214?u=b15a9f07b7cbf6c9dcdbcb6550bbd2c52f55aa50&v=4 - url: https://github.com/jmaralc -- login: psgandalf - avatarUrl: https://avatars.githubusercontent.com/u/8134158?v=4 - url: https://github.com/psgandalf -- login: samuelcolvin - avatarUrl: https://avatars.githubusercontent.com/u/4039449?u=807390ba9cfe23906c3bf8a0d56aaca3cf2bfa0d&v=4 - url: https://github.com/samuelcolvin -- login: jokull - avatarUrl: https://avatars.githubusercontent.com/u/701?u=0532b62166893d5160ef795c4c8b7512d971af05&v=4 - url: https://github.com/jokull -- login: wshayes - avatarUrl: https://avatars.githubusercontent.com/u/365303?u=07ca03c5ee811eb0920e633cc3c3db73dbec1aa5&v=4 - url: https://github.com/wshayes -- login: koxudaxi - avatarUrl: https://avatars.githubusercontent.com/u/630670?u=507d8577b4b3670546b449c4c2ccbc5af40d72f7&v=4 - url: https://github.com/koxudaxi -- login: falkben - avatarUrl: https://avatars.githubusercontent.com/u/653031?u=0c8d8f33d87f1aa1a6488d3f02105e9abc838105&v=4 - url: https://github.com/falkben -- login: jqueguiner - avatarUrl: https://avatars.githubusercontent.com/u/690878?u=e4835b2a985a0f2d52018e4926cb5a58c26a62e8&v=4 - url: https://github.com/jqueguiner -- login: Mazyod - avatarUrl: https://avatars.githubusercontent.com/u/860511?v=4 - url: https://github.com/Mazyod -- login: ltieman - avatarUrl: https://avatars.githubusercontent.com/u/1084689?u=e69b17de17cb3ca141a17daa7ccbe173ceb1eb17&v=4 - url: https://github.com/ltieman -- login: mrmattwright - avatarUrl: https://avatars.githubusercontent.com/u/1277725?v=4 - url: https://github.com/mrmattwright -- login: westonsteimel - avatarUrl: https://avatars.githubusercontent.com/u/1593939?u=0f2c0e3647f916fe295d62fa70da7a4c177115e3&v=4 - url: https://github.com/westonsteimel -- login: timdrijvers - avatarUrl: https://avatars.githubusercontent.com/u/1694939?v=4 - url: https://github.com/timdrijvers -- login: mrgnw - avatarUrl: https://avatars.githubusercontent.com/u/2504532?u=7ec43837a6d0afa80f96f0788744ea6341b89f97&v=4 - url: https://github.com/mrgnw -- login: madisonmay - avatarUrl: https://avatars.githubusercontent.com/u/2645393?u=f22b93c6ea345a4d26a90a3834dfc7f0789fcb63&v=4 - url: https://github.com/madisonmay -- login: saivarunk - avatarUrl: https://avatars.githubusercontent.com/u/2976867?u=71f4385e781e9a9e871a52f2d4686f9a8d69ba2f&v=4 - url: https://github.com/saivarunk -- login: andre1sk - avatarUrl: https://avatars.githubusercontent.com/u/3148093?v=4 - url: https://github.com/andre1sk -- login: Shark009 - avatarUrl: https://avatars.githubusercontent.com/u/3163309?v=4 - url: https://github.com/Shark009 -- login: peterHoburg - avatarUrl: https://avatars.githubusercontent.com/u/3860655?u=f55f47eb2d6a9b495e806ac5a044e3ae01ccc1fa&v=4 - url: https://github.com/peterHoburg -- login: dudil - avatarUrl: https://avatars.githubusercontent.com/u/4785835?u=58b7ea39123e0507f3b2996448a27256b16fd697&v=4 - url: https://github.com/dudil -- login: ennui93 - avatarUrl: https://avatars.githubusercontent.com/u/5300907?u=5b5452725ddb391b2caaebf34e05aba873591c3a&v=4 - url: https://github.com/ennui93 -- login: sco1 - avatarUrl: https://avatars.githubusercontent.com/u/5323929?u=2b8434060d0c9d93de80a2a945baed94a412c31e&v=4 - url: https://github.com/sco1 -- login: MacroPower - avatarUrl: https://avatars.githubusercontent.com/u/5648814?u=b2730000c9f9a471282b9849d2cc85711d7973d4&v=4 - url: https://github.com/MacroPower -- login: ginomempin - avatarUrl: https://avatars.githubusercontent.com/u/6091865?v=4 - url: https://github.com/ginomempin -- login: iwpnd - avatarUrl: https://avatars.githubusercontent.com/u/6152183?u=b2286006daafff5f991557344fee20b5da59639a&v=4 - url: https://github.com/iwpnd -- login: s3ich4n - avatarUrl: https://avatars.githubusercontent.com/u/6926298?u=ba3025d698e1c986655e776ae383a3d60d9d578e&v=4 - url: https://github.com/s3ich4n -- login: Rehket - avatarUrl: https://avatars.githubusercontent.com/u/7015688?u=3afb0ba200feebbc7f958950e92db34df2a3c172&v=4 - url: https://github.com/Rehket -- login: christippett - avatarUrl: https://avatars.githubusercontent.com/u/7218120?u=434b9d29287d7de25772d94ddc74a9bd6d969284&v=4 - url: https://github.com/christippett -- login: Kludex - avatarUrl: https://avatars.githubusercontent.com/u/7353520?u=cf8455cb899806b774a3a71073f88583adec99f6&v=4 - url: https://github.com/Kludex -- login: Shackelford-Arden - avatarUrl: https://avatars.githubusercontent.com/u/7362263?v=4 - url: https://github.com/Shackelford-Arden -- login: macleodmac - avatarUrl: https://avatars.githubusercontent.com/u/8996312?u=e39c68c3e0b1d264dcba4850134a291680f46355&v=4 - url: https://github.com/macleodmac -- login: cristeaadrian - avatarUrl: https://avatars.githubusercontent.com/u/9112724?v=4 - url: https://github.com/cristeaadrian -- login: otivvormes - avatarUrl: https://avatars.githubusercontent.com/u/11317418?u=6de1edefb6afd0108c0ad2816bd6efc4464a9c44&v=4 - url: https://github.com/otivvormes -- login: iambobmae - avatarUrl: https://avatars.githubusercontent.com/u/12390270?u=c9a35c2ee5092a9b4135ebb1f91b7f521c467031&v=4 - url: https://github.com/iambobmae -- login: ronaldnwilliams - avatarUrl: https://avatars.githubusercontent.com/u/13632749?u=ac41a086d0728bf66a9d2bee9e5e377041ff44a4&v=4 - url: https://github.com/ronaldnwilliams -- login: uselessscat - avatarUrl: https://avatars.githubusercontent.com/u/15332878?u=8485a1b7383c274b28f383370ee2d5f9a6cd423b&v=4 - url: https://github.com/uselessscat -- login: natenka - avatarUrl: https://avatars.githubusercontent.com/u/15850513?u=00d1083c980d0b4ce32835dc07eee7f43f34fd2f&v=4 - url: https://github.com/natenka -- login: la-mar - avatarUrl: https://avatars.githubusercontent.com/u/16618300?u=7755c0521d2bb0d704f35a51464b15c1e2e6c4da&v=4 - url: https://github.com/la-mar -- login: robintully - avatarUrl: https://avatars.githubusercontent.com/u/17059673?u=862b9bb01513f5acd30df97433cb97a24dbfb772&v=4 - url: https://github.com/robintully -- login: ShaulAb - avatarUrl: https://avatars.githubusercontent.com/u/18129076?u=2c8d48e47f2dbee15c3f89c3d17d4c356504386c&v=4 - url: https://github.com/ShaulAb -- login: wedwardbeck - avatarUrl: https://avatars.githubusercontent.com/u/19333237?u=1de4ae2bf8d59eb4c013f21d863cbe0f2010575f&v=4 - url: https://github.com/wedwardbeck -- login: linusg - avatarUrl: https://avatars.githubusercontent.com/u/19366641?u=125e390abef8fff3b3b0d370c369cba5d7fd4c67&v=4 - url: https://github.com/linusg -- login: RedCarpetUp - avatarUrl: https://avatars.githubusercontent.com/u/20360440?v=4 - url: https://github.com/RedCarpetUp -- login: daddycocoaman - avatarUrl: https://avatars.githubusercontent.com/u/21189155?u=756f6a17c71c538b11470f70839baacab43807ef&v=4 - url: https://github.com/daddycocoaman -- login: Filimoa - avatarUrl: https://avatars.githubusercontent.com/u/21352040?u=75e02d102d2ee3e3d793e555fa5c63045913ccb0&v=4 - url: https://github.com/Filimoa -- login: raminsj13 - avatarUrl: https://avatars.githubusercontent.com/u/24259406?u=d51f2a526312ebba150a06936ed187ca0727d329&v=4 - url: https://github.com/raminsj13 -- login: comoelcometa - avatarUrl: https://avatars.githubusercontent.com/u/25950317?u=c6751efa038561b9bc5fa56d1033d5174e10cd65&v=4 - url: https://github.com/comoelcometa -- login: veprimk - avatarUrl: https://avatars.githubusercontent.com/u/29689749?u=f8cb5a15a286e522e5b189bc572d5a1a90217fb2&v=4 - url: https://github.com/veprimk -- login: orihomie - avatarUrl: https://avatars.githubusercontent.com/u/29889683?u=6bc2135a52fcb3a49e69e7d50190796618185fda&v=4 - url: https://github.com/orihomie -- login: SaltyCoco - avatarUrl: https://avatars.githubusercontent.com/u/31451104?u=6ee4e17c07d21b7054f54a12fa9cc377a1b24ff9&v=4 - url: https://github.com/SaltyCoco -- login: mauroalejandrojm - avatarUrl: https://avatars.githubusercontent.com/u/31569442?u=cdada990a1527926a36e95f62c30a8b48bbc49a1&v=4 - url: https://github.com/mauroalejandrojm -- login: public-daniel - avatarUrl: https://avatars.githubusercontent.com/u/32238294?u=0377e38dd023395c9643d5388b4e9489a24b4d34&v=4 - url: https://github.com/public-daniel -- login: ybressler - avatarUrl: https://avatars.githubusercontent.com/u/40807730?u=6621dc9ab53b697912ab2a32211bb29ae90a9112&v=4 - url: https://github.com/ybressler -- login: dbanty - avatarUrl: https://avatars.githubusercontent.com/u/43723790?u=0cf33e4f40efc2ea206a1189fd63a11344eb88ed&v=4 - url: https://github.com/dbanty -- login: dudikbender - avatarUrl: https://avatars.githubusercontent.com/u/53487583?u=494f85229115076121b3639a3806bbac1c6ae7f6&v=4 - url: https://github.com/dudikbender -- login: primer-io - avatarUrl: https://avatars.githubusercontent.com/u/62146168?v=4 - url: https://github.com/primer-io -- login: tkrestiankova - avatarUrl: https://avatars.githubusercontent.com/u/67013045?v=4 - url: https://github.com/tkrestiankova -- login: daverin - avatarUrl: https://avatars.githubusercontent.com/u/70378377?u=6d1814195c0de7162820eaad95a25b423a3869c0&v=4 - url: https://github.com/daverin -- login: anthonycepeda - avatarUrl: https://avatars.githubusercontent.com/u/72019805?u=892f700c79f9732211bd5221bf16eec32356a732&v=4 - url: https://github.com/anthonycepeda -- login: linux-china - avatarUrl: https://avatars.githubusercontent.com/u/46711?v=4 - url: https://github.com/linux-china -- login: jhb - avatarUrl: https://avatars.githubusercontent.com/u/142217?v=4 - url: https://github.com/jhb -- login: yourkin - avatarUrl: https://avatars.githubusercontent.com/u/178984?v=4 - url: https://github.com/yourkin -- login: jmagnusson - avatarUrl: https://avatars.githubusercontent.com/u/190835?v=4 - url: https://github.com/jmagnusson -- login: slafs - avatarUrl: https://avatars.githubusercontent.com/u/210173?v=4 - url: https://github.com/slafs -- login: adamghill - avatarUrl: https://avatars.githubusercontent.com/u/317045?u=f1349d5ffe84a19f324e204777859fbf69ddf633&v=4 - url: https://github.com/adamghill -- login: eteq - avatarUrl: https://avatars.githubusercontent.com/u/346587?v=4 - url: https://github.com/eteq -- login: dmig - avatarUrl: https://avatars.githubusercontent.com/u/388564?v=4 - url: https://github.com/dmig -- login: hongqn - avatarUrl: https://avatars.githubusercontent.com/u/405587?u=470b4c04832e45141fd5264d3354845cc9fc6466&v=4 - url: https://github.com/hongqn -- login: rinckd - avatarUrl: https://avatars.githubusercontent.com/u/546002?u=1fcc7e664dc86524a0af6837a0c222829c3fd4e5&v=4 - url: https://github.com/rinckd -- login: Pytlicek - avatarUrl: https://avatars.githubusercontent.com/u/1430522?u=169dba3bfbc04ed214a914640ff435969f19ddb3&v=4 - url: https://github.com/Pytlicek -- login: okken - avatarUrl: https://avatars.githubusercontent.com/u/1568356?u=0a991a21bdc62e2bea9ad311652f2c45f453dc84&v=4 - url: https://github.com/okken -- login: leogregianin - avatarUrl: https://avatars.githubusercontent.com/u/1684053?u=94ddd387601bd1805034dbe83e6eba0491c15323&v=4 - url: https://github.com/leogregianin -- login: cbonoz - avatarUrl: https://avatars.githubusercontent.com/u/2351087?u=fd3e8030b2cc9fbfbb54a65e9890c548a016f58b&v=4 - url: https://github.com/cbonoz -- login: rglsk - avatarUrl: https://avatars.githubusercontent.com/u/2768101?u=e349c88673f2155fe021331377c656a9d74bcc25&v=4 - url: https://github.com/rglsk -- login: Atem18 - avatarUrl: https://avatars.githubusercontent.com/u/2875254?v=4 - url: https://github.com/Atem18 -- login: paul121 - avatarUrl: https://avatars.githubusercontent.com/u/3116995?u=6e2d8691cc345e63ee02e4eb4d7cef82b1fcbedc&v=4 - url: https://github.com/paul121 -- login: igorcorrea - avatarUrl: https://avatars.githubusercontent.com/u/3438238?u=c57605077c31a8f7b2341fc4912507f91b4a5621&v=4 - url: https://github.com/igorcorrea -- login: zsinx6 - avatarUrl: https://avatars.githubusercontent.com/u/3532625?u=ba75a5dc744d1116ccfeaaf30d41cb2fe81fe8dd&v=4 - url: https://github.com/zsinx6 -- login: pawamoy - avatarUrl: https://avatars.githubusercontent.com/u/3999221?u=b030e4c89df2f3a36bc4710b925bdeb6745c9856&v=4 - url: https://github.com/pawamoy -- login: spyker77 - avatarUrl: https://avatars.githubusercontent.com/u/4953435?u=568baae6469628e020fe0bab16e395b7ae10c7d3&v=4 - url: https://github.com/spyker77 -- login: serfer2 - avatarUrl: https://avatars.githubusercontent.com/u/5196592?u=e8798d87120952ed41876778f0cc8a1ddb47f901&v=4 - url: https://github.com/serfer2 -- login: JonasKs - avatarUrl: https://avatars.githubusercontent.com/u/5310116?u=98a049f3e1491bffb91e1feb7e93def6881a9389&v=4 - url: https://github.com/JonasKs -- login: holec - avatarUrl: https://avatars.githubusercontent.com/u/6438041?u=f5af71ec85b3a9d7b8139cb5af0512b02fa9ab1e&v=4 - url: https://github.com/holec -- login: BartlomiejRasztabiga - avatarUrl: https://avatars.githubusercontent.com/u/8852711?u=ed213d60f7a423df31ceb1004aa3ec60e612cb98&v=4 - url: https://github.com/BartlomiejRasztabiga -- login: davanstrien - avatarUrl: https://avatars.githubusercontent.com/u/8995957?u=fb2aad2b52bb4e7b56db6d7c8ecc9ae1eac1b984&v=4 - url: https://github.com/davanstrien -- login: and-semakin - avatarUrl: https://avatars.githubusercontent.com/u/9129071?u=ea77ddf7de4bc375d546bf2825ed420eaddb7666&v=4 - url: https://github.com/and-semakin -- login: VivianSolide - avatarUrl: https://avatars.githubusercontent.com/u/9358572?u=ffb2e2ec522a15dcd3f0af1f9fd1df4afe418afa&v=4 - url: https://github.com/VivianSolide -- login: hard-coders - avatarUrl: https://avatars.githubusercontent.com/u/9651103?u=f2d3d2038c55d86d7f9348f4e6c5e30191e4ee8b&v=4 - url: https://github.com/hard-coders -- login: satwikkansal - avatarUrl: https://avatars.githubusercontent.com/u/10217535?u=b12d6ef74ea297de9e46da6933b1a5b7ba9e6a61&v=4 - url: https://github.com/satwikkansal -- login: pheanex - avatarUrl: https://avatars.githubusercontent.com/u/10408624?u=5b6bab6ee174aa6e991333e06eb29f628741013d&v=4 - url: https://github.com/pheanex -- login: wotori - avatarUrl: https://avatars.githubusercontent.com/u/10486621?u=0044c295b91694b8c9bccc0a805681f794250f7b&v=4 - url: https://github.com/wotori -- login: JimFawkes - avatarUrl: https://avatars.githubusercontent.com/u/12075115?u=dc58ecfd064d72887c34bf500ddfd52592509acd&v=4 - url: https://github.com/JimFawkes -- login: logan-connolly - avatarUrl: https://avatars.githubusercontent.com/u/16244943?u=8ae66dfbba936463cc8aa0dd7a6d2b4c0cc757eb&v=4 - url: https://github.com/logan-connolly -- login: iPr0ger - avatarUrl: https://avatars.githubusercontent.com/u/19322290?v=4 - url: https://github.com/iPr0ger -- login: sadikkuzu - avatarUrl: https://avatars.githubusercontent.com/u/23168063?u=765ed469c44c004560079210ccdad5b29938eaa9&v=4 - url: https://github.com/sadikkuzu -- login: ghandic - avatarUrl: https://avatars.githubusercontent.com/u/23500353?u=e2e1d736f924d9be81e8bfc565b6d8836ba99773&v=4 - url: https://github.com/ghandic -- login: MoronVV - avatarUrl: https://avatars.githubusercontent.com/u/24293616?v=4 - url: https://github.com/MoronVV -- login: AngusWG - avatarUrl: https://avatars.githubusercontent.com/u/26385612?u=f4d4c8bd2097cdd58eb9e385932b83c78777f3c0&v=4 - url: https://github.com/AngusWG -- login: mertguvencli - avatarUrl: https://avatars.githubusercontent.com/u/29762151?u=16a906d90df96c8cff9ea131a575c4bc171b1523&v=4 - url: https://github.com/mertguvencli -- login: rgreen32 - avatarUrl: https://avatars.githubusercontent.com/u/35779241?u=c9d64ad1ab364b6a1ec8e3d859da9ca802d681d8&v=4 - url: https://github.com/rgreen32 -- login: askurihin - avatarUrl: https://avatars.githubusercontent.com/u/37978981?v=4 - url: https://github.com/askurihin -- login: berrysauce - avatarUrl: https://avatars.githubusercontent.com/u/38889179?u=758ed15a5be8bbd03855f5a74f42c19f7946ee32&v=4 - url: https://github.com/berrysauce -- login: JitPackJoyride - avatarUrl: https://avatars.githubusercontent.com/u/40203625?u=9638bfeacfa5940358188f8205ce662bba022b53&v=4 - url: https://github.com/JitPackJoyride -- login: es3n1n - avatarUrl: https://avatars.githubusercontent.com/u/40367813?u=e881a3880f1e342d19a1ea7c8e1b6d76c52dc294&v=4 - url: https://github.com/es3n1n -- login: ilias-ant - avatarUrl: https://avatars.githubusercontent.com/u/42189572?u=a2d6121bac4d125d92ec207460fa3f1842d37e66&v=4 - url: https://github.com/ilias-ant -- login: kbhatiya999 - avatarUrl: https://avatars.githubusercontent.com/u/47816034?v=4 - url: https://github.com/kbhatiya999 -- login: akanz1 - avatarUrl: https://avatars.githubusercontent.com/u/51492342?u=2280f57134118714645e16b535c1a37adf6b369b&v=4 - url: https://github.com/akanz1 -- login: rychardvale - avatarUrl: https://avatars.githubusercontent.com/u/54805553?u=3d20ab05301d05f9ac3500fb79a2bfee3842b753&v=4 - url: https://github.com/rychardvale -- login: athemeart - avatarUrl: https://avatars.githubusercontent.com/u/61623624?v=4 - url: https://github.com/athemeart -- login: Rhythmicc - avatarUrl: https://avatars.githubusercontent.com/u/29839231?u=2100781089a259707c475c4547bd7995b0fc18ee&v=4 - url: https://github.com/Rhythmicc diff --git a/docs/en/data/sponsors.yml b/docs/en/data/sponsors.yml index 49df39c99a3b4..e0a4ee783d8d5 100644 --- a/docs/en/data/sponsors.yml +++ b/docs/en/data/sponsors.yml @@ -1,11 +1,11 @@ gold: - - url: https://www.deta.sh/?ref=fastapi - title: The launchpad for all your (team's) ideas - img: https://fastapi.tiangolo.com/img/sponsors/deta.svg - url: https://bit.ly/2QSouzH title: "Jina: build neural search-as-a-service for any kind of data in just minutes." img: https://fastapi.tiangolo.com/img/sponsors/jina.svg silver: + - url: https://www.deta.sh/?ref=fastapi + title: The launchpad for all your (team's) ideas + img: https://fastapi.tiangolo.com/img/sponsors/deta.svg - url: https://www.investsuite.com/jobs title: Wealthtech jobs with FastAPI img: https://fastapi.tiangolo.com/img/sponsors/investsuite.svg @@ -15,7 +15,10 @@ silver: - url: https://talkpython.fm/fastapi-sponsor title: FastAPI video courses on demand from people you trust img: https://fastapi.tiangolo.com/img/sponsors/talkpython.png -bronze: - url: https://testdriven.io/courses/tdd-fastapi/ title: Learn to build high-quality web apps with best practices img: https://fastapi.tiangolo.com/img/sponsors/testdriven.svg +bronze: + - url: https://calmcode.io + title: Code. Simply. Clearly. Calmly. + img: https://fastapi.tiangolo.com/img/sponsors/calmcode.jpg diff --git a/docs/en/data/sponsors_badge.yml b/docs/en/data/sponsors_badge.yml new file mode 100644 index 0000000000000..9e95a62550f2e --- /dev/null +++ b/docs/en/data/sponsors_badge.yml @@ -0,0 +1,7 @@ +logins: + - jina-ai + - deta + - investsuite + - vimsoHQ + - mikeckennedy + - koaning diff --git a/docs/en/docs/css/custom.css b/docs/en/docs/css/custom.css index 2b6d551734a34..7d3503e492b86 100644 --- a/docs/en/docs/css/custom.css +++ b/docs/en/docs/css/custom.css @@ -1,3 +1,13 @@ +.termynal-comment { + color: #4a968f; + font-style: italic; + display: block; +} + +.termy [data-termynal] { + white-space: pre-wrap; +} + a.external-link::after { /* \00A0 is a non-breaking space to make the mark be on the same line as the link @@ -12,6 +22,10 @@ a.internal-link::after { content: "\00A0↪"; } +.shadow { + box-shadow: 5px 5px 10px #999; +} + /* Give space to lower icons so Gitter chat doesn't get on top of them */ .md-footer-meta { padding-bottom: 2em; @@ -20,6 +34,7 @@ a.internal-link::after { .user-list { display: flex; flex-wrap: wrap; + margin-bottom: 2rem; } .user-list-center { diff --git a/docs/en/docs/css/termynal.css b/docs/en/docs/css/termynal.css index 0484e65d4d8eb..406c00897c8d8 100644 --- a/docs/en/docs/css/termynal.css +++ b/docs/en/docs/css/termynal.css @@ -17,7 +17,8 @@ max-width: 100%; background: var(--color-bg); color: var(--color-text); - font-size: 18px; + /* font-size: 18px; */ + font-size: 15px; /* font-family: 'Fira Mono', Consolas, Menlo, Monaco, 'Courier New', Courier, monospace; */ font-family: 'Roboto Mono', 'Fira Mono', Consolas, Menlo, Monaco, 'Courier New', Courier, monospace; border-radius: 4px; diff --git a/docs/en/docs/deployment/https.md b/docs/en/docs/deployment/https.md index c735f1f4a11f2..1a3b1a0aade6c 100644 --- a/docs/en/docs/deployment/https.md +++ b/docs/en/docs/deployment/https.md @@ -7,42 +7,184 @@ But it is way more complex than that. !!! tip If you are in a hurry or don't care, continue with the next sections for step by step instructions to set everything up with different techniques. -To learn the basics of HTTPS, from a consumer perspective, check https://howhttps.works/. - -Now, from a developer's perspective, here are several things to have in mind while thinking about HTTPS: - -* For HTTPS, the server needs to have "certificates" generated by a third party. - * Those certificates are actually acquired from the third-party, not "generated". -* Certificates have a lifetime. - * They expire. - * And then they need to be renewed, acquired again from the third party. -* The encryption of the connection happens at the TCP level. - * That's one layer below HTTP. - * So, the certificate and encryption handling is done before HTTP. -* TCP doesn't know about "domains". Only about IP addresses. - * The information about the specific domain requested goes in the HTTP data. -* The HTTPS certificates "certify" a certain domain, but the protocol and encryption happen at the TCP level, before knowing which domain is being dealt with. -* By default, that would mean that you can only have one HTTPS certificate per IP address. +To **learn the basics of HTTPS**, from a consumer perspective, check https://howhttps.works/. + +Now, from a **developer's perspective**, here are several things to have in mind while thinking about HTTPS: + +* For HTTPS, **the server** needs to **have "certificates"** generated by a **third party**. + * Those certificates are actually **acquired** from the third party, not "generated". +* Certificates have a **lifetime**. + * They **expire**. + * And then they need to be **renewed**, **acquired again** from the third party. +* The encryption of the connection happens at the **TCP level**. + * That's one layer **below HTTP**. + * So, the **certificate and encryption** handling is done **before HTTP**. +* **TCP doesn't know about "domains"**. Only about IP addresses. + * The information about the **specific domain** requested goes in the **HTTP data**. +* The **HTTPS certificates** "certify" a **certain domain**, but the protocol and encryption happen at the TCP level, **before knowing** which domain is being dealt with. +* **By default**, that would mean that you can only have **one HTTPS certificate per IP address**. * No matter how big your server is or how small each application you have on it might be. - * There is a solution to this, however. -* There's an extension to the TLS protocol (the one handling the encryption at the TCP level, before HTTP) called SNI. - * This SNI extension allows one single server (with a single IP address) to have several HTTPS certificates and serve multiple HTTPS domains/applications. - * For this to work, a single component (program) running on the server, listening on the public IP address, must have all the HTTPS certificates in the server. -* After obtaining a secure connection, the communication protocol is still HTTP. - * The contents are encrypted, even though they are being sent with the HTTP protocol. + * There is a **solution** to this, however. +* There's an **extension** to the **TLS** protocol (the one handling the encryption at the TCP level, before HTTP) called **SNI**. + * This SNI extension allows one single server (with a **single IP address**) to have **several HTTPS certificates** and serve **multiple HTTPS domains/applications**. + * For this to work, a **single** component (program) running on the server, listening on the **public IP address**, must have **all the HTTPS certificates** in the server. +* **After** obtaining a secure connection, the communication protocol is **still HTTP**. + * The contents are **encrypted**, even though they are being sent with the **HTTP protocol**. + +It is a common practice to have **one program/HTTP server** running on the server (the machine, host, etc.) and **managing all the HTTPS parts**: receiving the **encrypted HTTPS requests**, sending the **decrypted HTTP requests** to the actual HTTP application running in the same server (the **FastAPI** application, in this case), take the **HTTP response** from the application, **encrypt it** using the appropriate **HTTPS certificate** and sending it back to the client using **HTTPS**. This server is often called a **TLS Termination Proxy**. -It is a common practice to have one program/HTTP server running on the server (the machine, host, etc.) and managing all the HTTPS parts : sending the decrypted HTTP requests to the actual HTTP application running in the same server (the **FastAPI** application, in this case), take the HTTP response from the application, encrypt it using the appropriate certificate and sending it back to the client using HTTPS. This server is often called a TLS Termination Proxy. +Some of the options you could use as a TLS Termination Proxy are: + +* Traefik (that can also handle certificate renewals) +* Caddy (that can also handle certificate renewals) +* Nginx +* HAProxy ## Let's Encrypt -Before Let's Encrypt, these HTTPS certificates were sold by trusted third-parties. +Before Let's Encrypt, these **HTTPS certificates** were sold by trusted third parties. The process to acquire one of these certificates used to be cumbersome, require quite some paperwork and the certificates were quite expensive. -But then Let's Encrypt was created. +But then **Let's Encrypt** was created. -It is a project from the Linux Foundation. It provides HTTPS certificates for free. In an automated way. These certificates use all the standard cryptographic security, and are short lived (about 3 months), so the security is actually better because of their reduced lifespan. +It is a project from the Linux Foundation. It provides **HTTPS certificates for free**, in an automated way. These certificates use all the standard cryptographic security, and are short-lived (about 3 months), so the **security is actually better** because of their reduced lifespan. The domains are securely verified and the certificates are generated automatically. This also allows automating the renewal of these certificates. -The idea is to automate the acquisition and renewal of these certificates, so that you can have secure HTTPS, for free, forever. +The idea is to automate the acquisition and renewal of these certificates so that you can have **secure HTTPS, for free, forever**. + +## HTTPS for Developers + +Here's an example of how an HTTPS API could look like, step by step, paying attention mainly to the ideas important for developers. + +### Domain Name + +It would probably all start by you **acquiring** some **domain name**. Then, you would configure it in a DNS server (possibly your same cloud provider). + +You would probably get a cloud server (a virtual machine) or something similar, and it would have a fixed **public IP address**. + +In the DNS server(s) you would configure a record (an "`A record`") to point **your domain** to the public **IP address of your server**. + +You would probably do this just once, the first time, when setting everything up. + +!!! tip + This Domain Name part is way before HTTPS, but as everything depends on the domain and the IP address, it's worth mentioning it here. + +### DNS + +Now let's focus on all the actual HTTPS parts. + +First, the browser would check with the **DNS servers** what is the **IP for the domain**, in this case, `someapp.example.com`. + +The DNS servers would tell the browser to use some specific **IP address**. That would be the public IP address used by your server, that you configured in the DNS servers. + + + +### TLS Handshake Start + +The browser would then communicate with that IP address on **port 443** (the HTTPS port). + +The first part of the communication is just to establish the connection between the client and the server and to decide the cryptographic keys they will use, etc. + + + +This interaction between the client and the server to establish the TLS connection is called the **TLS handshake**. + +### TLS with SNI Extension + +**Only one process** in the server can be listening on a specific **port** in a specific **IP address**. There could be other processes listening on other ports in the same IP address, but only one for each combination of IP address and port. + +TLS (HTTPS) uses the specific port `443` by default. So that's the port we would need. + +As only one process can be listening on this port, the process that would do it would be the **TLS Termination Proxy**. + +The TLS Termination Proxy would have access to one or more **TLS certificates** (HTTPS certificates). + +Using the **SNI extension** discussed above, the TLS Termination Proxy would check which of the TLS (HTTPS) certificates available it should use for this connection, using the one that matches the domain expected by the client. + +In this case, it would use the certificate for `someapp.example.com`. + + + +The client already **trusts** the entity that generated that TLS certificate (in this case Let's Encrypt, but we'll see about that later), so it can **verify** that the certificate is valid. + +Then, using the certificate, the client and the TLS Termination Proxy **decide how to encrypt** the rest of the **TCP communication**. This completes the **TLS Handshake** part. + +After this, the client and the server have an **encrypted TCP connection**, this is what TLS provides. And then they can use that connection to start the actual **HTTP communication**. + +And that's what **HTTPS** is, it's just plain **HTTP** inside a **secure TLS connection** instead of a pure (unencrypted) TCP connection. + +!!! tip + Notice that the encryption of the communication happens at the **TCP level**, not at the HTTP level. + +### HTTPS Request + +Now that the client and server (specifically the browser and the TLS Termination Proxy) have an **encrypted TCP connection**, they can start the **HTTP communication**. + +So, the client sends an **HTTPS request**. This is just an HTTP request through an encrypted TLS connection. + + + +### Decrypt the Request + +The TLS Termination Proxy would use the encryption agreed to **decrypt the request**, and would transmit the **plain (decrypted) HTTP request** to the process running the application (for example a process with Uvicorn running the FastAPI application). + + + +### HTTP Response + +The application would process the request and send a **plain (unencrypted) HTTP response** to the TLS Termination Proxy. + + + +### HTTPS Response + +The TLS Termination Proxy would then **encrypt the response** using the cryptography agreed before (that started with the certificate for `someapp.example.com`), and send it back to the browser. + +Next, the browser would verify that the response is valid and encrypted with the right cryptographic key, etc. It would then **decrypt the response** and process it. + + + +The client (browser) will know that the response comes from the correct server because it is using the cryptography they agreed using the **HTTPS certificate** before. + +### Multiple Applications + +In the same server (or servers), there could be **multiple applications**, for example, other API programs or a database. + +Only one process can be handling the specific IP and port (the TLS Termination Proxy in our example) but the other applications/processes can be running on the server(s) too, as long as they don't try to use the same **combination of public IP and port**. + + + +That way, the TLS Termination Proxy could handle HTTPS and certificates for **multiple domains**, for multiple applications, and then transmit the requests to the right application in each case. + +### Certificate Renewal + +At some point in the future, each certificate would **expire** (about 3 months after acquiring it). + +And then, there would be another program (in some cases it's another program, in some cases it could be the same TLS Termination Proxy) that would talk to Let's Encrypt, and renew the certificate(s). + + + +The **TLS certificates** are **associated with a domain name**, not with an IP address. + +So, to renew the certificates, the renewal program needs to **prove** to the authority (Let's Encrypt) that it indeed **"owns" and controls that domain**. + +To do that, and to accommodate different application needs, there are several ways it can do it. Some popular ways are: + +* **Modify some DNS records**. + * For this, the renewal program needs to support the APIs of the DNS provider, so, depending on the DNS provider you are using, this might or might not be an option. +* **Run as a server** (at least during the certificate acquisition process) on the public IP address associated with the domain. + * As we said above, only one process can be listening on a specific IP and port. + * This is one of the reasons why it's very useful when the same TLS Termination Proxy also takes care of the certificate renewal process. + * Otherwise, you might have to stop the TLS Termination Proxy momentarily, start the renewal program to acquire the certificates, then configure them with the TLS Termination Proxy, and then restart the TLS Termination Proxy. This is not ideal, as your app(s) will not be available during the time that the TLS Termination Proxy is off. + +All this renewal process, while still serving the app, is one of the main reasons why you would want to have a **separate system to handle HTTPS** with a TLS Termination Proxy instead of just using the TLS certificates with the application server directly (e.g. Uvicorn). + +## Recap + +Having **HTTPS** is very important, and quite **critical** in most cases. Most of the effort you as a developer have to put around HTTPS is just about **understanding these concepts** and how they work. + +But once you know the basic information of **HTTPS for developers** you can easily combine and configure different tools to help you manage everything in a simple way. + +In some of the next chapters I'll show you several concrete examples of how to set up **HTTPS** for **FastAPI** applications. 🔒 diff --git a/docs/en/docs/fastapi-people.md b/docs/en/docs/fastapi-people.md index ce8ea388752c8..0cbcb69c9ab42 100644 --- a/docs/en/docs/fastapi-people.md +++ b/docs/en/docs/fastapi-people.md @@ -114,54 +114,55 @@ These are the **Sponsors**. 😎 They are supporting my work with **FastAPI** (and others), mainly through GitHub Sponsors. +{% if sponsors %} + +{% if sponsors.gold %} + ### Gold Sponsors -{% if sponsors %} {% for sponsor in sponsors.gold -%} {% endfor %} {% endif %} +{% if sponsors.silver %} + ### Silver Sponsors -{% if sponsors %} {% for sponsor in sponsors.silver -%} {% endfor %} {% endif %} +{% if sponsors.bronze %} + ### Bronze Sponsors -{% if sponsors %} {% for sponsor in sponsors.bronze -%} {% endfor %} {% endif %} +{% endif %} + ### Individual Sponsors -{% if people %} -{% if people.sponsors_50 %} +{% if github_sponsors %} +{% for group in github_sponsors.sponsors %}
-{% for user in people.sponsors_50 %} - -{% endfor %} +{% for user in group %} +{% if user.login not in sponsors_badge.logins %} -
+
@{{ user.login }}
{% endif %} -{% endif %} - -{% if people %} -
-{% for user in people.sponsors %} - - {% endfor %}
+ +{% endfor %} {% endif %} ## About the data - technical details diff --git a/docs/en/docs/features.md b/docs/en/docs/features.md index 0ca7902aaca37..c92795d4a7ced 100644 --- a/docs/en/docs/features.md +++ b/docs/en/docs/features.md @@ -32,7 +32,6 @@ If you need a 2 minute refresher of how to use Python types (even if you don't u You write standard Python with types: ```Python -from typing import List, Dict from datetime import date from pydantic import BaseModel diff --git a/docs/en/docs/img/deployment/https/https.drawio b/docs/en/docs/img/deployment/https/https.drawio new file mode 100644 index 0000000000000..31cfab96bce3a --- /dev/null +++ b/docs/en/docs/img/deployment/https/https.drawio @@ -0,0 +1,277 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/docs/en/docs/img/deployment/https/https.svg b/docs/en/docs/img/deployment/https/https.svg new file mode 100644 index 0000000000000..e63345eba3204 --- /dev/null +++ b/docs/en/docs/img/deployment/https/https.svg @@ -0,0 +1,62 @@ +
Server(s)
Server(s)
https://someapp.example.com
https://someapp.example.com
DNS Servers
DNS Servers
TLS Termination Proxy
TLS Termination Proxy +
Cert Renovation Program
Cert Renovation Program
Let's Encrypt
Let's Encrypt
FastAPI app for: someapp.example.com
FastAPI app for: someapp.example.com
Another app: another.example.com
Another app: another.example.com
One more app: onemore.example.com
One more app: onemore.example.com
A Database
A Database
Plain response from: someapp.example.com
Plain response from: someapp.example.com
Port 443 (HTTPS)
Port 443 (HTTPS)
Encrypted request for: someapp.example.com
Encrypted request for: someapp.example.com
Who is: someapp.example.com
Who is: someapp.example.com
IP:
123.124.125.126
IP:...
Renew HTTPS cert for: someapp.example.com
Renew HTTPS cert for: someapp.example.com
New HTTPS cert for: someapp.example.com
New HTTPS cert for: someapp.example.com
TLS Handshake
TLS Handshake
Encrypted response from: someapp.example.com
Encrypted response from: someapp.example.com
HTTPS certificates
HTTPS certificates +
someapp.example.com
someapp.example.com +
another.example.net
another.example.net +
onemore.example.org
onemore.example.org +
IP:
123.124.125.126
IP:...
Decrypted request for: someapp.example.com
Decrypted request for: someapp.example.com
Viewer does not support full SVG 1.1
\ No newline at end of file diff --git a/docs/en/docs/img/deployment/https/https01.drawio b/docs/en/docs/img/deployment/https/https01.drawio new file mode 100644 index 0000000000000..9bc5340ce5858 --- /dev/null +++ b/docs/en/docs/img/deployment/https/https01.drawio @@ -0,0 +1,78 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/docs/en/docs/img/deployment/https/https01.svg b/docs/en/docs/img/deployment/https/https01.svg new file mode 100644 index 0000000000000..4fee0adfc0161 --- /dev/null +++ b/docs/en/docs/img/deployment/https/https01.svg @@ -0,0 +1,57 @@ +
https://someapp.example.com
https://someapp.example.com
DNS Servers
DNS Servers
Who is: someapp.example.com
Who is: someapp.example.com
IP:
123.124.125.126
IP:...
Viewer does not support full SVG 1.1
\ No newline at end of file diff --git a/docs/en/docs/img/deployment/https/https02.drawio b/docs/en/docs/img/deployment/https/https02.drawio new file mode 100644 index 0000000000000..0f7578d3e4dc4 --- /dev/null +++ b/docs/en/docs/img/deployment/https/https02.drawio @@ -0,0 +1,110 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/docs/en/docs/img/deployment/https/https02.svg b/docs/en/docs/img/deployment/https/https02.svg new file mode 100644 index 0000000000000..1f37a7098b373 --- /dev/null +++ b/docs/en/docs/img/deployment/https/https02.svg @@ -0,0 +1,57 @@ +
Server(s)
Server(s)
https://someapp.example.com
https://someapp.example.com
DNS Servers
DNS Servers
Port 443 (HTTPS)
Port 443 (HTTPS)
IP:
123.124.125.126
IP:...
Who is: someapp.example.com
Who is: someapp.example.com
IP:
123.124.125.126
IP:...
TLS Handshake
TLS Handshake
Viewer does not support full SVG 1.1
\ No newline at end of file diff --git a/docs/en/docs/img/deployment/https/https03.drawio b/docs/en/docs/img/deployment/https/https03.drawio new file mode 100644 index 0000000000000..c5766086c082d --- /dev/null +++ b/docs/en/docs/img/deployment/https/https03.drawio @@ -0,0 +1,131 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/docs/en/docs/img/deployment/https/https03.svg b/docs/en/docs/img/deployment/https/https03.svg new file mode 100644 index 0000000000000..e68e1c45964ac --- /dev/null +++ b/docs/en/docs/img/deployment/https/https03.svg @@ -0,0 +1,62 @@ +
Server(s)
Server(s)
https://someapp.example.com
https://someapp.example.com
DNS Servers
DNS Servers
TLS Termination Proxy
TLS Termination Proxy +
Port 443 (HTTPS)
Port 443 (HTTPS)
Who is: someapp.example.com
Who is: someapp.example.com
IP:
123.124.125.126
IP:...
TLS Handshake
TLS Handshake
HTTPS certificates
HTTPS certificates +
someapp.example.com
someapp.example.com +
another.example.net
another.example.net +
onemore.example.org
onemore.example.org +
IP:
123.124.125.126
IP:...
Viewer does not support full SVG 1.1
\ No newline at end of file diff --git a/docs/en/docs/img/deployment/https/https04.drawio b/docs/en/docs/img/deployment/https/https04.drawio new file mode 100644 index 0000000000000..ea357a6c1b475 --- /dev/null +++ b/docs/en/docs/img/deployment/https/https04.drawio @@ -0,0 +1,152 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/docs/en/docs/img/deployment/https/https04.svg b/docs/en/docs/img/deployment/https/https04.svg new file mode 100644 index 0000000000000..4c9b7999bc0dd --- /dev/null +++ b/docs/en/docs/img/deployment/https/https04.svg @@ -0,0 +1,62 @@ +
Server(s)
Server(s)
https://someapp.example.com
https://someapp.example.com
DNS Servers
DNS Servers
TLS Termination Proxy
TLS Termination Proxy +
Port 443 (HTTPS)
Port 443 (HTTPS)
Encrypted request for: someapp.example.com
Encrypted request for: someapp.example.com
Who is: someapp.example.com
Who is: someapp.example.com
IP:
123.124.125.126
IP:...
TLS Handshake
TLS Handshake
HTTPS certificates
HTTPS certificates +
someapp.example.com
someapp.example.com +
another.example.net
another.example.net +
onemore.example.org
onemore.example.org +
IP:
123.124.125.126
IP:...
Viewer does not support full SVG 1.1
\ No newline at end of file diff --git a/docs/en/docs/img/deployment/https/https05.drawio b/docs/en/docs/img/deployment/https/https05.drawio new file mode 100644 index 0000000000000..9b8b7c6f710df --- /dev/null +++ b/docs/en/docs/img/deployment/https/https05.drawio @@ -0,0 +1,166 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/docs/en/docs/img/deployment/https/https05.svg b/docs/en/docs/img/deployment/https/https05.svg new file mode 100644 index 0000000000000..d11647b9b3174 --- /dev/null +++ b/docs/en/docs/img/deployment/https/https05.svg @@ -0,0 +1,62 @@ +
Server(s)
Server(s)
https://someapp.example.com
https://someapp.example.com
DNS Servers
DNS Servers
TLS Termination Proxy
TLS Termination Proxy +
FastAPI app for: someapp.example.com
FastAPI app for: someapp.example.com
Decrypted request for: someapp.example.com
Decrypted request for: someapp.example.com
Port 443 (HTTPS)
Port 443 (HTTPS)
Encrypted request for: someapp.example.com
Encrypted request for: someapp.example.com
Who is: someapp.example.com
Who is: someapp.example.com
IP:
123.124.125.126
IP:...
TLS Handshake
TLS Handshake
HTTPS certificates
HTTPS certificates +
someapp.example.com
someapp.example.com +
another.example.net
another.example.net +
onemore.example.org
onemore.example.org +
IP:
123.124.125.126
IP:...
Viewer does not support full SVG 1.1
\ No newline at end of file diff --git a/docs/en/docs/img/deployment/https/https06.drawio b/docs/en/docs/img/deployment/https/https06.drawio new file mode 100644 index 0000000000000..5bb85813fd5ed --- /dev/null +++ b/docs/en/docs/img/deployment/https/https06.drawio @@ -0,0 +1,183 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/docs/en/docs/img/deployment/https/https06.svg b/docs/en/docs/img/deployment/https/https06.svg new file mode 100644 index 0000000000000..10e03b7c5642e --- /dev/null +++ b/docs/en/docs/img/deployment/https/https06.svg @@ -0,0 +1,62 @@ +
Server(s)
Server(s)
https://someapp.example.com
https://someapp.example.com
DNS Servers
DNS Servers
TLS Termination Proxy
TLS Termination Proxy +
FastAPI app for: someapp.example.com
FastAPI app for: someapp.example.com
Plain response from: someapp.example.com
Plain response from: someapp.example.com
Decrypted request for: someapp.example.com
Decrypted request for: someapp.example.com
Port 443 (HTTPS)
Port 443 (HTTPS)
Encrypted request for: someapp.example.com
Encrypted request for: someapp.example.com
Who is: someapp.example.com
Who is: someapp.example.com
IP:
123.124.125.126
IP:...
TLS Handshake
TLS Handshake
HTTPS certificates
HTTPS certificates +
someapp.example.com
someapp.example.com +
another.example.net
another.example.net +
onemore.example.org
onemore.example.org +
IP:
123.124.125.126
IP:...
Viewer does not support full SVG 1.1
\ No newline at end of file diff --git a/docs/en/docs/img/deployment/https/https07.drawio b/docs/en/docs/img/deployment/https/https07.drawio new file mode 100644 index 0000000000000..1ca994b2236ae --- /dev/null +++ b/docs/en/docs/img/deployment/https/https07.drawio @@ -0,0 +1,203 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/docs/en/docs/img/deployment/https/https07.svg b/docs/en/docs/img/deployment/https/https07.svg new file mode 100644 index 0000000000000..e409d8871332e --- /dev/null +++ b/docs/en/docs/img/deployment/https/https07.svg @@ -0,0 +1,62 @@ +
Server(s)
Server(s)
https://someapp.example.com
https://someapp.example.com
DNS Servers
DNS Servers
TLS Termination Proxy
TLS Termination Proxy +
FastAPI app for: someapp.example.com
FastAPI app for: someapp.example.com
Plain response from: someapp.example.com
Plain response from: someapp.example.com
Decrypted request for: someapp.example.com
Decrypted request for: someapp.example.com
Port 443 (HTTPS)
Port 443 (HTTPS)
Encrypted request for: someapp.example.com
Encrypted request for: someapp.example.com
Who is: someapp.example.com
Who is: someapp.example.com
IP:
123.124.125.126
IP:...
TLS Handshake
TLS Handshake
Encrypted response from: someapp.example.com
Encrypted response from: someapp.example.com
HTTPS certificates
HTTPS certificates +
someapp.example.com
someapp.example.com +
another.example.net
another.example.net +
onemore.example.org
onemore.example.org +
IP:
123.124.125.126
IP:...
Viewer does not support full SVG 1.1
\ No newline at end of file diff --git a/docs/en/docs/img/deployment/https/https08.drawio b/docs/en/docs/img/deployment/https/https08.drawio new file mode 100644 index 0000000000000..8a4f41056ca69 --- /dev/null +++ b/docs/en/docs/img/deployment/https/https08.drawio @@ -0,0 +1,217 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/docs/en/docs/img/deployment/https/https08.svg b/docs/en/docs/img/deployment/https/https08.svg new file mode 100644 index 0000000000000..3047dd821759b --- /dev/null +++ b/docs/en/docs/img/deployment/https/https08.svg @@ -0,0 +1,62 @@ +
Server(s)
Server(s)
https://someapp.example.com
https://someapp.example.com
DNS Servers
DNS Servers
TLS Termination Proxy
TLS Termination Proxy +
FastAPI app for: someapp.example.com
FastAPI app for: someapp.example.com
Another app: another.example.com
Another app: another.example.com
One more app: onemore.example.com
One more app: onemore.example.com
A Database
A Database
Plain response from: someapp.example.com
Plain response from: someapp.example.com
Decrypted request for: someapp.example.com
Decrypted request for: someapp.example.com
Port 443 (HTTPS)
Port 443 (HTTPS)
Encrypted request for: someapp.example.com
Encrypted request for: someapp.example.com
Who is: someapp.example.com
Who is: someapp.example.com
IP:
123.124.125.126
IP:...
TLS Handshake
TLS Handshake
Encrypted response from: someapp.example.com
Encrypted response from: someapp.example.com
HTTPS certificates
HTTPS certificates +
someapp.example.com
someapp.example.com +
another.example.net
another.example.net +
onemore.example.org
onemore.example.org +
IP:
123.124.125.126
IP:...
Viewer does not support full SVG 1.1
\ No newline at end of file diff --git a/docs/en/docs/img/sponsors/calmcode.jpg b/docs/en/docs/img/sponsors/calmcode.jpg new file mode 100644 index 0000000000000..59e68dd073578 Binary files /dev/null and b/docs/en/docs/img/sponsors/calmcode.jpg differ diff --git a/docs/en/docs/release-notes.md b/docs/en/docs/release-notes.md index ef29e9f236d5d..f67b11f0f1d63 100644 --- a/docs/en/docs/release-notes.md +++ b/docs/en/docs/release-notes.md @@ -2,6 +2,34 @@ ## Latest Changes +* 📝 Upgrade HTTPS guide with more explanations and diagrams. PR [#3950](https://github.com/tiangolo/fastapi/pull/3950) by [@tiangolo](https://github.com/tiangolo). +* 🌐 Add German translation for `docs/features.md`. PR [#3699](https://github.com/tiangolo/fastapi/pull/3699) by [@mawassk](https://github.com/mawassk). +* 🎨 Tweak CSS styles for shell animations. PR [#3888](https://github.com/tiangolo/fastapi/pull/3888) by [@tiangolo](https://github.com/tiangolo). +* 🔧 Add new Sponsor Calmcode.io. PR [#3777](https://github.com/tiangolo/fastapi/pull/3777) by [@tiangolo](https://github.com/tiangolo). + +## 0.68.1 + +* ✨ Add support for `read_with_orm_mode`, to support [SQLModel](https://sqlmodel.tiangolo.com/) relationship attributes. PR [#3757](https://github.com/tiangolo/fastapi/pull/3757) by [@tiangolo](https://github.com/tiangolo). + +### Translations + +* 🌐 Add Portuguese translation of `docs/fastapi-people.md`. PR [#3461](https://github.com/tiangolo/fastapi/pull/3461) by [@ComicShrimp](https://github.com/ComicShrimp). +* 🌐 Add Chinese translation for `docs/tutorial/dependencies/dependencies-in-path-operation-decorators.md`. PR [#3492](https://github.com/tiangolo/fastapi/pull/3492) by [@jaystone776](https://github.com/jaystone776). +* 🔧 Add new Translation tracking issues for German and Indonesian. PR [#3718](https://github.com/tiangolo/fastapi/pull/3718) by [@tiangolo](https://github.com/tiangolo). +* 🌐 Add Chinese translation for `docs/tutorial/dependencies/sub-dependencies.md`. PR [#3491](https://github.com/tiangolo/fastapi/pull/3491) by [@jaystone776](https://github.com/jaystone776). +* 🌐 Add Portuguese translation for `docs/advanced/index.md`. PR [#3460](https://github.com/tiangolo/fastapi/pull/3460) by [@ComicShrimp](https://github.com/ComicShrimp). +* 🌐 Portuguese translation of `docs/async.md`. PR [#1330](https://github.com/tiangolo/fastapi/pull/1330) by [@Serrones](https://github.com/Serrones). +* 🌐 Add French translation for `docs/async.md`. PR [#3416](https://github.com/tiangolo/fastapi/pull/3416) by [@Smlep](https://github.com/Smlep). + +### Internal + +* ✨ Add GitHub Action: Notify Translations. PR [#3715](https://github.com/tiangolo/fastapi/pull/3715) by [@tiangolo](https://github.com/tiangolo). +* ✨ Update computation of FastAPI People and sponsors. PR [#3714](https://github.com/tiangolo/fastapi/pull/3714) by [@tiangolo](https://github.com/tiangolo). +* ✨ Enable recent Material for MkDocs Insiders features. PR [#3710](https://github.com/tiangolo/fastapi/pull/3710) by [@tiangolo](https://github.com/tiangolo). +* 🔥 Remove/clean extra imports from examples in docs for features. PR [#3709](https://github.com/tiangolo/fastapi/pull/3709) by [@tiangolo](https://github.com/tiangolo). +* ➕ Update docs library to include sources in Markdown. PR [#3648](https://github.com/tiangolo/fastapi/pull/3648) by [@tiangolo](https://github.com/tiangolo). +* ⬆ Enable tests for Python 3.9. PR [#2298](https://github.com/tiangolo/fastapi/pull/2298) by [@Kludex](https://github.com/Kludex). +* 👥 Update FastAPI People. PR [#3642](https://github.com/tiangolo/fastapi/pull/3642) by [@github-actions[bot]](https://github.com/apps/github-actions). ## 0.68.0 diff --git a/docs/en/mkdocs.yml b/docs/en/mkdocs.yml index d2fe2acaf6dc7..a11ac6871289f 100644 --- a/docs/en/mkdocs.yml +++ b/docs/en/mkdocs.yml @@ -20,6 +20,7 @@ theme: features: - search.suggest - search.highlight + - content.tabs.link icon: repo: fontawesome/brands/github-alt logo: img/icon-white.svg @@ -162,7 +163,10 @@ markdown_extensions: permalink: true - markdown.extensions.codehilite: guess_lang: false -- markdown_include.include: +# Uncomment these 2 lines during development to more easily add highlights +# - pymdownx.highlight: +# linenums: true +- mdx_include: base_path: docs - admonition - codehilite @@ -171,7 +175,7 @@ markdown_extensions: custom_fences: - name: mermaid class: mermaid - format: !!python/name:pymdownx.superfences.fence_div_format '' + format: !!python/name:pymdownx.superfences.fence_code_format '' - pymdownx.tabbed extra: social: @@ -224,6 +228,5 @@ extra_css: - css/termynal.css - css/custom.css extra_javascript: -- https://unpkg.com/mermaid@8.4.6/dist/mermaid.min.js - js/termynal.js - js/custom.js diff --git a/docs/en/overrides/main.html b/docs/en/overrides/main.html index eca89a553aec2..aa381faa37f4c 100644 --- a/docs/en/overrides/main.html +++ b/docs/en/overrides/main.html @@ -22,12 +22,12 @@
-
+
diff --git a/docs/es/docs/features.md b/docs/es/docs/features.md index 4e7453566a2ae..945b2cc9438de 100644 --- a/docs/es/docs/features.md +++ b/docs/es/docs/features.md @@ -32,7 +32,6 @@ Si necesitas un repaso de 2 minutos de cómo usar los tipos de Python (así no u Escribes Python estándar con tipos así: ```Python -from typing import List, Dict from datetime import date from pydantic import BaseModel diff --git a/docs/es/mkdocs.yml b/docs/es/mkdocs.yml index e9e6511485a50..3c0c65c877990 100644 --- a/docs/es/mkdocs.yml +++ b/docs/es/mkdocs.yml @@ -20,6 +20,7 @@ theme: features: - search.suggest - search.highlight + - content.tabs.link icon: repo: fontawesome/brands/github-alt logo: https://fastapi.tiangolo.com/img/icon-white.svg @@ -68,7 +69,7 @@ markdown_extensions: permalink: true - markdown.extensions.codehilite: guess_lang: false -- markdown_include.include: +- mdx_include: base_path: docs - admonition - codehilite @@ -77,7 +78,7 @@ markdown_extensions: custom_fences: - name: mermaid class: mermaid - format: !!python/name:pymdownx.superfences.fence_div_format '' + format: !!python/name:pymdownx.superfences.fence_code_format '' - pymdownx.tabbed extra: social: @@ -130,6 +131,5 @@ extra_css: - https://fastapi.tiangolo.com/css/termynal.css - https://fastapi.tiangolo.com/css/custom.css extra_javascript: -- https://unpkg.com/mermaid@8.4.6/dist/mermaid.min.js - https://fastapi.tiangolo.com/js/termynal.js - https://fastapi.tiangolo.com/js/custom.js diff --git a/docs/fr/docs/async.md b/docs/fr/docs/async.md new file mode 100644 index 0000000000000..20f4ee1011b61 --- /dev/null +++ b/docs/fr/docs/async.md @@ -0,0 +1,394 @@ +# Concurrence et les mots-clés async et await + +Cette page vise à fournir des détails sur la syntaxe `async def` pour les *fonctions de chemins* et quelques rappels sur le code asynchrone, la concurrence et le parallélisme. + +## Vous êtes pressés ? + +TL;DR : + +Si vous utilisez des bibliothèques tierces qui nécessitent d'être appelées avec `await`, telles que : + +```Python +results = await some_library() +``` +Alors, déclarez vos *fonctions de chemins* avec `async def` comme ceci : + +```Python hl_lines="2" +@app.get('/') +async def read_results(): + results = await some_library() + return results +``` + +!!! note + Vous pouvez uniquement utiliser `await` dans les fonctions créées avec `async def`. + +--- + +Si vous utilisez une bibliothèque externe qui communique avec quelque chose (une BDD, une API, un système de fichiers, etc.) et qui ne supporte pas l'utilisation d'`await` (ce qui est actuellement le cas pour la majorité des bibliothèques de BDD), alors déclarez vos *fonctions de chemin* normalement, avec le classique `def`, comme ceci : + +```Python hl_lines="2" +@app.get('/') +def results(): + results = some_library() + return results +``` + +--- + +Si votre application n'a pas à communiquer avec une bibliothèque externe et pas à attendre de réponse, utilisez `async def`. + +--- + +Si vous ne savez pas, utilisez seulement `def` comme vous le feriez habituellement. + +--- + +**Note** : vous pouvez mélanger `def` et `async def` dans vos *fonctions de chemin* autant que nécessaire, **FastAPI** saura faire ce qu'il faut avec. + +Au final, peu importe le cas parmi ceux ci-dessus, **FastAPI** fonctionnera de manière asynchrone et sera extrêmement rapide. + +Mais si vous suivez bien les instructions ci-dessus, alors **FastAPI** pourra effectuer quelques optimisations et ainsi améliorer les performances. + +## Détails techniques + +Les versions modernes de Python supportent le **code asynchrone** grâce aux **"coroutines"** avec les syntaxes **`async` et `await`**. + +Analysons les différentes parties de cette phrase dans les sections suivantes : + +* **Code asynchrone** +* **`async` et `await`** +* **Coroutines** + +## Code asynchrone + +Faire du code asynchrone signifie que le langage 💬 est capable de dire à l'ordinateur / au programme 🤖 qu'à un moment du code, il 🤖 devra attendre que *quelque chose d'autre* se termine autre part. Disons que ce *quelque chose d'autre* est appelé "fichier-lent" 📝. + +Donc, pendant ce temps, l'ordinateur pourra effectuer d'autres tâches, pendant que "fichier-lent" 📝 se termine. + +Ensuite l'ordinateur / le programme 🤖 reviendra à chaque fois qu'il en a la chance que ce soit parce qu'il attend à nouveau, ou car il 🤖 a fini tout le travail qu'il avait à faire. Il 🤖 regardera donc si les tâches qu'il attend ont terminé d'être effectuées. + +Ensuite, il 🤖 prendra la première tâche à finir (disons, notre "fichier-lent" 📝) et continuera à faire avec cette dernière ce qu'il était censé. + +Ce "attendre quelque chose d'autre" fait généralement référence à des opérations I/O qui sont relativement "lentes" (comparées à la vitesse du processeur et de la mémoire RAM) telles qu'attendre que : + +* de la donnée soit envoyée par le client à travers le réseau +* de la donnée envoyée depuis votre programme soit reçue par le client à travers le réseau +* le contenu d'un fichier sur le disque soit lu par le système et passé à votre programme +* le contenu que votre programme a passé au système soit écrit sur le disque +* une opération effectuée à distance par une API se termine +* une opération en BDD se termine +* une requête à une BDD renvoie un résultat +* etc. + +Le temps d'exécution étant consommé majoritairement par l'attente d'opérations I/O on appelle ceci des opérations "I/O bound". + +Ce concept se nomme l'"asynchronisme" car l'ordinateur / le programme n'a pas besoin d'être "synchronisé" avec la tâche, attendant le moment exact où cette dernière se terminera en ne faisant rien, pour être capable de récupérer le résultat de la tâche et l'utiliser dans la suite des opérations. + +À la place, en étant "asynchrone", une fois terminée, une tâche peut légèrement attendre (quelques microsecondes) que l'ordinateur / le programme finisse ce qu'il était en train de faire, et revienne récupérer le résultat. + +Pour parler de tâches "synchrones" (en opposition à "asynchrones"), on utilise souvent le terme "séquentiel", car l'ordinateur / le programme va effectuer toutes les étapes d'une tâche séquentiellement avant de passer à une autre tâche, même si ces étapes impliquent de l'attente. + +### Concurrence et Burgers + +L'idée de code **asynchrone** décrite ci-dessus est parfois aussi appelée **"concurrence"**. Ce qui est différent du **"parallélisme"**. + +La **concurrence** et le **parallélisme** sont tous deux liés à l'idée de "différentes choses arrivant plus ou moins au même moment". + +Mais les détails entre la **concurrence** et le **parallélisme** diffèrent sur de nombreux points. + +Pour expliquer la différence, voici une histoire de burgers : + +#### Burgers concurrents + +Vous amenez votre crush 😍 dans votre fast food 🍔 favori, et faites la queue pendant que le serveur 💁 prend les commandes des personnes devant vous. + +Puis vient votre tour, vous commandez alors 2 magnifiques burgers 🍔 pour votre crush 😍 et vous. + +Vous payez 💸. + +Le serveur 💁 dit quelque chose à son collègue dans la cuisine 👨‍🍳 pour qu'il sache qu'il doit préparer vos burgers 🍔 (bien qu'il soit déjà en train de préparer ceux des clients précédents). + +Le serveur 💁 vous donne le numéro assigné à votre commande. + +Pendant que vous attendez, vous allez choisir une table avec votre crush 😍, vous discutez avec votre crush 😍 pendant un long moment (les burgers étant "magnifiques" ils sont très longs à préparer ✨🍔✨). + +Pendant que vous êtes assis à table, en attendant que les burgers 🍔 soient prêts, vous pouvez passer ce temps à admirer à quel point votre crush 😍 est géniale, mignonne et intelligente ✨😍✨. + +Pendant que vous discutez avec votre crush 😍, de temps en temps vous jetez un coup d'oeil au nombre affiché au-dessus du comptoir pour savoir si c'est à votre tour d'être servis. + +Jusqu'au moment où c'est (enfin) votre tour. Vous allez au comptoir, récupérez vos burgers 🍔 et revenez à votre table. + +Vous et votre crush 😍 mangez les burgers 🍔 et passez un bon moment ✨. + +--- + +Imaginez que vous êtes l'ordinateur / le programme 🤖 dans cette histoire. + +Pendant que vous faites la queue, vous être simplement inactif 😴, attendant votre tour, ne faisant rien de "productif". Mais la queue est rapide car le serveur 💁 prend seulement les commandes (et ne les prépare pas), donc tout va bien. + +Ensuite, quand c'est votre tour, vous faites des actions "productives" 🤓, vous étudiez le menu, décidez ce que vous voulez, demandez à votre crush 😍 son choix, payez 💸, vérifiez que vous utilisez la bonne carte de crédit, vérifiez que le montant débité sur la carte est correct, vérifiez que la commande contient les bons produits, etc. + +Mais ensuite, même si vous n'avez pas encore vos burgers 🍔, votre travail avec le serveur 💁 est "en pause" ⏸, car vous devez attendre 🕙 que vos burgers soient prêts. + +Après vous être écarté du comptoir et vous être assis à votre table avec le numéro de votre commande, vous pouvez tourner 🔀 votre attention vers votre crush 😍, et "travailler" ⏯ 🤓 là-dessus. Vous êtes donc à nouveau en train de faire quelque chose de "productif" 🤓, vous flirtez avec votre crush 😍. + +Puis le serveur 💁 dit "J'ai fini de préparer les burgers" 🍔 en mettant votre numéro sur l'affichage du comptoir, mais vous ne courrez pas immédiatement au moment où votre numéro s'affiche. Vous savez que personne ne volera vos burgers 🍔 car vous avez votre numéro et les autres clients ont le leur. + +Vous attendez donc que votre crush 😍 finisse son histoire, souriez gentiment et dites que vous allez chercher les burgers ⏸. + +Pour finir vous allez au comptoir 🔀, vers la tâche initiale qui est désormais terminée ⏯, récupérez les burgers 🍔, remerciez le serveur et ramenez les burgers 🍔 à votre table. Ceci termine l'étape / la tâche d'interaction avec le comptoir ⏹. Ce qui ensuite, crée une nouvelle tâche de "manger les burgers" 🔀 ⏯, mais la précédente, "récupérer les burgers" est terminée ⏹. + +#### Burgers parallèles + +Imaginons désormais que ce ne sont pas des "burgers concurrents" mais des "burgers parallèles". + +Vous allez avec votre crush 😍 dans un fast food 🍔 parallélisé. + +Vous attendez pendant que plusieurs (disons 8) serveurs qui sont aussi des cuisiniers 👨‍🍳👨‍🍳👨‍🍳👨‍🍳👨‍🍳👨‍🍳👨‍🍳👨‍🍳 prennent les commandes des personnes devant vous. + +Chaque personne devant vous attend 🕙 que son burger 🍔 soit prêt avant de quitter le comptoir car chacun des 8 serveurs va lui-même préparer le burger directement avant de prendre la commande suivante. + +Puis c'est enfin votre tour, vous commandez 2 magnifiques burgers 🍔 pour vous et votre crush 😍. + +Vous payez 💸. + +Le serveur va dans la cuisine 👨‍🍳. + +Vous attendez devant le comptoir afin que personne ne prenne vos burgers 🍔 avant vous, vu qu'il n'y a pas de numéro de commande. + +Vous et votre crush 😍 étant occupés à vérifier que personne ne passe devant vous prendre vos burgers au moment où ils arriveront 🕙, vous ne pouvez pas vous préoccuper de votre crush 😞. + +C'est du travail "synchrone", vous être "synchronisés" avec le serveur/cuisinier 👨‍🍳. Vous devez attendre 🕙 et être présent au moment exact où le serveur/cuisinier 👨‍🍳 finira les burgers 🍔 et vous les donnera, sinon quelqu'un risque de vous les prendre. + +Puis le serveur/cuisinier 👨‍🍳 revient enfin avec vos burgers 🍔, après un long moment d'attente 🕙 devant le comptoir. + +Vous prenez vos burgers 🍔 et allez à une table avec votre crush 😍 + +Vous les mangez, et vous avez terminé 🍔 ⏹. + +Durant tout ce processus, il n'y a presque pas eu de discussions ou de flirts car la plupart de votre temps à été passé à attendre 🕙 devant le comptoir 😞. + +--- + +Dans ce scénario de burgers parallèles, vous êtes un ordinateur / programme 🤖 avec deux processeurs (vous et votre crush 😍) attendant 🕙 à deux et dédiant votre attention 🕙 à "attendre devant le comptoir" pour une longue durée. + +Le fast-food a 8 processeurs (serveurs/cuisiniers) 👨‍🍳👨‍🍳👨‍🍳👨‍🍳👨‍🍳👨‍🍳👨‍🍳👨‍🍳. Alors que le fast-food de burgers concurrents en avait 2 (un serveur et un cuisinier). + +Et pourtant l'expérience finale n'est pas meilleure 😞. + +--- + +C'est donc l'histoire équivalente parallèle pour les burgers 🍔. + +Pour un exemple plus courant dans la "vie réelle", imaginez une banque. + +Jusqu'à récemment, la plupart des banques avaient plusieurs caisses (et banquiers) 👨‍💼👨‍💼👨‍💼👨‍💼 et une unique file d'attente 🕙🕙🕙🕙🕙🕙🕙🕙. + +Tous les banquiers faisaient l'intégralité du travail avec chaque client avant de passer au suivant 👨‍💼⏯. + +Et vous deviez attendre 🕙 dans la file pendant un long moment ou vous perdiez votre place. + +Vous n'auriez donc probablement pas envie d'amener votre crush 😍 avec vous à la banque 🏦. + +#### Conclusion + +Dans ce scénario des "burgers du fast-food avec votre crush", comme il y a beaucoup d'attente 🕙, il est très logique d'avoir un système concurrent ⏸🔀⏯. + +Et c'est le cas pour la plupart des applications web. + +Vous aurez de nombreux, nombreux utilisateurs, mais votre serveur attendra 🕙 que leur connexion peu performante envoie des requêtes. + +Puis vous attendrez 🕙 de nouveau que leurs réponses reviennent. + +Cette "attente" 🕙 se mesure en microsecondes, mais tout de même, en cumulé cela fait beaucoup d'attente. + +C'est pourquoi il est logique d'utiliser du code asynchrone ⏸🔀⏯ pour des APIs web. + +La plupart des frameworks Python existants (y compris Flask et Django) ont été créés avant que les nouvelles fonctionnalités asynchrones de Python n'existent. Donc, les façons dont ils peuvent être déployés supportent l'exécution parallèle et une ancienne forme d'exécution asynchrone qui n'est pas aussi puissante que les nouvelles fonctionnalités de Python. + +Et cela, bien que les spécifications principales du web asynchrone en Python (ou ASGI) ont été développées chez Django, pour ajouter le support des WebSockets. + +Ce type d'asynchronicité est ce qui a rendu NodeJS populaire (bien que NodeJS ne soit pas parallèle) et c'est la force du Go en tant que langage de programmation. + +Et c'est le même niveau de performance que celui obtenu avec **FastAPI**. + +Et comme on peut avoir du parallélisme et de l'asynchronicité en même temps, on obtient des performances plus hautes que la plupart des frameworks NodeJS et égales à celles du Go, qui est un langage compilé plus proche du C (tout ça grâce à Starlette). + +### Est-ce que la concurrence est mieux que le parallélisme ? + +Nope ! C'est ça la morale de l'histoire. + +La concurrence est différente du parallélisme. C'est mieux sur des scénarios **spécifiques** qui impliquent beaucoup d'attente. À cause de ça, c'est généralement bien meilleur que le parallélisme pour le développement d'applications web. Mais pas pour tout. + +Donc pour équilibrer tout ça, imaginez l'histoire suivante : + +> Vous devez nettoyer une grande et sale maison. + +*Oui, c'est toute l'histoire*. + +--- + +Il n'y a plus d'attente 🕙 nulle part, juste beaucoup de travail à effectuer, dans différentes pièces de la maison. + +Vous pourriez diviser en différentes sections comme avec les burgers, d'abord le salon, puis la cuisine, etc. Mais vous n'attendez 🕙 rien, vous ne faites que nettoyer et nettoyer, la séparation en sections ne changerait rien au final. + +Cela prendrait autant de temps pour finir avec ou sans sections (concurrence) et vous auriez effectué la même quantité de travail. + +Mais dans ce cas, si pouviez amener 8 ex-serveurs/cuisiniers/devenus-nettoyeurs 👨‍🍳👨‍🍳👨‍🍳👨‍🍳👨‍🍳👨‍🍳👨‍🍳👨‍🍳, et que chacun d'eux (plus vous) pouvait prendre une zone de la maison pour la nettoyer, vous pourriez faire tout le travail en parallèle, et finir plus tôt. + +Dans ce scénario, chacun des nettoyeurs (vous y compris) serait un processeur, faisant sa partie du travail. + +Et comme la plupart du temps d'exécution est pris par du "vrai" travail (et non de l'attente), et que le travail dans un ordinateur est fait par un CPU, ce sont des problèmes dits "CPU bound". + +--- + +Des exemples communs d'opérations "CPU bounds" sont les procédés qui requièrent des traitements mathématiques complexes. + +Par exemple : + +* Traitements d'**audio** et d'**images**. +* La **vision par ordinateur** : une image est composée de millions de pixels, chaque pixel ayant 3 valeurs / couleurs, les traiter tous va nécessiter d'effectuer des traitements sur chaque pixel, et de préférence tous en même temps. +* L'apprentissage automatique (ou **Machine Learning**) : cela nécessite de nombreuses multiplications de matrices et vecteurs. Imaginez une énorme feuille de calcul remplie de nombres que vous multiplierez entre eux tous au même moment. +* L'apprentissage profond (ou **Deep Learning**) : est un sous-domaine du **Machine Learning**, donc les mêmes raisons s'appliquent. Avec la différence qu'il n'y a pas une unique feuille de calcul de nombres à multiplier, mais une énorme quantité d'entre elles, et dans de nombreux cas, on utilise un processeur spécial pour construire et / ou utiliser ces modèles. + +### Concurrence + Parallélisme : Web + Machine Learning + +Avec **FastAPI** vous pouvez bénéficier de la concurrence qui est très courante en developement web (c'est l'attrait principal de NodeJS). + +Mais vous pouvez aussi profiter du parallélisme et multiprocessing afin de gérer des charges **CPU bound** qui sont récurrentes dans les systèmes de *Machine Learning*. + +Ça, ajouté au fait que Python soit le langage le plus populaire pour la **Data Science**, le **Machine Learning** et surtout le **Deep Learning**, font de **FastAPI** un très bon choix pour les APIs et applications de **Data Science** / **Machine Learning**. + +Pour comprendre comment mettre en place ce parallélisme en production, allez lire la section [Déploiement](deployment/index.md){.internal-link target=_blank}. + +## `async` et `await` + +Les versions modernes de Python ont une manière très intuitive de définir le code asynchrone, tout en gardant une apparence de code "séquentiel" classique en laissant Python faire l'attente pour vous au bon moment. + +Pour une opération qui nécessite de l'attente avant de donner un résultat et qui supporte ces nouvelles fonctionnalités Python, vous pouvez l'utiliser comme tel : + +```Python +burgers = await get_burgers(2) +``` + +Le mot-clé important ici est `await`. Il informe Python qu'il faut attendre ⏸ que `get_burgers(2)` finisse d'effectuer ses opérations 🕙 avant de stocker les résultats dans la variable `burgers`. Grâce à cela, Python saura qu'il peut aller effectuer d'autres opérations 🔀 ⏯ pendant ce temps (comme par exemple recevoir une autre requête). + +Pour que `await` fonctionne, il doit être placé dans une fonction qui supporte l'asynchronicité. Pour que ça soit le cas, il faut déclarer cette dernière avec `async def` : + +```Python hl_lines="1" +async def get_burgers(number: int): + # Opérations asynchrones pour créer les burgers + return burgers +``` + +...et non `def` : + +```Python hl_lines="2" +# Ceci n'est pas asynchrone +def get_sequential_burgers(number: int): + # Opérations asynchrones pour créer les burgers + return burgers +``` + +Avec `async def`, Python sait que dans cette fonction il doit prendre en compte les expressions `await`, et qu'il peut mettre en pause ⏸ l'exécution de la fonction pour aller faire autre chose 🔀 avant de revenir. + +Pour appeler une fonction définie avec `async def`, vous devez utiliser `await`. Donc ceci ne marche pas : + +```Python +# Ceci ne fonctionne pas, car get_burgers a été défini avec async def +burgers = get_burgers(2) +``` + +--- + +Donc, si vous utilisez une bibliothèque qui nécessite que ses fonctions soient appelées avec `await`, vous devez définir la *fonction de chemin* en utilisant `async def` comme dans : + +```Python hl_lines="2-3" +@app.get('/burgers') +async def read_burgers(): + burgers = await get_burgers(2) + return burgers +``` + +### Plus de détails techniques + +Vous avez donc compris que `await` peut seulement être utilisé dans des fonctions définies avec `async def`. + +Mais en même temps, les fonctions définies avec `async def` doivent être appelées avec `await` et donc dans des fonctions définies elles aussi avec `async def`. + +Vous avez donc remarqué ce paradoxe d'oeuf et de la poule, comment appelle-t-on la première fonction `async` ? + +Si vous utilisez **FastAPI**, pas besoin de vous en inquiéter, car cette "première" fonction sera votre *fonction de chemin* ; et **FastAPI** saura comment arriver au résultat attendu. + +Mais si vous utilisez `async` / `await` sans **FastAPI**, allez jetez un coup d'oeil à la documentation officielle de Python. + +### Autres formes de code asynchrone + +L'utilisation d'`async` et `await` est relativement nouvelle dans ce langage. + +Mais cela rend la programmation asynchrone bien plus simple. + +Cette même syntaxe (ou presque) était aussi incluse dans les versions modernes de Javascript (dans les versions navigateur et NodeJS). + +Mais avant ça, gérer du code asynchrone était bien plus complexe et difficile. + +Dans les versions précédentes de Python, vous auriez utilisé des *threads* ou Gevent. Mais le code aurait été bien plus difficile à comprendre, débugger, et concevoir. + +Dans les versions précédentes de Javascript NodeJS / Navigateur, vous auriez utilisé des "callbacks". Menant potentiellement à ce que l'on appelle le "callback hell". + + +## Coroutines + +**Coroutine** est juste un terme élaboré pour désigner ce qui est retourné par une fonction définie avec `async def`. Python sait que c'est comme une fonction classique qui va démarrer à un moment et terminer à un autre, mais qu'elle peut aussi être mise en pause ⏸, du moment qu'il y a un `await` dans son contenu. + +Mais toutes ces fonctionnalités d'utilisation de code asynchrone avec `async` et `await` sont souvent résumées comme l'utilisation des *coroutines*. On peut comparer cela à la principale fonctionnalité clé de Go, les "Goroutines". + +## Conclusion + +Reprenons la phrase du début de la page : + +> Les versions modernes de Python supportent le **code asynchrone** grâce aux **"coroutines"** avec les syntaxes **`async` et `await`**. + +Ceci devrait être plus compréhensible désormais. ✨ + +Tout ceci est donc ce qui donne sa force à **FastAPI** (à travers Starlette) et lui permet d'avoir des performances aussi impressionnantes. + +## Détails très techniques + +!!! warning "Attention !" + Vous pouvez probablement ignorer cela. + + Ce sont des détails très poussés sur comment **FastAPI** fonctionne en arrière-plan. + + Si vous avez de bonnes connaissances techniques (coroutines, threads, code bloquant, etc.) et êtes curieux de comment **FastAPI** gère `async def` versus le `def` classique, cette partie est faite pour vous. + +### Fonctions de chemin + +Quand vous déclarez une *fonction de chemin* avec un `def` normal et non `async def`, elle est exécutée dans un groupe de threads (threadpool) externe qui est ensuite attendu, plutôt que d'être appelée directement (car cela bloquerait le serveur). + +Si vous venez d'un autre framework asynchrone qui ne fonctionne pas comme de la façon décrite ci-dessus et que vous êtes habitués à définir des *fonctions de chemin* basiques avec un simple `def` pour un faible gain de performance (environ 100 nanosecondes), veuillez noter que dans **FastAPI**, l'effet serait plutôt contraire. Dans ces cas-là, il vaut mieux utiliser `async def` à moins que votre *fonction de chemin* utilise du code qui effectue des opérations I/O bloquantes. + +Au final, dans les deux situations, il est fort probable que **FastAPI** soit tout de même [plus rapide](/#performance){.internal-link target=_blank} que (ou au moins de vitesse égale à) votre framework précédent. + +### Dépendances + +La même chose s'applique aux dépendances. Si une dépendance est définie avec `def` plutôt que `async def`, elle est exécutée dans la threadpool externe. + +### Sous-dépendances + +Vous pouvez avoir de multiples dépendances et sous-dépendances dépendant les unes des autres (en tant que paramètres de la définition de la *fonction de chemin*), certaines créées avec `async def` et d'autres avec `def`. Cela fonctionnerait aussi, et celles définies avec un simple `def` seraient exécutées sur un thread externe (venant de la threadpool) plutôt que d'être "attendues". + +### Autres fonctions utilitaires + +Toute autre fonction utilitaire que vous appelez directement peut être créée avec un classique `def` ou avec `async def` et **FastAPI** n'aura pas d'impact sur la façon dont vous l'appelez. + +Contrairement aux fonctions que **FastAPI** appelle pour vous : les *fonctions de chemin* et dépendances. + +Si votre fonction utilitaire est une fonction classique définie avec `def`, elle sera appelée directement (telle qu'écrite dans votre code), pas dans une threadpool, si la fonction est définie avec `async def` alors vous devrez attendre (avec `await`) que cette fonction se termine avant de passer à la suite du code. + +--- + +Encore une fois, ce sont des détails très techniques qui peuvent être utiles si vous venez ici les chercher. + +Sinon, les instructions de la section Vous êtes pressés ? ci-dessus sont largement suffisantes. diff --git a/docs/fr/docs/fastapi-people.md b/docs/fr/docs/fastapi-people.md index ac6e19831343d..9ec2718c47390 100644 --- a/docs/fr/docs/fastapi-people.md +++ b/docs/fr/docs/fastapi-people.md @@ -114,25 +114,28 @@ Ce sont les **Sponsors**. 😎 Ils soutiennent mon travail avec **FastAPI** (et d'autres) avec GitHub Sponsors. +{% if sponsors.gold %} + ### Gold Sponsors -{% if sponsors %} {% for sponsor in sponsors.gold -%} {% endfor %} {% endif %} +{% if sponsors.silver %} + ### Silver Sponsors -{% if sponsors %} {% for sponsor in sponsors.silver -%} {% endfor %} {% endif %} +{% if sponsors.bronze %} + ### Bronze Sponsors -{% if sponsors %} {% for sponsor in sponsors.bronze -%} {% endfor %} @@ -140,28 +143,22 @@ Ils soutiennent mon travail avec **FastAPI** (et d'autres) avec -{% for user in people.sponsors_50 %} - -{% endfor %} +{% for user in group %} +{% if user.login not in sponsors_badge.logins %} -
+ {% endif %} -{% endif %} - -{% if people %} -
-{% for user in people.sponsors %} - - {% endfor %}
+ +{% endfor %} {% endif %} ## À propos des données - détails techniques diff --git a/docs/fr/docs/features.md b/docs/fr/docs/features.md index d669220a6216a..4d8f18403a0d0 100644 --- a/docs/fr/docs/features.md +++ b/docs/fr/docs/features.md @@ -32,7 +32,6 @@ Si vous souhaitez un rappel de 2 minutes sur l'utilisation des types en Python ( Vous écrivez du python standard avec des annotations de types: ```Python -from typing import List, Dict from datetime import date from pydantic import BaseModel diff --git a/docs/fr/mkdocs.yml b/docs/fr/mkdocs.yml index 88b3fe02eb713..4891a29caeac2 100644 --- a/docs/fr/mkdocs.yml +++ b/docs/fr/mkdocs.yml @@ -20,6 +20,7 @@ theme: features: - search.suggest - search.highlight + - content.tabs.link icon: repo: fontawesome/brands/github-alt logo: https://fastapi.tiangolo.com/img/icon-white.svg @@ -58,6 +59,7 @@ nav: - python-types.md - Tutoriel - Guide utilisateur: - tutorial/background-tasks.md +- async.md - project-generation.md - alternatives.md - external-links.md @@ -66,7 +68,7 @@ markdown_extensions: permalink: true - markdown.extensions.codehilite: guess_lang: false -- markdown_include.include: +- mdx_include: base_path: docs - admonition - codehilite @@ -75,7 +77,7 @@ markdown_extensions: custom_fences: - name: mermaid class: mermaid - format: !!python/name:pymdownx.superfences.fence_div_format '' + format: !!python/name:pymdownx.superfences.fence_code_format '' - pymdownx.tabbed extra: social: @@ -128,6 +130,5 @@ extra_css: - https://fastapi.tiangolo.com/css/termynal.css - https://fastapi.tiangolo.com/css/custom.css extra_javascript: -- https://unpkg.com/mermaid@8.4.6/dist/mermaid.min.js - https://fastapi.tiangolo.com/js/termynal.js - https://fastapi.tiangolo.com/js/custom.js diff --git a/docs/id/mkdocs.yml b/docs/id/mkdocs.yml index 983bbd62b2a25..88028533ec626 100644 --- a/docs/id/mkdocs.yml +++ b/docs/id/mkdocs.yml @@ -20,6 +20,7 @@ theme: features: - search.suggest - search.highlight + - content.tabs.link icon: repo: fontawesome/brands/github-alt logo: https://fastapi.tiangolo.com/img/icon-white.svg @@ -58,7 +59,7 @@ markdown_extensions: permalink: true - markdown.extensions.codehilite: guess_lang: false -- markdown_include.include: +- mdx_include: base_path: docs - admonition - codehilite @@ -67,7 +68,7 @@ markdown_extensions: custom_fences: - name: mermaid class: mermaid - format: !!python/name:pymdownx.superfences.fence_div_format '' + format: !!python/name:pymdownx.superfences.fence_code_format '' - pymdownx.tabbed extra: social: @@ -120,6 +121,5 @@ extra_css: - https://fastapi.tiangolo.com/css/termynal.css - https://fastapi.tiangolo.com/css/custom.css extra_javascript: -- https://unpkg.com/mermaid@8.4.6/dist/mermaid.min.js - https://fastapi.tiangolo.com/js/termynal.js - https://fastapi.tiangolo.com/js/custom.js diff --git a/docs/it/mkdocs.yml b/docs/it/mkdocs.yml index bfe2dfe83294f..477f9c55ae715 100644 --- a/docs/it/mkdocs.yml +++ b/docs/it/mkdocs.yml @@ -20,6 +20,7 @@ theme: features: - search.suggest - search.highlight + - content.tabs.link icon: repo: fontawesome/brands/github-alt logo: https://fastapi.tiangolo.com/img/icon-white.svg @@ -58,7 +59,7 @@ markdown_extensions: permalink: true - markdown.extensions.codehilite: guess_lang: false -- markdown_include.include: +- mdx_include: base_path: docs - admonition - codehilite @@ -67,7 +68,7 @@ markdown_extensions: custom_fences: - name: mermaid class: mermaid - format: !!python/name:pymdownx.superfences.fence_div_format '' + format: !!python/name:pymdownx.superfences.fence_code_format '' - pymdownx.tabbed extra: social: @@ -120,6 +121,5 @@ extra_css: - https://fastapi.tiangolo.com/css/termynal.css - https://fastapi.tiangolo.com/css/custom.css extra_javascript: -- https://unpkg.com/mermaid@8.4.6/dist/mermaid.min.js - https://fastapi.tiangolo.com/js/termynal.js - https://fastapi.tiangolo.com/js/custom.js diff --git a/docs/ja/docs/fastapi-people.md b/docs/ja/docs/fastapi-people.md index 1d263449afb6b..229d8dde5a052 100644 --- a/docs/ja/docs/fastapi-people.md +++ b/docs/ja/docs/fastapi-people.md @@ -115,25 +115,28 @@ FastAPIには、様々なバックグラウンドの人々を歓迎する素晴 彼らは、GitHub Sponsors を介して私の **FastAPI** などに関する活動を支援してくれています。 +{% if sponsors.gold %} + ### Gold Sponsors -{% if sponsors %} {% for sponsor in sponsors.gold -%} {% endfor %} {% endif %} +{% if sponsors.silver %} + ### Silver Sponsors -{% if sponsors %} {% for sponsor in sponsors.silver -%} {% endfor %} {% endif %} +{% if sponsors.bronze %} + ### Bronze Sponsors -{% if sponsors %} {% for sponsor in sponsors.bronze -%} {% endfor %} @@ -141,28 +144,22 @@ FastAPIには、様々なバックグラウンドの人々を歓迎する素晴 ### Individual Sponsors -{% if people %} -{% if people.sponsors_50 %} +{% if github_sponsors %} +{% for group in github_sponsors.sponsors %}
-{% for user in people.sponsors_50 %} - -{% endfor %} +{% for user in group %} +{% if user.login not in sponsors_badge.logins %} -
+ {% endif %} -{% endif %} - -{% if people %} -
-{% for user in people.sponsors %} - - {% endfor %}
+ +{% endfor %} {% endif %} ## データについて - 技術詳細 diff --git a/docs/ja/docs/features.md b/docs/ja/docs/features.md index 4ae9c71f6c28d..2c406f4819d8e 100644 --- a/docs/ja/docs/features.md +++ b/docs/ja/docs/features.md @@ -31,7 +31,6 @@ FastAPIの機能はすべて、標準のPython 3.6型宣言に基づいていま 型を使用した標準的なPythonを記述します: ```Python -from typing import List, Dict from datetime import date from pydantic import BaseModel diff --git a/docs/ja/mkdocs.yml b/docs/ja/mkdocs.yml index d928710296da7..2617fec96c85c 100644 --- a/docs/ja/mkdocs.yml +++ b/docs/ja/mkdocs.yml @@ -20,6 +20,7 @@ theme: features: - search.suggest - search.highlight + - content.tabs.link icon: repo: fontawesome/brands/github-alt logo: https://fastapi.tiangolo.com/img/icon-white.svg @@ -98,7 +99,7 @@ markdown_extensions: permalink: true - markdown.extensions.codehilite: guess_lang: false -- markdown_include.include: +- mdx_include: base_path: docs - admonition - codehilite @@ -107,7 +108,7 @@ markdown_extensions: custom_fences: - name: mermaid class: mermaid - format: !!python/name:pymdownx.superfences.fence_div_format '' + format: !!python/name:pymdownx.superfences.fence_code_format '' - pymdownx.tabbed extra: social: @@ -160,6 +161,5 @@ extra_css: - https://fastapi.tiangolo.com/css/termynal.css - https://fastapi.tiangolo.com/css/custom.css extra_javascript: -- https://unpkg.com/mermaid@8.4.6/dist/mermaid.min.js - https://fastapi.tiangolo.com/js/termynal.js - https://fastapi.tiangolo.com/js/custom.js diff --git a/docs/ko/mkdocs.yml b/docs/ko/mkdocs.yml index 8d2119db8dbef..6ee22afba77f7 100644 --- a/docs/ko/mkdocs.yml +++ b/docs/ko/mkdocs.yml @@ -20,6 +20,7 @@ theme: features: - search.suggest - search.highlight + - content.tabs.link icon: repo: fontawesome/brands/github-alt logo: https://fastapi.tiangolo.com/img/icon-white.svg @@ -64,7 +65,7 @@ markdown_extensions: permalink: true - markdown.extensions.codehilite: guess_lang: false -- markdown_include.include: +- mdx_include: base_path: docs - admonition - codehilite @@ -73,7 +74,7 @@ markdown_extensions: custom_fences: - name: mermaid class: mermaid - format: !!python/name:pymdownx.superfences.fence_div_format '' + format: !!python/name:pymdownx.superfences.fence_code_format '' - pymdownx.tabbed extra: social: @@ -126,6 +127,5 @@ extra_css: - https://fastapi.tiangolo.com/css/termynal.css - https://fastapi.tiangolo.com/css/custom.css extra_javascript: -- https://unpkg.com/mermaid@8.4.6/dist/mermaid.min.js - https://fastapi.tiangolo.com/js/termynal.js - https://fastapi.tiangolo.com/js/custom.js diff --git a/docs/pl/mkdocs.yml b/docs/pl/mkdocs.yml index 9f12413d98fd4..379957783a4e2 100644 --- a/docs/pl/mkdocs.yml +++ b/docs/pl/mkdocs.yml @@ -20,6 +20,7 @@ theme: features: - search.suggest - search.highlight + - content.tabs.link icon: repo: fontawesome/brands/github-alt logo: https://fastapi.tiangolo.com/img/icon-white.svg @@ -58,7 +59,7 @@ markdown_extensions: permalink: true - markdown.extensions.codehilite: guess_lang: false -- markdown_include.include: +- mdx_include: base_path: docs - admonition - codehilite @@ -67,7 +68,7 @@ markdown_extensions: custom_fences: - name: mermaid class: mermaid - format: !!python/name:pymdownx.superfences.fence_div_format '' + format: !!python/name:pymdownx.superfences.fence_code_format '' - pymdownx.tabbed extra: social: @@ -120,6 +121,5 @@ extra_css: - https://fastapi.tiangolo.com/css/termynal.css - https://fastapi.tiangolo.com/css/custom.css extra_javascript: -- https://unpkg.com/mermaid@8.4.6/dist/mermaid.min.js - https://fastapi.tiangolo.com/js/termynal.js - https://fastapi.tiangolo.com/js/custom.js diff --git a/docs/pt/docs/advanced/index.md b/docs/pt/docs/advanced/index.md new file mode 100644 index 0000000000000..d1a57c6d17a4b --- /dev/null +++ b/docs/pt/docs/advanced/index.md @@ -0,0 +1,24 @@ +# Guia de Usuário Avançado - Introdução + +## Recursos Adicionais + +O [Tutorial - Guia de Usuário](../tutorial/){.internal-link target=_blank} deve ser o suficiente para dar a você um tour por todos os principais recursos do **FastAPI**. + +Na próxima seção você verá outras opções, configurações, e recursos adicionais. + +!!! tip "Dica" + As próximas seções **não são necessáriamente "avançadas"** + + E é possível que para seu caso de uso, a solução esteja em uma delas. + +## Leia o Tutorial primeiro + +Você ainda pode usar a maior parte dos recursos no **FastAPI** com o conhecimento do [Tutorial - Guia de Usuário](../tutorial/){.internal-link target=_blank}. + +E as próximas seções assumem que você já leu ele, e que você conhece suas ideias principais. + +## Curso TestDriven.io + +Se você gostaria de fazer um curso avançado-iniciante para complementar essa seção da documentação, você pode querer conferir: Test-Driven Development com FastAPI e Docker por **TestDriven.io**. + +Eles estão atualmente doando 10% de todos os lucros para o desenvolvimento do **FastAPI**. 🎉 😄 diff --git a/docs/pt/docs/async.md b/docs/pt/docs/async.md new file mode 100644 index 0000000000000..44f4b5148ecaa --- /dev/null +++ b/docs/pt/docs/async.md @@ -0,0 +1,394 @@ +# Concorrência e async / await + +Detalhes sobre a sintaxe `async def` para *funções de operação de rota* e alguns conceitos de código assíncrono, concorrência e paralelismo. + +## Com pressa? + +TL;DR: + +Se você estiver utilizando bibliotecas de terceiros que dizem para você chamar as funções com `await`, como: + +```Python +results = await some_library() +``` + +Então, declare sua *função de operação de rota* com `async def` como: + +```Python hl_lines="2" +@app.get('/') +async def read_results(): + results = await some_library() + return results +``` + +!!! note + Você só pode usar `await` dentro de funções criadas com `async def`. + +--- + +Se você está usando biblioteca de terceiros que se comunica com alguma coisa (um banco de dados, uma API, sistema de arquivos etc) e não tem suporte para utilizar `await` (esse é atualmente o caso para a maioria das bibliotecas de banco de dados), então declare suas *funções de operação de rota* normalmente, com apenas `def`, como: + +```Python hl_lines="2" +@app.get('/') +def results(): + results = some_library() + return results +``` + +--- + +Se sua aplicação (de alguma forma) não tem que se comunicar com nada mais e tem que esperar que o respondam, use `async def`. + +--- + +Se você simplesmente não sabe, use apenas `def`. + +--- + +**Note**: Você pode misturar `def` e `async def` nas suas *funções de operação de rota* tanto quanto necessário e definir cada função usando a melhor opção para você. FastAPI irá fazer a coisa certa com elas. + +De qualquer forma, em ambos os casos acima, FastAPI irá trabalhar assincronamente e ser extremamente rápido. + +Seguindo os passos acima, ele será capaz de fazer algumas otimizações de performance. + +## Detalhes Técnicos + +Versões modernas de Python tem suporte para **"código assíncrono"** usando algo chamado **"corrotinas"**, com sintaxe **`async` e `await`**. + +Vamos ver aquela frase por partes na seção abaixo: + +* **Código assíncrono** +* **`async` e `await`** +* **Corrotinas** + +## Código assíncrono + +Código assíncrono apenas significa que a linguagem 💬 tem um jeito de dizer para o computador / programa 🤖 que em certo ponto, ele 🤖 terá que esperar por *algo* para finalizar em outro lugar. Vamos dizer que esse *algo* seja chamado "arquivo lento" 📝. + +Então, durante esse tempo, o computador pode ir e fazer outro trabalho, enquanto o "arquivo lento" 📝 termine. + +Então o computador / programa 🤖 irá voltar toda hora que tiver uma chance porquê ele ainda está esperando o "arquivo lento", ou ele 🤖 nunca irá terminar todo o trabalho que tem até esse ponto. E ele 🤖 irá ver se alguma das tarefas que estava esperando já terminaram, fazendo o que quer que tinham que fazer. + +Depois, ele 🤖 pega a primeira tarefa para finalizar (vamos dizer, nosso "arquivo lento" 📝) e continua o que ele tem que fazer com isso. + +Esse "esperar por algo" normalmente se refere a operações I/O que são relativamente "lentas" (comparadas a velocidade do processador e da memória RAM), como esperar por: + +* dados do cliente para serem enviados através da rede +* dados enviados pelo seu programa para serem recebidos pelo clente através da rede +* conteúdo de um arquivo no disco pra ser lido pelo sistema e entregar ao seu programa +* conteúdo que seu programa deu ao sistema para ser escrito no disco +* uma operação remota API +* uma operação no banco de dados para finalizar +* uma solicitação no banco de dados esperando o retorno do resultado +* etc. + +Enquanto o tempo de execução é consumido mais pela espera das operações I/O, essas operações são chamadas de operações "limitadas por I/O". + +Isso é chamado de "assíncrono" porquê o computador / programa não tem que ser "sincronizado" com a tarefa lenta, esperando pelo exato momento que a tarefa finalize, enquanto não faz nada, para ser capaz de pegar o resultado da tarefa e dar continuidade ao trabalho. + +Ao invés disso, sendo um sistema "assíncrono", uma vez finalizada, a tarefa pode esperar um pouco (alguns microssegundos) para que o computador / programa finalize o que quer que esteja fazendo,e então volte para pegar o resultado e continue trabalhando com ele. + +Para "síncrono" (contrário de "assíncrono") também é utilizado o termo "sequencial", porquê o computador / programa segue todos os passos, na sequência, antes de trocar para uma tarefa diferente, mesmo se alguns passos envolvam esperar. + +### Concorrência e hambúrgueres + +Essa idéia de código **assíncrono** descrito acima é algo às vezes chamado de **"concorrência"**. E é diferente de **"paralelismo"**. + +**Concorrência** e **paralelismo** ambos são relacionados a "diferentes coisas acontecendo mais ou menos ao mesmo tempo". + +Mas os detalhes entre *concorrência* e *paralelismo* são bem diferentes. + +Para ver essa diferença, imagine a seguinte história sobre hambúrgueres: + +### Hambúrgueres concorrentes + +Você vai com seu _crush_ :heart_eyes: na lanchonete, fica na fila enquanto o caixa pega os pedidos das pessoas na sua frente. + +Então chega a sua vez, você pede dois saborosos hambúrgueres para você e seu _crush_ :heart_eyes:. + +Você paga. + +O caixa diz alguma coisa para o cara na cozinha para que ele tenha que preparar seus hambúrgueres (mesmo embora ele esteja preparando os lanches dos outros clientes). + +O caixa te entrega seu número de chamada. + +Enquanto você espera, você vai com seu _crush_ :heart_eyes: e pega uma mesa, senta e conversa com seu _crush_ :heart_eyes: por um bom tempo (como seus hambúrgueres são muito saborosos, leva um tempo para serem preparados). + +Enquanto você está sentado na mesa com seu _crush_ :heart_eyes:, esperando os hambúrgueres, você pode gastar o tempo admirando como lindo, maravilhoso e esperto é seu _crush_ :heart_eyes:. + +Enquanto espera e conversa com seu _crush_ :heart_eyes:, de tempos em tempos, você verifica o número de chamada exibido no balcão para ver se já é sua vez. + +Então a certo ponto, é finalmente sua vez. Você vai no balcão, pega seus hambúrgueres e volta para a mesa. + +Você e seu _crush_ :heart_eyes: comem os hambúrgueres e aproveitam o tempo. + +--- + +Imagine que você seja o computador / programa nessa história. + +Enquanto você está na fila, tranquilo, esperando por sua vez, não está fazendo nada "produtivo". Mas a fila é rápida porquê o caixa só está pegando os pedidos, então está tudo bem. + +Então, quando é sua vez, você faz o trabalho "produtivo" de verdade, você processa o menu, decide o que quer, pega a escolha de seu _crush_ :heart_eyes:, paga, verifica se entregou o valor correto em dinheiro ou cartão de crédito, verifica se foi cobrado corretamente, verifica se seu pedido está correto etc. + +Mas então, embora você ainda não tenha os hambúrgueres, seu trabalho no caixa está "pausado", porquê você tem que esperar seus hambúrgueres estarem prontos. + +Mas enquanto você se afasta do balcão e senta na mesa com o número da sua chamada, você pode trocar sua atenção para seu _crush_ :heart_eyes:, e "trabalhar" nisso. Então você está novamente fazendo algo muito "produtivo", como flertar com seu _crush_ :heart_eyes:. + +Então o caixa diz que "seus hambúrgueres estão prontos" colocando seu número no balcão, mas você não corre que nem um maluco imediatamente quando o número exibido é o seu. Você sabe que ninguém irá roubar seus hambúrgueres porquê você tem o número de chamada, e os outros tem os números deles. + +Então você espera que seu _crush_ :heart_eyes: termine a história que estava contando (terminar o trabalho atual / tarefa sendo processada), sorri gentilmente e diz que você está indo buscar os hambúrgueres. + +Então você vai no balcão, para a tarefa inicial que agora está finalizada, pega os hambúrgueres, e leva para a mesa. Isso finaliza esse passo / tarefa da interação com o balcão. Agora é criada uma nova tarefa, "comer hambúrgueres", mas a tarefa anterior, "pegar os hambúrgueres" já está finalizada. + +### Hambúrgueres paralelos + +Você vai com seu _crush_ :heart_eyes: em uma lanchonete paralela. + +Você fica na fila enquanto alguns (vamos dizer 8) caixas pegam os pedidos das pessoas na sua frente. + +Todo mundo antes de você está esperando pelos hambúrgueres estarem prontos antes de deixar o caixa porquê cada um dos 8 caixas vai e prepara o hambúrguer antes de pegar o próximo pedido. + +Então é finalmente sua vez, e pede 2 hambúrgueres muito saborosos para você e seu _crush_ :heart_eyes:. + +Você paga. + +O caixa vai para a cozinha. + +Você espera, na frente do balcão, para que ninguém pegue seus hambúrgueres antes de você, já que não tem números de chamadas. + +Enquanto você e seu _crush_ :heart_eyes: estão ocupados não permitindo que ninguém passe a frente e pegue seus hambúrgueres assim que estiverem prontos, você não pode dar atenção ao seu _crush_ :heart_eyes:. + +Isso é trabalho "síncrono", você está "sincronizado" com o caixa / cozinheiro. Você tem que esperar e estar lá no exato momento que o caixa / cozinheiro terminar os hambúrgueres e dá-los a você, ou então, outro alguém pode pegá-los. + +Então seu caixa / cozinheiro finalmente volta com seus hambúrgueres, depois de um longo tempo esperando por eles em frente ao balcão. + +Você pega seus hambúrgueres e vai para a mesa com seu _crush_ :heart_eyes:. + +Vocês comem os hambúrgueres, e o trabalho está terminado. + +Não houve muita conversa ou flerte já que a maior parte do tempo foi gasto esperando os lanches na frente do balcão. + +--- + +Nesse cenário dos hambúrgueres paralelos, você é um computador / programa com dois processadores (você e seu _crush_ :heart_eyes:), ambos esperando e dedicando a atenção de estar "esperando no balcão" por um bom tempo. + +A lanchonete paralela tem 8 processadores (caixas / cozinheiros). Enquanto a lanchonete dos hambúrgueres concorrentes tinham apenas 2 (um caixa e um cozinheiro). + +Ainda assim, a última experiência não foi a melhor. + +--- + +Essa poderia ser a história paralela equivalente aos hambúrgueres. + +Para um exemplo "mais real", imagine um banco. + +Até recentemente, a maioria dos bancos tinha muitos caixas e uma grande fila. + +Todos os caixas fazendo todo o trabalho, um cliente após o outro. + +E você tinha que esperar na fila por um longo tempo ou poderia perder a vez. + +Você provavelmente não gostaria de levar seu _crush_ :heart_eyes: com você para um rolezinho no banco. + +### Conclusão dos hambúrgueres + +Nesse cenário dos "hambúrgueres com seu _crush_ :heart_eyes:", como tem muita espera, faz mais sentido ter um sistema concorrente. + +Esse é o caso da maioria das aplicações web. + +Geralmente são muitos usuários, e seu servidor está esperando pelas suas conexões não tão boas para enviar as requisições. + +E então esperando novamente pelas respostas voltarem. + +Essa "espera" é medida em microssegundos, e ainda assim, somando tudo, é um monte de espera no final. + +Por isso que faz muito mais sentido utilizar código assíncrono para APIs web. + +A maioria dos frameworks Python existentes mais populares (incluindo Flask e Django) foram criados antes que os novos recursos assíncronos existissem em Python. Então, os meios que eles podem ser colocados em produção para suportar execução paralela mais a forma antiga de execução assíncrona não são tão poderosos quanto as novas capacidades. + +Mesmo embora a especificação principal para web assíncrono em Python (ASGI) foi desenvolvida no Django, para adicionar suporte para WebSockets. + +Esse tipo de assincronicidade é o que fez NodeJS popular (embora NodeJS não seja paralelo) e que essa seja a força do Go como uma linguagem de programa. + +E esse é o mesmo nível de performance que você tem com o **FastAPI**. + +E como você pode ter paralelismo e sincronicidade ao mesmo tempo, você tem uma maior performance do que a maioria dos frameworks NodeJS testados e lado a lado com Go, que é uma linguagem compilada próxima ao C (tudo graças ao Starlette). + +### Concorrência é melhor que paralelismo? + +Não! Essa não é a moral da história. + +Concorrência é diferente de paralelismo. E é melhor em cenários **específicos** que envolvam um monte de espera. Por isso, geralmente é muito melhor do que paralelismo para desenvolvimento de aplicações web. Mas não para tudo. + +Então, para equilibrar tudo, imagine a seguinte historinha: + +> Você tem que limpar uma grande casa suja. + +*Sim, essa é toda a história*. + +--- + +Não há espera em lugar algum, apenas um monte de trabalho para ser feito, em múltiplos cômodos da casa. + +Você poderia ter chamadas como no exemplo dos hambúrgueres, primeiro a sala de estar, então a cozinha, mas você não está esperando por nada, apenas limpar e limpar, as chamadas não afetariam em nada. + +Levaria o mesmo tempo para finalizar com ou sem chamadas (concorrência) e você teria feito o mesmo tanto de trabalho. + +Mas nesse caso, se você trouxesse os 8 ex-caixas / cozinheiros / agora-faxineiros, e cada um deles (mais você) pudessem dividir a casa para limpá-la, vocês fariam toda a limpeza em **paralelo**, com a ajuda extra, e terminariam muito mais cedo. + +Nesse cenário, cada um dos faxineiros (incluindo você) poderia ser um processador, fazendo a sua parte do trabalho. + +E a maior parte do tempo de execução é tomada por trabalho (ao invés de ficar esperando), e o trabalho em um computador é feito pela CPU, que podem gerar problemas que são chamados de "limite de CPU". + +--- + +Exemplos comuns de limite de CPU são coisas que exigem processamento matemático complexo. + +Por exemplo: + +* **Processamento de áudio** ou **imagem** +* **Visão do Computador**: uma imagem é composta por milhões de pixels, cada pixel tem 3 valores (cores, processamento que normalmente exige alguma computação em todos esses pixels ao mesmo tempo) + +* **Machine Learning**: Normalmente exige muita multiplicação de matrizes e vetores. Pense numa grande folha de papel com números e multiplicando todos eles juntos e ao mesmo tempo. + +* **Deep Learning**: Esse é um subcampo do Machine Learning, então o mesmo se aplica. A diferença é que não há apenas uma grande folha de papel com números para multiplicar, mas um grande conjunto de folhas de papel, e em muitos casos, você utiliza um processador especial para construir e/ou usar modelos. + +### Concorrência + Paralelismo: Web + Machine learning + +Com **FastAPI** você pode levar a vantagem da concorrência que é muito comum para desenvolvimento web (o mesmo atrativo de NodeJS). + +Mas você também pode explorar os benefícios do paralelismo e multiprocessamento (tendo múltiplos processadores rodando em paralelo) para trabalhos pesados que geram **limite de CPU** como aqueles em sistemas de Machine Learning. + +Isso, mais o simples fato que Python é a principal linguagem para **Data Science**, Machine Learning e especialmente Deep Learning, faz do FastAPI uma ótima escolha para APIs web e aplicações com Data Science / Machine Learning (entre muitas outras). + +Para ver como alcançar esse paralelismo em produção veja a seção sobre [Deployment](deployment.md){.internal-link target=_blank}. + +## `async` e `await` + +Versões modernas do Python tem um modo muito intuitivo para definir código assíncrono. Isso faz parecer normal o código "sequencial" e fazer o "esperar" para você nos momentos certos. + +Quando tem uma operação que exigirá espera antes de dar os resultados e tem suporte para esses recursos Python, você pode escrever assim: + +```Python +burgers = await get_burgers(2) +``` + +A chave aqui é o `await`. Ele diz ao Python que ele tem que esperar por `get_burgers(2)` para finalizar suas coisas antes de armazenar os resultados em `burgers`. Com isso, o Python saberá que ele pode ir e fazer outras coisas nesse meio tempo (como receber outra requisição). + +Para o `await` funcionar, tem que estar dentro de uma função que suporte essa assincronicidade. Para fazer isso, apenas declare a função com `async def`: + +```Python hl_lines="1" +async def get_burgers(number: int): + # Fazer alguma coisa assíncrona para criar os hambúrgueres + return burgers +``` + +...ao invés de `def`: + +```Python hl_lines="2" +# Isso não é assíncrono +def get_sequential_burgers(number: int): + # Faz alguma coisa sequencial para criar os hambúrgueres + return burgers +``` + +Com `async def`, o Python sabe que, dentro dessa função, tem que estar ciente das expressões `await`, e que isso pode "pausar" a execução dessa função, e poderá fazer outra coisa antes de voltar. + +Quando você quiser chamar uma função `async def`, você tem que "esperar". Então, isso não funcionará: + +```Python +# Isso não irá funcionar, porquê get_burgers foi definido com: async def +burgers = get_burgers(2) +``` + +--- + +Então, se você está usando uma biblioteca que diz que você pode chamá-la com `await`, você precisa criar as *funções de operação de rota* com `async def`, como em: + +```Python hl_lines="2 3" +@app.get('/burgers') +async def read_burgers(): + burgers = await get_burgers(2) + return burgers +``` + +### Mais detalhes técnicos + +Você deve ter observado que `await` pode ser usado somente dentro de funções definidas com `async def`. + +Mas ao mesmo tempo, funções definidas com `async def` tem que ser aguardadas. Então, funções com `async def` pdem ser chamadas somente dentro de funções definidas com `async def` também. + +Então, sobre o ovo e a galinha, como você chama a primeira função async? + +Se você estivar trabalhando com **FastAPI** não terá que se preocupar com isso, porquê essa "primeira" função será a sua *função de operação de rota*, e o FastAPI saberá como fazer a coisa certa. + +Mas se você quiser usar `async` / `await` sem FastAPI, verifique a documentação oficial Python. + +### Outras formas de código assíncrono + +Esse estilo de usar `async` e `await` é relativamente novo na linguagem. + +Mas ele faz o trabalho com código assíncrono muito mais fácil. + +Essa mesma sintaxe (ou quase a mesma) foi também incluída recentemente em versões modernas do JavaScript (no navegador e NodeJS). + +Mas antes disso, controlar código assíncrono era bem mais complexo e difícil. + +Nas versões anteriores do Python, você poderia utilizar threads ou Gevent. Mas o código é um pouco mais complexo de entender, debugar, e pensar sobre. + +Nas versões anteriores do NodeJS / Navegador JavaScript, você poderia utilizar "callbacks". O que leva ao inferno do callback. + +## Corrotinas + +**Corrotina** é apenas um jeito bonitinho para a coisa que é retornada de uma função `async def`. O Python sabe que é uma função que pode começar e terminar em algum ponto, mas que pode ser pausada internamente também, sempre que tiver um `await` dentro dela. + +Mas toda essa funcionalidade de código assíncrono com `async` e `await` é muitas vezes resumida como "corrotina". É comparável ao principal recurso chave do Go, a "Gorotina". + +## Conclusão + +Vamos ver a mesma frase com o conteúdo cima: + +> Versões modernas do Python tem suporte para **"código assíncrono"** usando algo chamado **"corrotinas"**, com sintaxe **`async` e `await`**. + +Isso pode fazer mais sentido agora. + +Tudo isso é o que deixa o FastAPI poderoso (através do Starlette) e que o faz ter uma performance impressionante. + +## Detalhes muito técnicos + +!!! warning + Você pode provavelmente pular isso. + + Esses são detalhes muito técnicos de como **FastAPI** funciona por baixo do capô. + + Se você tem algum conhecimento técnico (corrotinas, threads, blocking etc) e está curioso sobre como o FastAPI controla o `async def` vs normal `def`, vá em frente. + +### Funções de operação de rota + +Quando você declara uma *função de operação de rota* com `def` normal ao invés de `async def`, ela é rodada em uma threadpool externa que então é aguardada, ao invés de ser chamada diretamente (ela poderia bloquear o servidor). + +Se você está chegando de outro framework assíncrono que não faz o trabalho descrito acima e você está acostumado a definir triviais *funções de operação de rota* com simples `def` para ter um mínimo ganho de performance (cerca de 100 nanosegundos), por favor observe que no **FastAPI** o efeito pode ser bem o oposto. Nesses casos, é melhor usar `async def` a menos que suas *funções de operação de rota* utilizem código que performem bloqueamento IO. + +Ainda, em ambas as situações, as chances são que o **FastAPI** será [ainda mais rápido](/#performance){.internal-link target=_blank} do que (ou ao menos comparável a) seus frameworks antecessores. + +### Dependências + +O mesmo se aplica para as dependências. Se uma dependência tem as funções com padrão `def` ao invés de `async def`, ela é rodada no threadpool externo. + +### Sub-dependências + +Você pode ter múltiplas dependências e sub-dependências exigindo uma a outra (como parâmetros de definições de funções), algumas delas podem ser criadas com `async def` e algumas com `def` normal. Isso ainda poderia funcionar, e aquelas criadas com `def` podem ser chamadas em uma thread externa ao invés de serem "aguardadas". + +### Outras funções de utilidade + +Qualquer outra função de utilidade que você chame diretamente pode ser criada com `def` normal ou `async def` e o FastAPI não irá afetar o modo como você a chama. + +Isso está em contraste às funções que o FastAPI chama para você: *funções de operação de rota* e dependências. + +Se sua função de utilidade é uma função normal com `def`, ela será chamada diretamente (como você a escreve no código), não em uma threadpool, se a função é criada com `async def` então você deve esperar por essa função quando você chamá-la no seu código. + +--- + +Novamente, esses são detalhes muito técnicos que provavelmente possam ser úteis caso você esteja procurando por eles. + +Caso contrário, você deve ficar bem com as dicas da seção acima: Com pressa?. diff --git a/docs/pt/docs/fastapi-people.md b/docs/pt/docs/fastapi-people.md new file mode 100644 index 0000000000000..964cac68f1cd5 --- /dev/null +++ b/docs/pt/docs/fastapi-people.md @@ -0,0 +1,178 @@ +# Pessoas do FastAPI + +FastAPI possue uma comunidade incrível que recebe pessoas de todos os níveis. + +## Criador - Mantenedor + +Ei! 👋 + +Este sou eu: + +{% if people %} +
+{% for user in people.maintainers %} + +
@{{ user.login }}
Respostas: {{ user.answers }}
Pull Requests: {{ user.prs }}
+{% endfor %} + +
+{% endif %} + +Eu sou o criador e mantenedor do **FastAPI**. Você pode ler mais sobre isso em [Help FastAPI - Get Help - Connect with the author](help-fastapi.md#connect-with-the-author){.internal-link target=_blank}. + +...Mas aqui eu quero mostrar a você a comunidade. + +--- + +**FastAPI** recebe muito suporte da comunidade. E quero destacar suas contribuições. + +Estas são as pessoas que: + +* [Help others with issues (questions) in GitHub](help-fastapi.md#help-others-with-issues-in-github){.internal-link target=_blank}. +* [Create Pull Requests](help-fastapi.md#create-a-pull-request){.internal-link target=_blank}. +* Revisar Pull Requests, [especially important for translations](contributing.md#translations){.internal-link target=_blank}. + +Uma salva de palmas para eles. 👏 🙇 + +## Usuários mais ativos do ultimo mês + +Estes são os usuários que estão [helping others the most with issues (questions) in GitHub](help-fastapi.md#help-others-with-issues-in-github){.internal-link target=_blank} durante o ultimo mês. ☕ + +{% if people %} +
+{% for user in people.last_month_active %} + +
@{{ user.login }}
Issues respondidas: {{ user.count }}
+{% endfor %} + +
+{% endif %} + +## Especialistas + +Aqui está os **Especialistas do FastAPI**. 🤓 + + +Estes são os usuários que [helped others the most with issues (questions) in GitHub](help-fastapi.md#help-others-with-issues-in-github){.internal-link target=_blank} em *todo o tempo*. + +Eles provaram ser especialistas ajudando muitos outros. ✨ + +{% if people %} +
+{% for user in people.experts %} + +
@{{ user.login }}
Issues respondidas: {{ user.count }}
+{% endfor %} + +
+{% endif %} + +## Top Contribuidores + +Aqui está os **Top Contribuidores**. 👷 + +Esses usuários têm [created the most Pull Requests](help-fastapi.md#create-a-pull-request){.internal-link target=_blank} que tem sido *mergeado*. + +Eles contribuíram com o código-fonte, documentação, traduções, etc. 📦 + +{% if people %} +
+{% for user in people.top_contributors %} + +
@{{ user.login }}
Pull Requests: {{ user.count }}
+{% endfor %} + +
+{% endif %} + +Existem muitos outros contribuidores (mais de uma centena), você pode ver todos eles em Página de Contribuidores do FastAPI no GitHub. 👷 + +## Top Revisores + +Esses usuários são os **Top Revisores**. 🕵️ + +### Revisões para Traduções + +Eu só falo algumas línguas (e não muito bem 😅). Então, os revisores são aqueles que têm o [**poder de aprovar traduções**](contributing.md#translations){.internal-link target=_blank} da documentação. Sem eles, não haveria documentação em vários outros idiomas. + +--- + +Os **Top Revisores** 🕵️ revisaram a maior parte de Pull Requests de outros, garantindo a qualidade do código, documentação, e especialmente, as **traduções**. + +{% if people %} +
+{% for user in people.top_reviewers %} + +
@{{ user.login }}
Revisões: {{ user.count }}
+{% endfor %} + +
+{% endif %} + +## Patrocinadores + +Esses são os **Patrocinadores**. 😎 + +Eles estão apoiando meu trabalho **FastAPI** (e outros), principalmente através de GitHub Sponsors. + +{% if sponsors %} +{% if sponsors.gold %} + +### Patrocinadores Ouro + +{% for sponsor in sponsors.gold -%} + +{% endfor %} +{% endif %} + +{% if sponsors.silver %} + +### Patrocinadores Prata + +{% for sponsor in sponsors.silver -%} + +{% endfor %} +{% endif %} + +{% if sponsors.bronze %} + +### Patrocinadores Bronze + +{% for sponsor in sponsors.bronze -%} + +{% endfor %} +{% endif %} + +### Patrocinadores Individuais + +{% if github_sponsors %} +{% for group in github_sponsors.sponsors %} + +
+ +{% for user in group %} +{% if user.login not in sponsors_badge.logins %} + + + +{% endif %} +{% endfor %} + +
+ +{% endfor %} +{% endif %} + +{% endif %} + +## Sobre os dados - detalhes técnicos + +A principal intenção desta página é destacar o esforço da comunidade para ajudar os outros. + +Especialmente incluindo esforços que normalmente são menos visíveis, e em muitos casos mais árduo, como ajudar os outros com issues e revisando Pull Requests com traduções. + +Os dados são calculados todo mês, você pode ler o código fonte aqui. + +Aqui também estou destacando contribuições de patrocinadores. + +Eu também me reservo o direito de atualizar o algoritmo, seções, limites, etc (só para prevenir 🤷). diff --git a/docs/pt/docs/features.md b/docs/pt/docs/features.md index 51a57d96efba1..20014fe2df2b9 100644 --- a/docs/pt/docs/features.md +++ b/docs/pt/docs/features.md @@ -32,7 +32,6 @@ Se você precisa refrescar a memória rapidamente sobre como usar tipos do Pytho Você escreve Python padrão com tipos: ```Python -from typing import List, Dict from datetime import date from pydantic import BaseModel diff --git a/docs/pt/docs/index.md b/docs/pt/docs/index.md index 63f1f88fe9d03..848fff08aa2e8 100644 --- a/docs/pt/docs/index.md +++ b/docs/pt/docs/index.md @@ -39,7 +39,7 @@ Os recursos chave são: * estimativas baseadas em testes realizados com equipe interna de desenvolvimento, construindo aplicações em produção. -## Sponsors +## Patrocinadores Ouro @@ -54,7 +54,7 @@ Os recursos chave são: -Other sponsors +Outros patrocinadores ## Opiniões diff --git a/docs/pt/mkdocs.yml b/docs/pt/mkdocs.yml index fd2ac2190b0d0..ec0684d5450ee 100644 --- a/docs/pt/mkdocs.yml +++ b/docs/pt/mkdocs.yml @@ -20,6 +20,7 @@ theme: features: - search.suggest - search.highlight + - content.tabs.link icon: repo: fontawesome/brands/github-alt logo: https://fastapi.tiangolo.com/img/icon-white.svg @@ -54,12 +55,15 @@ nav: - uk: /uk/ - zh: /zh/ - features.md +- fastapi-people.md - Tutorial - Guia de Usuário: - tutorial/index.md - tutorial/first-steps.md - tutorial/body-fields.md - Segurança: - tutorial/security/index.md + - Guia de Usuário Avançado: + - advanced/index.md - Implantação: - deployment/index.md - deployment/versions.md @@ -72,7 +76,7 @@ markdown_extensions: permalink: true - markdown.extensions.codehilite: guess_lang: false -- markdown_include.include: +- mdx_include: base_path: docs - admonition - codehilite @@ -81,7 +85,7 @@ markdown_extensions: custom_fences: - name: mermaid class: mermaid - format: !!python/name:pymdownx.superfences.fence_div_format '' + format: !!python/name:pymdownx.superfences.fence_code_format '' - pymdownx.tabbed extra: social: @@ -134,6 +138,6 @@ extra_css: - https://fastapi.tiangolo.com/css/termynal.css - https://fastapi.tiangolo.com/css/custom.css extra_javascript: -- https://unpkg.com/mermaid@8.4.6/dist/mermaid.min.js - https://fastapi.tiangolo.com/js/termynal.js - https://fastapi.tiangolo.com/js/custom.js + diff --git a/docs/ru/mkdocs.yml b/docs/ru/mkdocs.yml index c737bb2b5ef0b..359cce8eb2961 100644 --- a/docs/ru/mkdocs.yml +++ b/docs/ru/mkdocs.yml @@ -20,6 +20,7 @@ theme: features: - search.suggest - search.highlight + - content.tabs.link icon: repo: fontawesome/brands/github-alt logo: https://fastapi.tiangolo.com/img/icon-white.svg @@ -58,7 +59,7 @@ markdown_extensions: permalink: true - markdown.extensions.codehilite: guess_lang: false -- markdown_include.include: +- mdx_include: base_path: docs - admonition - codehilite @@ -67,7 +68,7 @@ markdown_extensions: custom_fences: - name: mermaid class: mermaid - format: !!python/name:pymdownx.superfences.fence_div_format '' + format: !!python/name:pymdownx.superfences.fence_code_format '' - pymdownx.tabbed extra: social: @@ -120,6 +121,5 @@ extra_css: - https://fastapi.tiangolo.com/css/termynal.css - https://fastapi.tiangolo.com/css/custom.css extra_javascript: -- https://unpkg.com/mermaid@8.4.6/dist/mermaid.min.js - https://fastapi.tiangolo.com/js/termynal.js - https://fastapi.tiangolo.com/js/custom.js diff --git a/docs/sq/mkdocs.yml b/docs/sq/mkdocs.yml index c5ce8003f1297..53019fea5d372 100644 --- a/docs/sq/mkdocs.yml +++ b/docs/sq/mkdocs.yml @@ -20,6 +20,7 @@ theme: features: - search.suggest - search.highlight + - content.tabs.link icon: repo: fontawesome/brands/github-alt logo: https://fastapi.tiangolo.com/img/icon-white.svg @@ -58,7 +59,7 @@ markdown_extensions: permalink: true - markdown.extensions.codehilite: guess_lang: false -- markdown_include.include: +- mdx_include: base_path: docs - admonition - codehilite @@ -67,7 +68,7 @@ markdown_extensions: custom_fences: - name: mermaid class: mermaid - format: !!python/name:pymdownx.superfences.fence_div_format '' + format: !!python/name:pymdownx.superfences.fence_code_format '' - pymdownx.tabbed extra: social: @@ -120,6 +121,5 @@ extra_css: - https://fastapi.tiangolo.com/css/termynal.css - https://fastapi.tiangolo.com/css/custom.css extra_javascript: -- https://unpkg.com/mermaid@8.4.6/dist/mermaid.min.js - https://fastapi.tiangolo.com/js/termynal.js - https://fastapi.tiangolo.com/js/custom.js diff --git a/docs/tr/mkdocs.yml b/docs/tr/mkdocs.yml index d94b527f2f977..5e429fdefbeac 100644 --- a/docs/tr/mkdocs.yml +++ b/docs/tr/mkdocs.yml @@ -20,6 +20,7 @@ theme: features: - search.suggest - search.highlight + - content.tabs.link icon: repo: fontawesome/brands/github-alt logo: https://fastapi.tiangolo.com/img/icon-white.svg @@ -58,7 +59,7 @@ markdown_extensions: permalink: true - markdown.extensions.codehilite: guess_lang: false -- markdown_include.include: +- mdx_include: base_path: docs - admonition - codehilite @@ -67,7 +68,7 @@ markdown_extensions: custom_fences: - name: mermaid class: mermaid - format: !!python/name:pymdownx.superfences.fence_div_format '' + format: !!python/name:pymdownx.superfences.fence_code_format '' - pymdownx.tabbed extra: social: @@ -120,6 +121,5 @@ extra_css: - https://fastapi.tiangolo.com/css/termynal.css - https://fastapi.tiangolo.com/css/custom.css extra_javascript: -- https://unpkg.com/mermaid@8.4.6/dist/mermaid.min.js - https://fastapi.tiangolo.com/js/termynal.js - https://fastapi.tiangolo.com/js/custom.js diff --git a/docs/uk/mkdocs.yml b/docs/uk/mkdocs.yml index 4b8983c5796dc..4d8636bd68eba 100644 --- a/docs/uk/mkdocs.yml +++ b/docs/uk/mkdocs.yml @@ -20,6 +20,7 @@ theme: features: - search.suggest - search.highlight + - content.tabs.link icon: repo: fontawesome/brands/github-alt logo: https://fastapi.tiangolo.com/img/icon-white.svg @@ -58,7 +59,7 @@ markdown_extensions: permalink: true - markdown.extensions.codehilite: guess_lang: false -- markdown_include.include: +- mdx_include: base_path: docs - admonition - codehilite @@ -67,7 +68,7 @@ markdown_extensions: custom_fences: - name: mermaid class: mermaid - format: !!python/name:pymdownx.superfences.fence_div_format '' + format: !!python/name:pymdownx.superfences.fence_code_format '' - pymdownx.tabbed extra: social: @@ -120,6 +121,5 @@ extra_css: - https://fastapi.tiangolo.com/css/termynal.css - https://fastapi.tiangolo.com/css/custom.css extra_javascript: -- https://unpkg.com/mermaid@8.4.6/dist/mermaid.min.js - https://fastapi.tiangolo.com/js/termynal.js - https://fastapi.tiangolo.com/js/custom.js diff --git a/docs/zh/docs/fastapi-people.md b/docs/zh/docs/fastapi-people.md index ea239e6cabf3a..75651592db2b2 100644 --- a/docs/zh/docs/fastapi-people.md +++ b/docs/zh/docs/fastapi-people.md @@ -114,25 +114,28 @@ FastAPI 有一个非常棒的社区,它欢迎来自各个领域和背景的朋 他们主要通过GitHub Sponsors支持我在 **FastAPI** (和其他项目)的工作。 +{% if sponsors.gold %} + ### 金牌赞助商 -{% if sponsors %} {% for sponsor in sponsors.gold -%} {% endfor %} {% endif %} +{% if sponsors.silver %} + ### 银牌赞助商 -{% if sponsors %} {% for sponsor in sponsors.silver -%} {% endfor %} {% endif %} +{% if sponsors.bronze %} + ### 铜牌赞助商 -{% if sponsors %} {% for sponsor in sponsors.bronze -%} {% endfor %} @@ -140,28 +143,22 @@ FastAPI 有一个非常棒的社区,它欢迎来自各个领域和背景的朋 ### 个人赞助 -{% if people %} -{% if people.sponsors_50 %} +{% if github_sponsors %} +{% for group in github_sponsors.sponsors %}
-{% for user in people.sponsors_50 %} - -{% endfor %} +{% for user in group %} +{% if user.login not in sponsors_badge.logins %} -
+ {% endif %} -{% endif %} - -{% if people %} -
-{% for user in people.sponsors %} - - {% endfor %}
+ +{% endfor %} {% endif %} ## 关于数据 - 技术细节 diff --git a/docs/zh/docs/features.md b/docs/zh/docs/features.md index d41509435811e..4752947a3990a 100644 --- a/docs/zh/docs/features.md +++ b/docs/zh/docs/features.md @@ -33,7 +33,6 @@ 编写带有类型标注的标准 Python: ```Python -from typing import List, Dict from datetime import date from pydantic import BaseModel diff --git a/docs/zh/docs/tutorial/dependencies/dependencies-in-path-operation-decorators.md b/docs/zh/docs/tutorial/dependencies/dependencies-in-path-operation-decorators.md new file mode 100644 index 0000000000000..e36e33c4fb0f5 --- /dev/null +++ b/docs/zh/docs/tutorial/dependencies/dependencies-in-path-operation-decorators.md @@ -0,0 +1,73 @@ +# 路径操作装饰器依赖项 + +有时,我们并不需要在*路径操作函数*中使用依赖项的返回值。 + +或者说,有些依赖项不返回值。 + +但仍要执行或解析该依赖项。 + +对于这种情况,不必在声明*路径操作函数*的参数时使用 `Depends`,而是可以在*路径操作装饰器*中添加一个由 `dependencies` 组成的 `list`。 + +## 在*路径操作装饰器*中添加 `dependencies` 参数 + +*路径操作装饰器*支持可选参数 ~ `dependencies`。 + +该参数的值是由 `Depends()` 组成的 `list`: + +```Python hl_lines="17" +{!../../../docs_src/dependencies/tutorial006.py!} +``` + +路径操作装饰器依赖项(以下简称为**“路径装饰器依赖项”**)的执行或解析方式和普通依赖项一样,但就算这些依赖项会返回值,它们的值也不会传递给*路径操作函数*。 + +!!! tip "提示" + + 有些编辑器会检查代码中没使用过的函数参数,并显示错误提示。 + + 在*路径操作装饰器*中使用 `dependencies` 参数,可以确保在执行依赖项的同时,避免编辑器显示错误提示。 + + 使用路径装饰器依赖项还可以避免开发新人误会代码中包含无用的未使用参数。 + +!!! info "说明" + + 本例中,使用的是自定义响应头 `X-Key` 和 `X-Token`。 + + 但实际开发中,尤其是在实现安全措施时,最好使用 FastAPI 内置的[安全工具](../security/index.md){.internal-link target=_blank}(详见下一章)。 + +## 依赖项错误和返回值 + +路径装饰器依赖项也可以使用普通的依赖项*函数*。 + +### 依赖项的需求项 + +路径装饰器依赖项可以声明请求的需求项(比如响应头)或其他子依赖项: + +```Python hl_lines="6 11" +{!../../../docs_src/dependencies/tutorial006.py!} +``` + +### 触发异常 + +路径装饰器依赖项与正常的依赖项一样,可以 `raise` 异常: + +```Python hl_lines="8 13" +{!../../../docs_src/dependencies/tutorial006.py!} +``` + +### 返回值 + +无论路径装饰器依赖项是否返回值,路径操作都不会使用这些值。 + +因此,可以复用在其他位置使用过的、(能返回值的)普通依赖项,即使没有使用这个值,也会执行该依赖项: + +```Python hl_lines="9 14" +{!../../../docs_src/dependencies/tutorial006.py!} +``` + +## 为一组路径操作定义依赖项 + +稍后,[大型应用 - 多文件](../../tutorial/bigger-applications.md){.internal-link target=\_blank}一章中会介绍如何使用多个文件创建大型应用程序,在这一章中,您将了解到如何为一组*路径操作*声明单个 `dependencies` 参数。 + +## 全局依赖项 + +接下来,我们将学习如何为 `FastAPI` 应用程序添加全局依赖项,创建应用于每个*路径操作*的依赖项。 diff --git a/docs/zh/docs/tutorial/dependencies/sub-dependencies.md b/docs/zh/docs/tutorial/dependencies/sub-dependencies.md new file mode 100644 index 0000000000000..76d3e9f44469b --- /dev/null +++ b/docs/zh/docs/tutorial/dependencies/sub-dependencies.md @@ -0,0 +1,88 @@ +# 子依赖项 + +FastAPI 支持创建含**子依赖项**的依赖项。 + +并且,可以按需声明任意**深度**的子依赖项嵌套层级。 + +**FastAPI** 负责处理解析不同深度的子依赖项。 + +### 第一层依赖项 + +下列代码创建了第一层依赖项: + +```Python hl_lines="8-9" +{!../../../docs_src/dependencies/tutorial005.py!} +``` + +这段代码声明了类型为 `str` 的可选查询参数 `q`,然后返回这个查询参数。 + +这个函数很简单(不过也没什么用),但却有助于让我们专注于了解子依赖项的工作方式。 + +### 第二层依赖项 + +接下来,创建另一个依赖项函数,并同时用该依赖项自身再声明一个依赖项(所以这也是一个「依赖项」): + +```Python hl_lines="13" +{!../../../docs_src/dependencies/tutorial005.py!} +``` + +这里重点说明一下声明的参数: + +* 尽管该函数自身是依赖项,但还声明了另一个依赖项(它「依赖」于其他对象) + * 该函数依赖 `query_extractor`, 并把 `query_extractor` 的返回值赋给参数 `q` +* 同时,该函数还声明了类型是 `str` 的可选 cookie(`last_query`) + * 用户未提供查询参数 `q` 时,则使用上次使用后保存在 cookie 中的查询 + +### 使用依赖项 + +接下来,就可以使用依赖项: + +```Python hl_lines="21" +{!../../../docs_src/dependencies/tutorial005.py!} +``` + +!!! info "信息" + + 注意,这里在*路径操作函数*中只声明了一个依赖项,即 `query_or_cookie_extractor` 。 + + 但 **FastAPI** 必须先处理 `query_extractor`,以便在调用 `query_or_cookie_extractor` 时使用 `query_extractor` 返回的结果。 + +```mermaid +graph TB + +query_extractor(["query_extractor"]) +query_or_cookie_extractor(["query_or_cookie_extractor"]) + +read_query["/items/"] + +query_extractor --> query_or_cookie_extractor --> read_query +``` + +## 多次使用同一个依赖项 + +如果在同一个*路径操作* 多次声明了同一个依赖项,例如,多个依赖项共用一个子依赖项,**FastAPI** 在处理同一请求时,只调用一次该子依赖项。 + +FastAPI 不会为同一个请求多次调用同一个依赖项,而是把依赖项的返回值进行「缓存」,并把它传递给同一请求中所有需要使用该返回值的「依赖项」。 + +在高级使用场景中,如果不想使用「缓存」值,而是为需要在同一请求的每一步操作(多次)中都实际调用依赖项,可以把 `Depends` 的参数 `use_cache` 的值设置为 `False` : + +```Python hl_lines="1" +async def needy_dependency(fresh_value: str = Depends(get_value, use_cache=False)): + return {"fresh_value": fresh_value} +``` + +## 小结 + +千万别被本章里这些花里胡哨的词藻吓倒了,其实**依赖注入**系统非常简单。 + +依赖注入无非是与*路径操作函数*一样的函数罢了。 + +但它依然非常强大,能够声明任意嵌套深度的「图」或树状的依赖结构。 + +!!! tip "提示" + + 这些简单的例子现在看上去虽然没有什么实用价值, + + 但在**安全**一章中,您会了解到这些例子的用途, + + 以及这些例子所能节省的代码量。 \ No newline at end of file diff --git a/docs/zh/mkdocs.yml b/docs/zh/mkdocs.yml index 6a1ddb156dcc7..d73afaa934a65 100644 --- a/docs/zh/mkdocs.yml +++ b/docs/zh/mkdocs.yml @@ -20,6 +20,7 @@ theme: features: - search.suggest - search.highlight + - content.tabs.link icon: repo: fontawesome/brands/github-alt logo: https://fastapi.tiangolo.com/img/icon-white.svg @@ -82,6 +83,8 @@ nav: - tutorial/body-updates.md - 依赖项: - tutorial/dependencies/index.md + - tutorial/dependencies/sub-dependencies.md + - tutorial/dependencies/dependencies-in-path-operation-decorators.md - tutorial/dependencies/global-dependencies.md - 安全性: - tutorial/security/index.md @@ -107,7 +110,7 @@ markdown_extensions: permalink: true - markdown.extensions.codehilite: guess_lang: false -- markdown_include.include: +- mdx_include: base_path: docs - admonition - codehilite @@ -116,7 +119,7 @@ markdown_extensions: custom_fences: - name: mermaid class: mermaid - format: !!python/name:pymdownx.superfences.fence_div_format '' + format: !!python/name:pymdownx.superfences.fence_code_format '' - pymdownx.tabbed extra: social: @@ -169,6 +172,5 @@ extra_css: - https://fastapi.tiangolo.com/css/termynal.css - https://fastapi.tiangolo.com/css/custom.css extra_javascript: -- https://unpkg.com/mermaid@8.4.6/dist/mermaid.min.js - https://fastapi.tiangolo.com/js/termynal.js - https://fastapi.tiangolo.com/js/custom.js diff --git a/fastapi/__init__.py b/fastapi/__init__.py index 60f90b80d5058..3163626698bd2 100644 --- a/fastapi/__init__.py +++ b/fastapi/__init__.py @@ -1,6 +1,6 @@ """FastAPI framework, high performance, easy to learn, fast to code, ready for production""" -__version__ = "0.68.0" +__version__ = "0.68.1" from starlette import status as status diff --git a/fastapi/routing.py b/fastapi/routing.py index 0ad082341f16d..63ad7296409f0 100644 --- a/fastapi/routing.py +++ b/fastapi/routing.py @@ -65,6 +65,13 @@ def _prepare_response_content( exclude_none: bool = False, ) -> Any: if isinstance(res, BaseModel): + read_with_orm_mode = getattr(res.__config__, "read_with_orm_mode", None) + if read_with_orm_mode: + # Let from_orm extract the data from this model instead of converting + # it now to a dict. + # Otherwise there's no way to extract lazy data that requires attribute + # access instead of dict iteration, e.g. lazy relationships. + return res return res.dict( by_alias=True, exclude_unset=exclude_unset, diff --git a/pyproject.toml b/pyproject.toml index 45880cc313785..eddbb92aabc58 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -28,6 +28,7 @@ classifiers = [ "Programming Language :: Python :: 3.6", "Programming Language :: Python :: 3.7", "Programming Language :: Python :: 3.8", + "Programming Language :: Python :: 3.9", "Topic :: Internet :: WWW/HTTP :: HTTP Servers", "Topic :: Internet :: WWW/HTTP", ] @@ -67,7 +68,7 @@ test = [ doc = [ "mkdocs >=1.1.2,<2.0.0", "mkdocs-material >=7.1.9,<8.0.0", - "markdown-include >=0.6.0,<0.7.0", + "mdx-include >=1.4.1,<2.0.0", "mkdocs-markdownextradata-plugin >=0.1.7,<0.2.0", "typer-cli >=0.0.12,<0.0.13", "pyyaml >=5.3.1,<6.0.0" diff --git a/tests/test_read_with_orm_mode.py b/tests/test_read_with_orm_mode.py new file mode 100644 index 0000000000000..360ad250350ec --- /dev/null +++ b/tests/test_read_with_orm_mode.py @@ -0,0 +1,53 @@ +from typing import Any + +from fastapi import FastAPI +from fastapi.testclient import TestClient +from pydantic import BaseModel + + +class PersonBase(BaseModel): + name: str + lastname: str + + +class Person(PersonBase): + @property + def full_name(self) -> str: + return f"{self.name} {self.lastname}" + + class Config: + orm_mode = True + read_with_orm_mode = True + + +class PersonCreate(PersonBase): + pass + + +class PersonRead(PersonBase): + full_name: str + + class Config: + orm_mode = True + + +app = FastAPI() + + +@app.post("/people/", response_model=PersonRead) +def create_person(person: PersonCreate) -> Any: + db_person = Person.from_orm(person) + return db_person + + +client = TestClient(app) + + +def test_read_with_orm_mode() -> None: + person_data = {"name": "Dive", "lastname": "Wilson"} + response = client.post("/people/", json=person_data) + data = response.json() + assert response.status_code == 200, response.text + assert data["name"] == person_data["name"] + assert data["lastname"] == person_data["lastname"] + assert data["full_name"] == person_data["name"] + " " + person_data["lastname"] diff --git a/tests/test_tutorial/test_sql_databases_peewee/test_sql_databases_peewee.py b/tests/test_tutorial/test_sql_databases_peewee/test_sql_databases_peewee.py index 08cfa5ca9278c..2ebc31b953197 100644 --- a/tests/test_tutorial/test_sql_databases_peewee/test_sql_databases_peewee.py +++ b/tests/test_tutorial/test_sql_databases_peewee/test_sql_databases_peewee.py @@ -5,7 +5,7 @@ import pytest from fastapi.testclient import TestClient -from ...utils import skip_py36 +from ...utils import needs_py37 openapi_schema = { "openapi": "3.0.2", @@ -340,14 +340,14 @@ def client(): test_db.unlink() -@skip_py36 +@needs_py37 def test_openapi_schema(client): response = client.get("/openapi.json") assert response.status_code == 200, response.text assert response.json() == openapi_schema -@skip_py36 +@needs_py37 def test_create_user(client): test_user = {"email": "johndoe@example.com", "password": "secret"} response = client.post("/users/", json=test_user) @@ -359,7 +359,7 @@ def test_create_user(client): assert response.status_code == 400, response.text -@skip_py36 +@needs_py37 def test_get_user(client): response = client.get("/users/1") assert response.status_code == 200, response.text @@ -368,13 +368,13 @@ def test_get_user(client): assert "id" in data -@skip_py36 +@needs_py37 def test_inexistent_user(client): response = client.get("/users/999") assert response.status_code == 404, response.text -@skip_py36 +@needs_py37 def test_get_users(client): response = client.get("/users/") assert response.status_code == 200, response.text @@ -386,7 +386,7 @@ def test_get_users(client): time.sleep = MagicMock() -@skip_py36 +@needs_py37 def test_get_slowusers(client): response = client.get("/slowusers/") assert response.status_code == 200, response.text @@ -395,7 +395,7 @@ def test_get_slowusers(client): assert "id" in data[0] -@skip_py36 +@needs_py37 def test_create_item(client): item = {"title": "Foo", "description": "Something that fights"} response = client.post("/users/1/items/", json=item) @@ -419,7 +419,7 @@ def test_create_item(client): assert item_to_check["description"] == item["description"] -@skip_py36 +@needs_py37 def test_read_items(client): response = client.get("/items/") assert response.status_code == 200, response.text diff --git a/tests/test_typing_python39.py b/tests/test_typing_python39.py new file mode 100644 index 0000000000000..b1ea635e7724f --- /dev/null +++ b/tests/test_typing_python39.py @@ -0,0 +1,24 @@ +from fastapi import FastAPI +from fastapi.testclient import TestClient + +from .utils import needs_py39 + + +@needs_py39 +def test_typing(): + types = { + list[int]: [1, 2, 3], + dict[str, list[int]]: {"a": [1, 2, 3], "b": [4, 5, 6]}, + set[int]: [1, 2, 3], # `set` is converted to `list` + tuple[int, ...]: [1, 2, 3], # `tuple` is converted to `list` + } + for test_type, expect in types.items(): + app = FastAPI() + + @app.post("/", response_model=test_type) + def post_endpoint(input: test_type): + return input + + res = TestClient(app).post("/", json=expect) + assert res.status_code == 200, res.json() + assert res.json() == expect diff --git a/tests/test_union_inherited_body.py b/tests/test_union_inherited_body.py index a1a3f0ed5b5b7..e3d0acc99d49c 100644 --- a/tests/test_union_inherited_body.py +++ b/tests/test_union_inherited_body.py @@ -4,7 +4,7 @@ from fastapi.testclient import TestClient from pydantic import BaseModel -from .utils import skip_py36 +from .utils import needs_py37 # In Python 3.6: # u = Union[ExtendedItem, Item] == __main__.Item @@ -118,21 +118,21 @@ def save_union_different_body(item: Union[ExtendedItem, Item]): } -@skip_py36 +@needs_py37 def test_inherited_item_openapi_schema(): response = client.get("/openapi.json") assert response.status_code == 200, response.text assert response.json() == inherited_item_openapi_schema -@skip_py36 +@needs_py37 def test_post_extended_item(): response = client.post("/items/", json={"name": "Foo", "age": 5}) assert response.status_code == 200, response.text assert response.json() == {"item": {"name": "Foo", "age": 5}} -@skip_py36 +@needs_py37 def test_post_item(): response = client.post("/items/", json={"name": "Foo"}) assert response.status_code == 200, response.text diff --git a/tests/utils.py b/tests/utils.py index 2c6c2067d8152..92d1c48e873e2 100644 --- a/tests/utils.py +++ b/tests/utils.py @@ -2,4 +2,5 @@ import pytest -skip_py36 = pytest.mark.skipif(sys.version_info < (3, 7), reason="skip python3.6") +needs_py37 = pytest.mark.skipif(sys.version_info < (3, 7), reason="requires python3.7+") +needs_py39 = pytest.mark.skipif(sys.version_info < (3, 9), reason="requires python3.9+")