From e4453a850d794fd211eb3741c85cf3bec320c871 Mon Sep 17 00:00:00 2001 From: Koyo Miyazaki <82708304+KoyoMiyazaki@users.noreply.github.com> Date: Sat, 26 Jul 2025 19:48:11 +0900 Subject: [PATCH 01/16] =?UTF-8?q?=F0=9F=93=9D=20Fix=20highlight=20line=20i?= =?UTF-8?q?n=20`docs/ja/docs/tutorial/body.md`=20(#13927)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit fix highlight line in docs/ja/docs/tutorial/body.md --- docs/ja/docs/tutorial/body.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/ja/docs/tutorial/body.md b/docs/ja/docs/tutorial/body.md index 8376959d5..1298eec7e 100644 --- a/docs/ja/docs/tutorial/body.md +++ b/docs/ja/docs/tutorial/body.md @@ -22,7 +22,7 @@ GET リクエストでボディを送信することは、仕様では未定義 ます初めに、 `pydantic` から `BaseModel` をインポートする必要があります: -{* ../../docs_src/body/tutorial001.py hl[2] *} +{* ../../docs_src/body/tutorial001.py hl[4] *} ## データモデルの作成 @@ -30,7 +30,7 @@ GET リクエストでボディを送信することは、仕様では未定義 すべての属性にpython標準の型を使用します: -{* ../../docs_src/body/tutorial001.py hl[5:9] *} +{* ../../docs_src/body/tutorial001.py hl[7:11] *} クエリパラメータの宣言と同様に、モデル属性がデフォルト値をもつとき、必須な属性ではなくなります。それ以外は必須になります。オプショナルな属性にしたい場合は `None` を使用してください。 @@ -58,7 +58,7 @@ GET リクエストでボディを送信することは、仕様では未定義 *パスオペレーション* に加えるために、パスパラメータやクエリパラメータと同じ様に宣言します: -{* ../../docs_src/body/tutorial001.py hl[16] *} +{* ../../docs_src/body/tutorial001.py hl[18] *} ...そして、作成したモデル `Item` で型を宣言します。 @@ -125,7 +125,7 @@ GET リクエストでボディを送信することは、仕様では未定義 関数内部で、モデルの全ての属性に直接アクセスできます: -{* ../../docs_src/body/tutorial002.py hl[19] *} +{* ../../docs_src/body/tutorial002.py hl[21] *} ## リクエストボディ + パスパラメータ @@ -133,7 +133,7 @@ GET リクエストでボディを送信することは、仕様では未定義 **FastAPI** はパスパラメータである関数パラメータは**パスから受け取り**、Pydanticモデルによって宣言された関数パラメータは**リクエストボディから受け取る**ということを認識します。 -{* ../../docs_src/body/tutorial003.py hl[15:16] *} +{* ../../docs_src/body/tutorial003.py hl[17:18] *} ## リクエストボディ + パスパラメータ + クエリパラメータ @@ -141,7 +141,7 @@ GET リクエストでボディを送信することは、仕様では未定義 **FastAPI** はそれぞれを認識し、適切な場所からデータを取得します。 -{* ../../docs_src/body/tutorial004.py hl[16] *} +{* ../../docs_src/body/tutorial004.py hl[18] *} 関数パラメータは以下の様に認識されます: From 106a4338db8d546f3a6bf817e237331297e7f797 Mon Sep 17 00:00:00 2001 From: github-actions Date: Sat, 26 Jul 2025 10:48:36 +0000 Subject: [PATCH 02/16] =?UTF-8?q?=F0=9F=93=9D=20Update=20release=20notes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [skip ci] --- docs/en/docs/release-notes.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/en/docs/release-notes.md b/docs/en/docs/release-notes.md index e198e9437..d5186aad3 100644 --- a/docs/en/docs/release-notes.md +++ b/docs/en/docs/release-notes.md @@ -9,6 +9,7 @@ hide: ### Translations +* 📝 Fix highlight line in `docs/ja/docs/tutorial/body.md`. PR [#13927](https://github.com/fastapi/fastapi/pull/13927) by [@KoyoMiyazaki](https://github.com/KoyoMiyazaki). * 🌐 Add Persian translation for `docs/fa/docs/environment-variables.md`. PR [#13923](https://github.com/fastapi/fastapi/pull/13923) by [@Mohammad222PR](https://github.com/Mohammad222PR). * 🌐 Add Persian translation for `docs/fa/docs/python-types.md`. PR [#13524](https://github.com/fastapi/fastapi/pull/13524) by [@Mohammad222PR](https://github.com/Mohammad222PR). * 🌐 Update Portuguese Translation for `docs/pt/docs/project-generation.md`. PR [#13875](https://github.com/fastapi/fastapi/pull/13875) by [@EdmilsonRodrigues](https://github.com/EdmilsonRodrigues). From 54c7c34b25e9c71d218f4a01095520bc2b582b24 Mon Sep 17 00:00:00 2001 From: Chih-Hsuan Yen <1937689+yan12125@users.noreply.github.com> Date: Sat, 26 Jul 2025 18:49:23 +0800 Subject: [PATCH 03/16] =?UTF-8?q?=E2=AC=86=20Update=20httpx=20requirement?= =?UTF-8?q?=20to=20>=3D0.23.0,<0.29.0=20(#13114)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- requirements-docs-tests.txt | 2 +- requirements-github-actions.txt | 2 +- .../test_custom_request_and_route/test_tutorial002.py | 8 +++++--- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/requirements-docs-tests.txt b/requirements-docs-tests.txt index 71f4a7ab9..e7684a2e3 100644 --- a/requirements-docs-tests.txt +++ b/requirements-docs-tests.txt @@ -1,4 +1,4 @@ # For mkdocstrings and tests -httpx >=0.23.0,<0.28.0 +httpx >=0.23.0,<0.29.0 # For linting and generating docs versions ruff ==0.11.2 diff --git a/requirements-github-actions.txt b/requirements-github-actions.txt index 920aefea6..f807d06a8 100644 --- a/requirements-github-actions.txt +++ b/requirements-github-actions.txt @@ -1,6 +1,6 @@ PyGithub>=2.3.0,<3.0.0 pydantic>=2.5.3,<3.0.0 pydantic-settings>=2.1.0,<3.0.0 -httpx>=0.27.0,<0.28.0 +httpx>=0.27.0,<0.29.0 pyyaml >=5.3.1,<7.0.0 smokeshow diff --git a/tests/test_tutorial/test_custom_request_and_route/test_tutorial002.py b/tests/test_tutorial/test_custom_request_and_route/test_tutorial002.py index 6f7355aaa..647f1c5dd 100644 --- a/tests/test_tutorial/test_custom_request_and_route/test_tutorial002.py +++ b/tests/test_tutorial/test_custom_request_and_route/test_tutorial002.py @@ -1,4 +1,4 @@ -from dirty_equals import IsDict +from dirty_equals import IsDict, IsOneOf from fastapi.testclient import TestClient from docs_src.custom_request_and_route.tutorial002 import app @@ -24,14 +24,16 @@ def test_exception_handler_body_access(): "input": {"numbers": [1, 2, 3]}, } ], - "body": '{"numbers": [1, 2, 3]}', + # httpx 0.28.0 switches to compact JSON https://github.com/encode/httpx/issues/3363 + "body": IsOneOf('{"numbers": [1, 2, 3]}', '{"numbers":[1,2,3]}'), } } ) | IsDict( # TODO: remove when deprecating Pydantic v1 { "detail": { - "body": '{"numbers": [1, 2, 3]}', + # httpx 0.28.0 switches to compact JSON https://github.com/encode/httpx/issues/3363 + "body": IsOneOf('{"numbers": [1, 2, 3]}', '{"numbers":[1,2,3]}'), "errors": [ { "loc": ["body"], From bfbed308699d6f8204411e5bbaaa084b6e779c30 Mon Sep 17 00:00:00 2001 From: github-actions Date: Sat, 26 Jul 2025 10:49:51 +0000 Subject: [PATCH 04/16] =?UTF-8?q?=F0=9F=93=9D=20Update=20release=20notes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [skip ci] --- docs/en/docs/release-notes.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/en/docs/release-notes.md b/docs/en/docs/release-notes.md index d5186aad3..c75a8b574 100644 --- a/docs/en/docs/release-notes.md +++ b/docs/en/docs/release-notes.md @@ -18,6 +18,7 @@ hide: ### Internal +* ⬆ Update httpx requirement to >=0.23.0,<0.29.0. PR [#13114](https://github.com/fastapi/fastapi/pull/13114) by [@yan12125](https://github.com/yan12125). * 🔧 Update sponsors: Add Mobb. PR [#13916](https://github.com/fastapi/fastapi/pull/13916) by [@tiangolo](https://github.com/tiangolo). * 👥 Update FastAPI People - Experts. PR [#13889](https://github.com/fastapi/fastapi/pull/13889) by [@tiangolo](https://github.com/tiangolo). * 🔨 Update FastAPI People sleep interval, use external settings. PR [#13888](https://github.com/fastapi/fastapi/pull/13888) by [@tiangolo](https://github.com/tiangolo). From fcdd5031e1b150952bd70ea8d1ea805d5f1014c6 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sat, 26 Jul 2025 12:51:31 +0200 Subject: [PATCH 05/16] =?UTF-8?q?=E2=AC=86=20[pre-commit.ci]=20pre-commit?= =?UTF-8?q?=20autoupdate=20(#13894)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/astral-sh/ruff-pre-commit: v0.12.2 → v0.12.4](https://github.com/astral-sh/ruff-pre-commit/compare/v0.12.2...v0.12.4) Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 680cafce9..e461b0133 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -14,7 +14,7 @@ repos: - id: end-of-file-fixer - id: trailing-whitespace - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.12.2 + rev: v0.12.4 hooks: - id: ruff args: From 8bfc783194bdfc2142ac716ae208c735e1862c81 Mon Sep 17 00:00:00 2001 From: github-actions Date: Sat, 26 Jul 2025 10:51:53 +0000 Subject: [PATCH 06/16] =?UTF-8?q?=F0=9F=93=9D=20Update=20release=20notes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [skip ci] --- docs/en/docs/release-notes.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/en/docs/release-notes.md b/docs/en/docs/release-notes.md index c75a8b574..d5730e247 100644 --- a/docs/en/docs/release-notes.md +++ b/docs/en/docs/release-notes.md @@ -18,6 +18,7 @@ hide: ### Internal +* ⬆ [pre-commit.ci] pre-commit autoupdate. PR [#13894](https://github.com/fastapi/fastapi/pull/13894) by [@pre-commit-ci[bot]](https://github.com/apps/pre-commit-ci). * ⬆ Update httpx requirement to >=0.23.0,<0.29.0. PR [#13114](https://github.com/fastapi/fastapi/pull/13114) by [@yan12125](https://github.com/yan12125). * 🔧 Update sponsors: Add Mobb. PR [#13916](https://github.com/fastapi/fastapi/pull/13916) by [@tiangolo](https://github.com/tiangolo). * 👥 Update FastAPI People - Experts. PR [#13889](https://github.com/fastapi/fastapi/pull/13889) by [@tiangolo](https://github.com/tiangolo). From 2013336d65e1b7ac25aa3a4b130d41b6b2ac31f4 Mon Sep 17 00:00:00 2001 From: Edmilson Monteiro Rodrigues Neto Date: Sat, 26 Jul 2025 12:16:39 +0100 Subject: [PATCH 07/16] =?UTF-8?q?=20=F0=9F=8C=90=20Update=20Portuguese=20T?= =?UTF-8?q?ranslation=20for=20`docs/pt/docs/async.md`=20(#13863)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/pt/docs/async.md | 207 ++++++++++++++++++++++-------------------- 1 file changed, 108 insertions(+), 99 deletions(-) diff --git a/docs/pt/docs/async.md b/docs/pt/docs/async.md index 0d6bdbf0e..4425eba77 100644 --- a/docs/pt/docs/async.md +++ b/docs/pt/docs/async.md @@ -40,7 +40,7 @@ def 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 sua aplicação (de alguma forma) não tem que se comunicar com nada mais e esperar que o respondam, use `async def`. --- @@ -52,7 +52,7 @@ Se você simplesmente não sabe, use apenas `def`. 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. +Mas, seguindo os passos acima, ele será capaz de fazer algumas otimizações de performance. ## Detalhes Técnicos @@ -66,36 +66,36 @@ Vamos ver aquela frase por partes na seção abaixo: ## 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" 📝. +Código assíncrono apenas significa que a linguagem 💬 tem um jeito de dizer para o computador / programa 🤖 que em certo ponto do código, ele 🤖 terá que esperar *algo* 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, durante esse tempo, o computador pode ir e fazer outro trabalho, enquanto o "arquivo lento" 📝 termina. -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. +Então o computador / programa 🤖 irá voltar sempre que tiver uma chance, seja porque ele está esperando novamente, ou quando ele 🤖 terminar todo o trabalho que tem até esse ponto. E ele 🤖 irá ver se alguma das tarefas que estava esperando já terminaram de fazer 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. +Depois, ele 🤖 pega a primeira tarefa para finalizar (vamos dizer, nosso "arquivo lento" 📝) e continua o que tem que fazer com ela. -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: +Esse "esperar por algo" normalmente se refere a operações I/O que são relativamente "lentas" (comparadas à 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 +* dados enviados pelo seu programa serem recebidos pelo clente através da rede +* conteúdo de um arquivo no disco ser lido pelo sistema e entregue 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 +* uma operação em uma API remota +* uma operação no banco de dados finalizar +* uma solicitação no banco de dados retornar o 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". +Quanto o tempo de execução é consumido majoritariamente pela espera de operações I/O, essas operações são chamadas 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. +Isso é chamado de "assíncrono" porque o computador / programa não tem que ser "sincronizado" com a tarefa lenta, esperando pelo momento exato em que a tarefa finaliza, 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. +Ao invés disso, sendo um sistema "assíncrono", uma vez finalizada, a tarefa pode esperar na fila 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. +Para "síncrono" (contrário de "assíncrono") também é utilizado o termo "sequencial", porquê o computador / programa segue todos os passos, em 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"**. +Essa idéia de código **assíncrono** descrita acima é às vezes chamado de **"concorrência"**. Isso é diferente de **"paralelismo"**. **Concorrência** e **paralelismo** ambos são relacionados a "diferentes coisas acontecendo mais ou menos ao mesmo tempo". @@ -105,117 +105,115 @@ 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. +Você vai com seu _crush_ na lanchonete, e 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:. +Então chega a sua vez, você pede dois saborosos hambúrgueres para você e seu _crush_. 🍔🍔 -Você paga. +O caixa diz alguma coisa para o cozinheiro na cozinha para que eles saivam que têm que preparar seus hambúrgueres (mesmo que ele esteja atualmente preparando os lanches dos outros clientes). -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). +Você paga. 💸 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ê espera, você vai com seu _crush_ e pega uma mesa, senta e conversa com seu _crush_ por um bom tempo (já que seus hambúrgueres são muito saborosos, e 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:. +Já que você está sentado na mesa com seu _crush_, esperando os hambúrgueres, você pode passar esse tempo admirando o quão lindo, maravilhoso e esperto é seu _crush_ ✨😍✨. -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. +Enquanto espera e conversa com seu _crush_, de tempos em tempos, você verifica o número da 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. +Então em algum momento, é finalmente sua vez. Você vai ao 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. +Você e seu _crush_ comem os hambúrgueres e aproveitam o tempo. ✨ --- -Imagine que você seja o computador / programa nessa história. +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. +Enquanto você está na fila, você está somente ocioso 😴, esperando por sua vez, sem fazer nada muito "produtivo". Mas a fila é rápida porque o caixa só está pegando os pedidos (não os preparando), 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. +Então, quando é sua vez, você faz trabalho realmente "produtivo", você processa o menu, decide o que quer, pega a escolha de seu _crush_, paga, verifica se entregou o cartão ou a cédula correta, 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 então, embora você ainda não tenha os hambúrgueres, seu trabalho no caixa está "pausado" ⏸, porque você tem que esperar 🕙 seus hambúrgueres ficarem 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:. +Contudo, à medida que você se afasta do balcão e senta na mesa, com um número para sua chamada, você pode trocar 🔀 sua atenção para seu _crush_, e "trabalhar" ⏯ 🤓 nisso. Então você está novamente fazendo algo muito "produtivo", como flertar com seu _crush_ 😍. -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 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 porque você tem o seu número da chamada, e os outros têm os 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ê espera seu _crush_ terminar 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. +Então você vai ao balcão 🔀, para a tarefa inicial que agora está finalizada⏯, pega os hambúrgueres, agradece, e leva-os para a mesa. Isso finaliza esse passo / tarefa da interação com o balcão ⏹. Isso, por sua vez, cria uma nova tarefa, a de "comer hambúrgueres" 🔀 ⏯, mas a tarefa anterior de "pegar os hambúrgueres" já está finalizada ⏹. ### Hambúrgueres paralelos -Você vai com seu _crush_ :heart_eyes: em uma lanchonete paralela. +Agora vamos imaginar que esses não são "Hambúrgueres Concorrentes", e sim "Hambúrgueres Paralelos" -Você fica na fila enquanto alguns (vamos dizer 8) caixas pegam os pedidos das pessoas na sua frente. +Você vai com seu _crush_ na lanchonete paralela. -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. +Você fica na fila enquanto vários (vamos dizer 8) caixas que também são cozinheiros pegam os pedidos das pessoas na sua frente. -Então é finalmente sua vez, e pede 2 hambúrgueres muito saborosos para você e seu _crush_ :heart_eyes:. +Todo mundo na sua frente está esperando seus hambúrgueres ficarem prontos antes de deixar o caixa porque cada um dos 8 caixas vai e prepara o hambúrguer logo após receber o pedido, antes de pegar o próximo pedido. -Você paga. +Então é finalmente sua vez, você pede 2 hambúrgueres muito saborosos para você e seu _crush_. + +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. +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:. +Como você e seu _crush_ estão ocupados não permitindo que ninguém passe na frente e pegue seus hambúrgueres assim que estiverem prontos, você não pode dar atenção ao seu _crush_. 😞 -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. +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 os der 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. +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ê pega seus hambúrgueres e vai para a mesa com seu _crush_. -Vocês comem os hambúrgueres, e o trabalho está terminado. +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. +Não houve muita conversa ou flerte já que a maior parte do tempo foi gasto esperando 🕙 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. +Nesse cenário dos hambúrgueres paralelos, você é um computador / programa com dois processadores (você e seu _crush_), ambos esperando 🕙 e dedicando sua atenção ⏯ "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). +A lanchonete paralela tem 8 processadores (caixas / cozinheiros), enquanto a lanchonete dos hambúrgueres concorrentes tinha apenas 2 (um caixa e um cozinheiro). -Ainda assim, a última experiência não foi a melhor. +Ainda assim, a experiência final não foi a melhor. 😞 --- -Essa poderia ser a história paralela equivalente aos hambúrgueres. +Essa seria o equivalente paralelo à histório dos hambúrgueres. 🍔 Para um exemplo "mais real", imagine um banco. -Até recentemente, a maioria dos bancos tinha muitos caixas e uma grande fila. +Até recentemente, a maioria dos bancos tinham muitos caixas 👨‍💼👨‍💼👨‍💼👨‍💼 e uma grande fila 🕙🕙🕙🕙🕙🕙🕙🕙. -Todos os caixas fazendo todo o trabalho, um cliente após o outro. +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. +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. +Você provavelmente não gostaria de levar seu _crush_ 😍 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. +Nesse cenário dos "hambúrgueres com seu _crush_", 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. +Muitos, muitos usuários, mas seu servidor está esperando 🕙 pela sua conexão não tão boa enviar suas requisições. -Por isso que faz muito mais sentido utilizar código assíncrono para APIs web. +E então esperando 🕙 novamente as respostas voltarem. -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. +Essa "espera" 🕙 é medida em microssegundos, mas ainda assim, somando tudo, é um monte de espera no final. -Mesmo embora a especificação principal para web assíncrono em Python (ASGI) foi desenvolvida no Django, para adicionar suporte para WebSockets. +Por isso que faz bastante sentido utilizar código assíncrono ⏸🔀⏯ para APIs web. -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. +Esse tipo de assincronicidade é o que fez NodeJS popular (embora NodeJS não seja paralelo) e essa é a força do Go como uma linguagem de programação. 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). +E como você pode ter paralelismo e assincronicidade 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, mais próxima ao C (tudo graças ao Starlette). ### Concorrência é melhor que paralelismo? @@ -225,64 +223,64 @@ Concorrência é diferente de paralelismo. E é melhor em cenários **específic Então, para equilibrar tudo, imagine a seguinte historinha: -> Você tem que limpar uma grande casa suja. +> Você tem que limpar uma casa grande e 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. +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. +Você poderia ter turnos como no exemplo dos hambúrgueres, primeiro a sala de estar, então a cozinha, mas como você não está esperando por nada, apenas limpando e limpando, 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. +Levaria o mesmo tempo para finalizar com ou sem turnos (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". +E a maior parte do tempo de execução é tomada por trabalho real (ao invés de ficar esperando), e o trabalho em um computador é feito pela CPU. Eles chamam esses problemas de "limitados por CPU". --- -Exemplos comuns de limite de CPU são coisas que exigem processamento matemático complexo. +Exemplos comuns de operações limitadas por 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) +* **Visão Computacional**: uma imagem é composta por milhões de pixels, cada pixel tem 3 valores / cores, processar isso 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. +* **Machine Learning**: Normalmente exige muita multiplicação de matrizes e vetores. Pense numa grande planilha com números e em multiplicar 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. +* **Deep Learning**: Esse é um subcampo do Machine Learning, então, o mesmo se aplica. A diferença é que não há apenas uma grande planilha com números para multiplicar, mas um grande conjunto delas, e em muitos casos, você utiliza um processador especial para construir e/ou usar esses 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. +Mas você também pode explorar os benefícios do paralelismo e multiprocessamento (tendo múltiplos processadores rodando em paralelo) para trabalhos **limitados por 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). +Isso, somado ao 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/index.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. +Versões modernas do Python têm um modo muito intuitivo para definir código assíncrono. Isso faz parecer do mesmo jeito do código normal "sequencial" e fazer a "espera" 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: +Quando tem uma operação que exigirá espera antes de dar os resultados e tem suporte para esses novos recursos do 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). +A chave aqui é o `await`. Ele diz ao Python que ele tem que esperar por `get_burgers(2)` 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 + # Faz alguma coisa assíncrona para criar os hambúrgueres return burgers ``` @@ -295,9 +293,9 @@ def get_sequential_burgers(number: int): 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. +Com `async def`, o Python sabe que, dentro dessa função, ele deve estar ciente das expressões `await`, e que isso poderá "pausar" ⏸ a execução dessa função, e ir 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á: +Quando você quiser chamar uma função `async def`, você tem que "esperar" ela. Então, isso não funcionará: ```Python # Isso não irá funcionar, porquê get_burgers foi definido com: async def @@ -319,13 +317,24 @@ async def read_burgers(): 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. +Mas ao mesmo tempo, funções definidas com `async def` têm 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. +Mas se você quiser usar `async` / `await` sem FastAPI, você também pode fazê-lo. + +### Escreva seu próprio código assíncrono + +Starlette (e **FastAPI**) são baseados no AnyIO, o que o torna compatível com ambos o asyncio da biblioteca padrão do Python, e o Trio. + +Em particular, você pode usar diretamente o AnyIO para seus casos de uso avançados de concorrência que requerem padrões mais avançados no seu próprio código. + +E até se você não estiver utilizando FastAPI, você também pode escrever suas próprias aplicações assíncronas com o AnyIO por ser altamente compatível e ganhar seus benefícios (e.g. *concorrência estruturada*). + +Eu criei outra biblioteca em cima do AnyIO, como uma fina camada acima, para melhorar um pouco as anotações de tipo e obter melhor **autocompletar**, **erros de linha**, etc. Ela também possui uma introdução amigável e um tutorial para ajudar você a **entender** e escrever **seu próprio código async**: Asyncer. Seria particularmente útil se você precisar **combinar código async com código regular** (bloqueador/síncrono). + ### Outras formas de código assíncrono @@ -337,25 +346,25 @@ Essa mesma sintaxe (ou quase a mesma) foi também incluída recentemente em vers 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 Python, você poderia utilizar threads ou Gevent. Mas o código é bem 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. +Nas versões anteriores do NodeJS / Navegador JavaScript, você utilizaria "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. +**Corrotina** é apenas um jeito bonitinho para a coisa que é retornada de uma função `async def`. O Python sabe que é algo como uma função, que pode começar e que vai 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". +Mas toda essa funcionalidade de código assíncrono com `async` e `await` é muitas vezes resumida como usando "corrotinas". É comparável ao principal recurso chave do Go, a "Gorrotina". ## Conclusão -Vamos ver a mesma frase com o conteúdo cima: +Vamos ver a mesma frase de cima: -> Versões modernas do Python tem suporte para **"código assíncrono"** usando algo chamado **"corrotinas"**, com sintaxe **`async` e `await`**. +> Versões modernas do Python têm suporte para **"código assíncrono"** usando algo chamado **"corrotinas"**, com sintaxe **`async` e `await`**. -Isso pode fazer mais sentido agora. +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. +Tudo isso é o que empodera o FastAPI (através do Starlette) e que o faz ter uma performance tão impressionante. ## Detalhes muito técnicos @@ -365,25 +374,25 @@ 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. +Se você tem certo 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). +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 (já que ela bloquearia 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. +Se você está chegando de outro framework assíncrono que não funciona como descrito acima e você está acostumado a definir *funções de operação de rota* triviais somente de computação 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 performe bloqueamento IO. -Ainda, em ambas as situações, as chances são que o **FastAPI** será [ainda mais rápido](index.md#performance){.internal-link target=_blank} do que (ou ao menos comparável a) seus frameworks antecessores. +Ainda, em ambas as situações, as chances são que o **FastAPI** [ainda será mais rápido](index.md#performance){.internal-link target=_blank} do que (ou ao menos comparável a) seu framework anterior. ### 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. +O mesmo se aplica para as [dependências](tutorial/dependencies/index.md){.internal-link target=_blank}. 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". +Você pode ter múltiplas dependências e [sub-dependências](tutorial/dependencies/sub-dependencies.md){.internal-link target=_blank} requisitando uma à 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 funcionaria, e aquelas criadas com `def` normal seriam chamadas em uma thread externa (do threadpool) ao invés de serem "aguardadas". ### Outras funções de utilidade @@ -395,6 +404,6 @@ Se sua função de utilidade é uma função normal com `def`, ela será chamada --- -Novamente, esses são detalhes muito técnicos que provavelmente possam ser úteis caso você esteja procurando por eles. +Novamente, esses são detalhes muito técnicos que provavelmente seriam úteis caso você esteja procurando por eles. Caso contrário, você deve ficar bem com as dicas da seção acima: Com pressa?. From 89b4d1f076be7f7b30447344ef33cc895dade875 Mon Sep 17 00:00:00 2001 From: github-actions Date: Sat, 26 Jul 2025 11:17:02 +0000 Subject: [PATCH 08/16] =?UTF-8?q?=F0=9F=93=9D=20Update=20release=20notes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [skip ci] --- docs/en/docs/release-notes.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/en/docs/release-notes.md b/docs/en/docs/release-notes.md index d5730e247..a6a68584a 100644 --- a/docs/en/docs/release-notes.md +++ b/docs/en/docs/release-notes.md @@ -9,6 +9,7 @@ hide: ### Translations +* 🌐 Update Portuguese Translation for `docs/pt/docs/async.md`. PR [#13863](https://github.com/fastapi/fastapi/pull/13863) by [@EdmilsonRodrigues](https://github.com/EdmilsonRodrigues). * 📝 Fix highlight line in `docs/ja/docs/tutorial/body.md`. PR [#13927](https://github.com/fastapi/fastapi/pull/13927) by [@KoyoMiyazaki](https://github.com/KoyoMiyazaki). * 🌐 Add Persian translation for `docs/fa/docs/environment-variables.md`. PR [#13923](https://github.com/fastapi/fastapi/pull/13923) by [@Mohammad222PR](https://github.com/Mohammad222PR). * 🌐 Add Persian translation for `docs/fa/docs/python-types.md`. PR [#13524](https://github.com/fastapi/fastapi/pull/13524) by [@Mohammad222PR](https://github.com/Mohammad222PR). From 3d60b5e23e878ca28e823fa65b06d67de95a6588 Mon Sep 17 00:00:00 2001 From: Alejandra <90076947+alejsdev@users.noreply.github.com> Date: Sat, 26 Jul 2025 13:35:42 +0200 Subject: [PATCH 09/16] =?UTF-8?q?=F0=9F=93=9D=20Add=20discussion=20templat?= =?UTF-8?q?e=20for=20new=20language=20translation=20requests=20(#13535)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Sebastián Ramírez --- .github/DISCUSSION_TEMPLATE/translations.yml | 45 ++++++++++++++++++++ docs/en/docs/contributing.md | 37 +++++++++++----- 2 files changed, 72 insertions(+), 10 deletions(-) create mode 100644 .github/DISCUSSION_TEMPLATE/translations.yml diff --git a/.github/DISCUSSION_TEMPLATE/translations.yml b/.github/DISCUSSION_TEMPLATE/translations.yml new file mode 100644 index 000000000..16e304d99 --- /dev/null +++ b/.github/DISCUSSION_TEMPLATE/translations.yml @@ -0,0 +1,45 @@ +labels: [lang-all] +body: + - type: markdown + attributes: + value: | + Thanks for your interest in helping translate the FastAPI docs! 🌍 + + Please follow these instructions carefully to propose a new language translation. 🙏 + + This structured process helps ensure translations can be properly maintained long-term. + - type: checkboxes + id: checks + attributes: + label: Initial Checks + description: Please confirm and check all the following options. + options: + - label: I checked that this language is not already being translated in FastAPI docs. + required: true + - label: I searched existing discussions to ensure no one else proposed this language. + required: true + - label: I am a native speaker of the language I want to help translate. + required: true + - type: input + id: language + attributes: + label: Target Language + description: What language do you want to translate the FastAPI docs into? + placeholder: e.g. Latin + validations: + required: true + - type: textarea + id: additional_info + attributes: + label: Additional Information + description: Any other relevant information about your translation proposal + - type: markdown + attributes: + value: | + Translations are automatized with AI and then reviewed by native speakers. 🤖 🙋 + + This allows us to keep them consistent and up-to-date. + + If there are several native speakers commenting on this discussion and + committing to help review new translations, the FastAPI team will review it + and potentially make it an official translation. 😎 diff --git a/docs/en/docs/contributing.md b/docs/en/docs/contributing.md index 91c39c927..2583fd1fb 100644 --- a/docs/en/docs/contributing.md +++ b/docs/en/docs/contributing.md @@ -315,30 +315,47 @@ Now you can translate it all and see how it looks as you save the file. Some of these files are updated very frequently and a translation would always be behind, or they include the main content from English source files, etc. +#### Request a New Language + +Let's say that you want to request translations for a language that is not yet translated, not even some pages. For example, Latin. + +If there is no discussion for that language, you can start by requesting the new language. For that, you can follow these steps: + +* Create a new discussion following the template. +* Get a few native speakers to comment on the discussion and commit to help review translations for that language. + +Once there are several people in the discussion, the FastAPI team can evaluate it and can make it an official translation. + +Then the docs will be automatically translated using AI, and the team of native speakers can review the translation, and help tweak the AI prompts. + +Once there's a new translation, for example if docs are updated or there's a new section, there will be a comment in the same discussion with the link to the new translation to review. + #### New Language -Let's say that you want to add translations for a language that is not yet translated, not even some pages. +/// note -Let's say you want to add translations for Creole, and it's not yet there in the docs. +These steps will be performed by the FastAPI team. + +/// -Checking the link from above, the code for "Creole" is `ht`. +Checking the link from above (List of ISO 639-1 codes), you can see that the 2-letter code for Latin is `la`. -The next step is to run the script to generate a new translation directory: +Now you can create a new directory for the new language, running the following script:
```console // Use the command new-lang, pass the language code as a CLI argument -$ python ./scripts/docs.py new-lang ht +$ python ./scripts/docs.py new-lang la -Successfully initialized: docs/ht +Successfully initialized: docs/la ```
-Now you can check in your code editor the newly created directory `docs/ht/`. +Now you can check in your code editor the newly created directory `docs/la/`. -That command created a file `docs/ht/mkdocs.yml` with a simple config that inherits everything from the `en` version: +That command created a file `docs/la/mkdocs.yml` with a simple config that inherits everything from the `en` version: ```yaml INHERIT: ../en/mkdocs.yml @@ -350,11 +367,11 @@ You could also simply create that file with those contents manually. /// -That command also created a dummy file `docs/ht/index.md` for the main page, you can start by translating that one. +That command also created a dummy file `docs/la/index.md` for the main page, you can start by translating that one. You can continue with the previous instructions for an "Existing Language" for that process. -You can make the first pull request with those two files, `docs/ht/mkdocs.yml` and `docs/ht/index.md`. 🎉 +You can make the first pull request with those two files, `docs/la/mkdocs.yml` and `docs/la/index.md`. 🎉 #### Preview the result From d67f092db84c7c5cb7b882aa1e25c8a10b6727b6 Mon Sep 17 00:00:00 2001 From: github-actions Date: Sat, 26 Jul 2025 11:36:02 +0000 Subject: [PATCH 10/16] =?UTF-8?q?=F0=9F=93=9D=20Update=20release=20notes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [skip ci] --- docs/en/docs/release-notes.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/en/docs/release-notes.md b/docs/en/docs/release-notes.md index a6a68584a..0e5fa6303 100644 --- a/docs/en/docs/release-notes.md +++ b/docs/en/docs/release-notes.md @@ -7,6 +7,10 @@ hide: ## Latest Changes +### Docs + +* 📝 Add discussion template for new language translation requests. PR [#13535](https://github.com/fastapi/fastapi/pull/13535) by [@alejsdev](https://github.com/alejsdev). + ### Translations * 🌐 Update Portuguese Translation for `docs/pt/docs/async.md`. PR [#13863](https://github.com/fastapi/fastapi/pull/13863) by [@EdmilsonRodrigues](https://github.com/EdmilsonRodrigues). From ae02be90b6857cac1a00ea47c154457effcdcb5d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebasti=C3=A1n=20Ram=C3=ADrez?= Date: Sat, 26 Jul 2025 20:57:50 +0200 Subject: [PATCH 11/16] =?UTF-8?q?=F0=9F=94=A8=20Update=20translations=20sc?= =?UTF-8?q?ript=20to=20remove=20old=20(removed)=20files=20(#13928)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- docs/es/llm-prompt.md | 50 ----------------------- scripts/translate.py | 93 +++++++++++++++++++++++++++++++++++++++---- 2 files changed, 86 insertions(+), 57 deletions(-) diff --git a/docs/es/llm-prompt.md b/docs/es/llm-prompt.md index 3340dbc99..936ed9bba 100644 --- a/docs/es/llm-prompt.md +++ b/docs/es/llm-prompt.md @@ -4,56 +4,6 @@ Use the informal grammar (use "tú" instead of "usted"). For instructions or titles in imperative, keep them in imperative, for example "Edit it" to "Edítalo". -There are special blocks of notes, tips and others that look like: - -/// note - -To translate it, keep the same line and add the translation after a vertical bar: - -/// note | Nota - -Some examples: - -Source: - -/// tip - -Result: - -/// tip | Consejo - -Source: - -/// details | Preview - -Result: - -/// details | Vista previa - -Source: - -/// warning - -Result: - -/// warning | Advertencia - -Source: - -/// info - -Result: - -/// info | Información - -Source: - -/// note | Technical Details - -Result: - -/// note | Detalles Técnicos - --- For the next terms, use the following translations: diff --git a/scripts/translate.py b/scripts/translate.py index 9a2136d1b..09533747b 100644 --- a/scripts/translate.py +++ b/scripts/translate.py @@ -5,6 +5,7 @@ from typing import Iterable import typer import yaml from pydantic_ai import Agent +from rich import print non_translated_sections = ( "reference/", @@ -28,8 +29,38 @@ The content is written in markdown, write the translation in markdown as well. D When there's an example of code, the console or a terminal, normally surrounded by triple backticks and a keyword like "console" or "bash" (e.g. ```console), do not translate the content, keep the original in English. The original content will be surrounded by triple percentage signs (%) and you should translate it to the target language. Do not include the triple percentage signs in the translation. + +There are special blocks of notes, tips and others that look like: + +/// note + +To translate it, keep the same line and add the translation after a vertical bar. + +For example, if you were translating to Spanish, you would write: + +/// note | Nota + +Some examples in Spanish: + +Source: + +/// tip + +Result: + +/// tip | Consejo + +Source: + +/// details | Preview + +Result: + +/// details | Vista previa """ +app = typer.Typer() + @lru_cache def get_langs() -> dict[str, str]: @@ -46,6 +77,17 @@ def generate_lang_path(*, lang: str, path: Path) -> Path: return out_path +def generate_en_path(*, lang: str, path: Path) -> Path: + en_docs_path = Path("docs/en/docs") + assert not str(path).startswith(str(en_docs_path)), ( + f"Path must not be inside {en_docs_path}" + ) + lang_docs_path = Path(f"docs/{lang}/docs") + out_path = Path(str(path).replace(str(lang_docs_path), str(en_docs_path))) + return out_path + + +@app.command() def translate_page(*, lang: str, path: Path) -> None: langs = get_langs() language = langs[lang] @@ -68,8 +110,8 @@ def translate_page(*, lang: str, path: Path) -> None: agent = Agent("openai:gpt-4o") prompt_segments = [ - lang_prompt_content, general_prompt, + lang_prompt_content, ] if old_translation: prompt_segments.extend( @@ -119,6 +161,7 @@ def iter_paths_to_translate() -> Iterable[Path]: yield path +@app.command() def translate_all(lang: str) -> None: paths_to_process: list[Path] = [] for path in iter_paths_to_translate(): @@ -151,12 +194,48 @@ def translate_all(lang: str) -> None: print(f"Done translating: {p}") -def main(*, lang: str, path: Path = None) -> None: - if path: - translate_page(lang=lang, path=path) - else: - translate_all(lang=lang) +@app.command() +def list_removable(lang: str) -> list[Path]: + removable_paths: list[Path] = [] + lang_paths = Path(f"docs/{lang}").rglob("*.md") + for path in lang_paths: + en_path = generate_en_path(lang=lang, path=path) + if not en_path.exists(): + removable_paths.append(path) + print(removable_paths) + return removable_paths + + +@app.command() +def list_all_removable() -> list[Path]: + all_removable_paths: list[Path] = [] + langs = get_langs() + for lang in langs: + if lang == "en": + continue + removable_paths = list_removable(lang) + all_removable_paths.extend(removable_paths) + print(all_removable_paths) + return all_removable_paths + + +@app.command() +def remove_removable(lang: str) -> None: + removable_paths = list_removable(lang) + for path in removable_paths: + path.unlink() + print(f"Removed: {path}") + print("Done removing all removable paths") + + +@app.command() +def remove_all_removable() -> None: + all_removable = list_all_removable() + for removable_path in all_removable: + removable_path.unlink() + print(f"Removed: {removable_path}") + print("Done removing all removable paths") if __name__ == "__main__": - typer.run(main) + app() From 4f0aae9d237059c48aace9cc222f62e3ef4f2ee5 Mon Sep 17 00:00:00 2001 From: github-actions Date: Sat, 26 Jul 2025 18:58:14 +0000 Subject: [PATCH 12/16] =?UTF-8?q?=F0=9F=93=9D=20Update=20release=20notes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [skip ci] --- docs/en/docs/release-notes.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/en/docs/release-notes.md b/docs/en/docs/release-notes.md index 0e5fa6303..3b94dfd8f 100644 --- a/docs/en/docs/release-notes.md +++ b/docs/en/docs/release-notes.md @@ -23,6 +23,7 @@ hide: ### Internal +* 🔨 Update translations script to remove old (removed) files. PR [#13928](https://github.com/fastapi/fastapi/pull/13928) by [@tiangolo](https://github.com/tiangolo). * ⬆ [pre-commit.ci] pre-commit autoupdate. PR [#13894](https://github.com/fastapi/fastapi/pull/13894) by [@pre-commit-ci[bot]](https://github.com/apps/pre-commit-ci). * ⬆ Update httpx requirement to >=0.23.0,<0.29.0. PR [#13114](https://github.com/fastapi/fastapi/pull/13114) by [@yan12125](https://github.com/yan12125). * 🔧 Update sponsors: Add Mobb. PR [#13916](https://github.com/fastapi/fastapi/pull/13916) by [@tiangolo](https://github.com/tiangolo). From adc328e258d00c5717070a103626cb48a03abecf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebasti=C3=A1n=20Ram=C3=ADrez?= Date: Sat, 26 Jul 2025 23:19:05 +0200 Subject: [PATCH 13/16] =?UTF-8?q?=F0=9F=94=A8=20Refactor=20translate=20scr?= =?UTF-8?q?ipt=20with=20extra=20feedback=20(prints)=20(#13932)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- scripts/translate.py | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/scripts/translate.py b/scripts/translate.py index 09533747b..6af12dc98 100644 --- a/scripts/translate.py +++ b/scripts/translate.py @@ -106,7 +106,9 @@ def translate_page(*, lang: str, path: Path) -> None: original_content = path.read_text() old_translation: str | None = None if out_path.exists(): + print(f"Found existing translation: {out_path}") old_translation = out_path.read_text() + print(f"Translating {path} to {lang} ({language})") agent = Agent("openai:gpt-4o") prompt_segments = [ @@ -131,13 +133,14 @@ def translate_page(*, lang: str, path: Path) -> None: ] ) prompt = "\n\n".join(prompt_segments) - + print(f"Running agent for {out_path}") result = agent.run_sync(prompt) out_content = f"{result.data.strip()}\n" + print(f"Saving translation to {out_path}") out_path.write_text(out_content) -def iter_paths_to_translate() -> Iterable[Path]: +def iter_all_en_paths() -> Iterable[Path]: """ Iterate on the markdown files to translate in order of priority. """ @@ -161,13 +164,16 @@ def iter_paths_to_translate() -> Iterable[Path]: yield path -@app.command() -def translate_all(lang: str) -> None: - paths_to_process: list[Path] = [] - for path in iter_paths_to_translate(): +def iter_en_paths_to_translate() -> Iterable[Path]: + for path in iter_all_en_paths(): if str(path).replace("docs/en/docs/", "").startswith(non_translated_sections): continue - paths_to_process.append(path) + yield path + + +@app.command() +def translate_all(lang: str) -> None: + paths_to_process = list(iter_en_paths_to_translate()) print("Original paths:") for p in paths_to_process: print(f" - {p}") From 4c40b484f1a6ecea0921dc58292c3f733ddf5d9b Mon Sep 17 00:00:00 2001 From: github-actions Date: Sat, 26 Jul 2025 21:19:29 +0000 Subject: [PATCH 14/16] =?UTF-8?q?=F0=9F=93=9D=20Update=20release=20notes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [skip ci] --- docs/en/docs/release-notes.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/en/docs/release-notes.md b/docs/en/docs/release-notes.md index 3b94dfd8f..56c325977 100644 --- a/docs/en/docs/release-notes.md +++ b/docs/en/docs/release-notes.md @@ -23,6 +23,7 @@ hide: ### Internal +* 🔨 Refactor translate script with extra feedback (prints). PR [#13932](https://github.com/fastapi/fastapi/pull/13932) by [@tiangolo](https://github.com/tiangolo). * 🔨 Update translations script to remove old (removed) files. PR [#13928](https://github.com/fastapi/fastapi/pull/13928) by [@tiangolo](https://github.com/tiangolo). * ⬆ [pre-commit.ci] pre-commit autoupdate. PR [#13894](https://github.com/fastapi/fastapi/pull/13894) by [@pre-commit-ci[bot]](https://github.com/apps/pre-commit-ci). * ⬆ Update httpx requirement to >=0.23.0,<0.29.0. PR [#13114](https://github.com/fastapi/fastapi/pull/13114) by [@yan12125](https://github.com/yan12125). From 273b06adab5fd527492ff9ed3f98cfa653d10002 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebasti=C3=A1n=20Ram=C3=ADrez?= Date: Sat, 26 Jul 2025 23:27:35 +0200 Subject: [PATCH 15/16] =?UTF-8?q?=E2=9A=92=EF=B8=8F=20Update=20translate?= =?UTF-8?q?=20script,=20show=20and=20update=20outdated=20translations=20(#?= =?UTF-8?q?13933)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- requirements-translations.txt | 1 + scripts/translate.py | 35 +++++++++++++++++++++++++++++++++++ 2 files changed, 36 insertions(+) diff --git a/requirements-translations.txt b/requirements-translations.txt index 7a2a8004e..90f718032 100644 --- a/requirements-translations.txt +++ b/requirements-translations.txt @@ -1 +1,2 @@ pydantic-ai==0.0.30 +GitPython==3.1.45 diff --git a/scripts/translate.py b/scripts/translate.py index 6af12dc98..b78d7211b 100644 --- a/scripts/translate.py +++ b/scripts/translate.py @@ -2,6 +2,7 @@ from functools import lru_cache from pathlib import Path from typing import Iterable +import git import typer import yaml from pydantic_ai import Agent @@ -243,5 +244,39 @@ def remove_all_removable() -> None: print("Done removing all removable paths") +@app.command() +def list_outdated(lang: str) -> list[Path]: + dir_path = Path(__file__).absolute().parent.parent + repo = git.Repo(dir_path) + + outdated_paths: list[Path] = [] + en_lang_paths = list(iter_en_paths_to_translate()) + for path in en_lang_paths: + lang_path = generate_lang_path(lang=lang, path=path) + if not lang_path.exists(): + outdated_paths.append(path) + continue + en_commit_datetime = list(repo.iter_commits(paths=path, max_count=1))[ + 0 + ].committed_datetime + lang_commit_datetime = list(repo.iter_commits(paths=lang_path, max_count=1))[ + 0 + ].committed_datetime + if lang_commit_datetime < en_commit_datetime: + outdated_paths.append(path) + print(outdated_paths) + return outdated_paths + + +@app.command() +def update_outdated(lang: str) -> None: + outdated_paths = list_outdated(lang) + for path in outdated_paths: + print(f"Updating lang: {lang} path: {path}") + translate_page(lang=lang, path=path) + print(f"Done updating: {path}") + print("Done updating all outdated paths") + + if __name__ == "__main__": app() From d5d302c3da11262f4e320a9af5dbb9d13386e1b5 Mon Sep 17 00:00:00 2001 From: github-actions Date: Sat, 26 Jul 2025 21:27:58 +0000 Subject: [PATCH 16/16] =?UTF-8?q?=F0=9F=93=9D=20Update=20release=20notes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [skip ci] --- docs/en/docs/release-notes.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/en/docs/release-notes.md b/docs/en/docs/release-notes.md index 56c325977..f3984fa9e 100644 --- a/docs/en/docs/release-notes.md +++ b/docs/en/docs/release-notes.md @@ -23,6 +23,7 @@ hide: ### Internal +* ⚒️ Update translate script, show and update outdated translations. PR [#13933](https://github.com/fastapi/fastapi/pull/13933) by [@tiangolo](https://github.com/tiangolo). * 🔨 Refactor translate script with extra feedback (prints). PR [#13932](https://github.com/fastapi/fastapi/pull/13932) by [@tiangolo](https://github.com/tiangolo). * 🔨 Update translations script to remove old (removed) files. PR [#13928](https://github.com/fastapi/fastapi/pull/13928) by [@tiangolo](https://github.com/tiangolo). * ⬆ [pre-commit.ci] pre-commit autoupdate. PR [#13894](https://github.com/fastapi/fastapi/pull/13894) by [@pre-commit-ci[bot]](https://github.com/apps/pre-commit-ci).