Compare commits

..

65 Commits

Author SHA1 Message Date
Sebastián Ramírez
e54e5a8980 🔖 Release version 0.136.1 2026-04-23 18:45:55 +02:00
github-actions[bot]
9a8a5fd999 📝 Update release notes
[skip ci]
2026-04-23 16:40:58 +00:00
Sofie Van Landeghem
7815a32f2e ⬆️ Update Pydantic v2 code to address deprecations (#15101)
Co-authored-by: pre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com>
2026-04-23 18:40:29 +02:00
github-actions[bot]
ef1c927b05 📝 Update release notes
[skip ci]
2026-04-23 16:36:15 +00:00
Motov Yurii
38039e12a8 🔨 Tweak translation script (#15174) 2026-04-23 18:35:43 +02:00
github-actions[bot]
4fa826ce0a 📝 Update release notes
[skip ci]
2026-04-22 13:11:11 +00:00
dependabot[bot]
c39415673e ⬆ Bump mkdocs-material from 9.7.1 to 9.7.6 (#15408)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-04-22 15:10:45 +02:00
github-actions[bot]
ae230ad2f9 📝 Update release notes
[skip ci]
2026-04-22 13:04:53 +00:00
dependabot[bot]
d9eb39d1a1 ⬆ Bump inline-snapshot from 0.31.1 to 0.32.6 (#15409)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Motov Yurii <109919500+YuriiMotov@users.noreply.github.com>
2026-04-22 15:04:27 +02:00
github-actions[bot]
4f8b5d14d3 📝 Update release notes
[skip ci]
2026-04-22 12:53:59 +00:00
dependabot[bot]
04958499e0 ⬆ Bump pytest-codspeed from 4.3.0 to 4.4.0 (#15407)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-04-22 14:53:36 +02:00
github-actions[bot]
71f3c30108 📝 Update release notes
[skip ci]
2026-04-22 12:49:37 +00:00
dependabot[bot]
ea230dc80b ⬆ Bump pytest-cov from 7.0.0 to 7.1.0 (#15406)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-04-22 14:49:01 +02:00
github-actions[bot]
5f68b19002 📝 Update release notes
[skip ci]
2026-04-22 12:45:14 +00:00
dependabot[bot]
7402e33375 ⬆ Bump cloudflare/wrangler-action from 3.14.1 to 3.15.0 (#15405)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-04-22 14:44:48 +02:00
github-actions[bot]
750ef483f2 📝 Update release notes
[skip ci]
2026-04-22 12:41:48 +00:00
dependabot[bot]
7c6b79eb3b ⬆ Bump mypy from 1.19.1 to 1.20.1 (#15410)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-04-22 14:41:23 +02:00
github-actions[bot]
c38782e0e8 📝 Update release notes
[skip ci]
2026-04-22 07:43:16 +00:00
dependabot[bot]
840e462667 ⬆ Bump python-dotenv from 1.2.1 to 1.2.2 (#15400)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-04-22 09:42:50 +02:00
github-actions[bot]
88021c3dc0 📝 Update release notes
[skip ci]
2026-04-21 12:46:11 +00:00
dependabot[bot]
13eade2fd9 ⬆ Bump starlette from 0.52.1 to 1.0.0 (#15397)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-04-21 14:45:41 +02:00
github-actions[bot]
e0900abc6d 📝 Update release notes
[skip ci]
2026-04-21 12:43:48 +00:00
dependabot[bot]
4e6d8dc600 ⬆ Bump pygithub from 2.8.1 to 2.9.1 (#15396)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-04-21 14:43:25 +02:00
github-actions[bot]
2bf3cdeb19 📝 Update release notes
[skip ci]
2026-04-21 12:34:33 +00:00
dependabot[bot]
043fdce921 ⬆ Bump pyjwt from 2.12.0 to 2.12.1 (#15393)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-04-21 14:34:09 +02:00
github-actions[bot]
9d79b257ad 📝 Update release notes
[skip ci]
2026-04-21 12:27:48 +00:00
dependabot[bot]
6fadc67d62 ⬆ Bump zizmor from 1.23.1 to 1.24.1 (#15394)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-04-21 14:27:24 +02:00
github-actions[bot]
a93b1db937 📝 Update release notes
[skip ci]
2026-04-21 12:25:12 +00:00
dependabot[bot]
e5a86c3972 ⬆ Bump strawberry-graphql from 0.312.3 to 0.314.3 (#15395)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-04-21 14:24:45 +02:00
github-actions[bot]
916edab526 📝 Update release notes
[skip ci]
2026-04-21 08:16:18 +00:00
dependabot[bot]
355c12ce0e ⬆ Bump python-multipart from 0.0.22 to 0.0.26 (#15360)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Motov Yurii <109919500+YuriiMotov@users.noreply.github.com>
2026-04-21 10:15:51 +02:00
github-actions[bot]
4a29794efb 📝 Update release notes
[skip ci]
2026-04-21 08:13:27 +00:00
dependabot[bot]
c6bf1a9702 ⬆ Bump authlib from 1.6.9 to 1.6.11 (#15373)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Motov Yurii <109919500+YuriiMotov@users.noreply.github.com>
2026-04-21 08:12:58 +00:00
github-actions[bot]
19c7b4bc28 📝 Update release notes
[skip ci]
2026-04-21 07:57:39 +00:00
dependabot[bot]
0d68ccc87c ⬆ Bump aiohttp from 3.13.3 to 3.13.4 (#15282)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Motov Yurii <109919500+YuriiMotov@users.noreply.github.com>
2026-04-21 07:57:13 +00:00
github-actions[bot]
fb5f830bf8 📝 Update release notes
[skip ci]
2026-04-21 07:47:30 +00:00
dependabot[bot]
7ce742e2c1 ⬆ Bump pygments from 2.19.2 to 2.20.0 (#15263)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Motov Yurii <109919500+YuriiMotov@users.noreply.github.com>
2026-04-21 07:47:00 +00:00
github-actions[bot]
28b18ced2d 📝 Update release notes
[skip ci]
2026-04-21 07:38:46 +00:00
Motov Yurii
2d625c0962 ⬆ Bump pymdown-extensions from 10.20.1 to 10.21.2 (#15391) 2026-04-21 09:38:21 +02:00
github-actions[bot]
ecc9069b90 📝 Update release notes
[skip ci]
2026-04-20 21:41:14 +00:00
dependabot[bot]
7d23cc8fe8 ⬆ Bump pillow from 12.1.1 to 12.2.0 (#15333)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Motov Yurii <109919500+YuriiMotov@users.noreply.github.com>
2026-04-20 21:40:49 +00:00
github-actions[bot]
636998e53a 📝 Update release notes
[skip ci]
2026-04-20 21:33:25 +00:00
dependabot[bot]
a8773aadf8 ⬆ Bump pytest from 9.0.2 to 9.0.3 (#15334)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-04-20 23:32:59 +02:00
github-actions[bot]
2378fbbc45 📝 Update release notes
[skip ci]
2026-04-20 21:20:00 +00:00
dependabot[bot]
c874e067dc ⬆ Bump actions/upload-artifact from 7.0.0 to 7.0.1 (#15374)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-04-20 23:19:34 +02:00
github-actions[bot]
bdd6c70760 📝 Update release notes
[skip ci]
2026-04-20 21:10:31 +00:00
dependabot[bot]
a595e44d28 ⬆ Bump actions/cache from 5.0.4 to 5.0.5 (#15385)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-04-20 23:10:03 +02:00
github-actions[bot]
2fa00db858 📝 Update release notes
[skip ci]
2026-04-16 12:48:09 +00:00
Sebastián Ramírez
41df44a29c 🔧 Update sponsors: remove Zuplo (#15369) 2026-04-16 12:47:22 +00:00
github-actions[bot]
6976b0d4e6 📝 Update release notes
[skip ci]
2026-04-16 12:42:52 +00:00
Sebastián Ramírez
3808d8540f 🔧 Update sponsors: remove Speakeasy (#15368) 2026-04-16 12:42:27 +00:00
github-actions[bot]
24db1286eb 📝 Update release notes
[skip ci]
2026-04-16 12:21:29 +00:00
Motov Yurii
3f4169be1a 🔒️ Add zizmor and fix audit findings (#15316) 2026-04-16 12:21:03 +00:00
Sebastián Ramírez
708606c982 🔖 Release version 0.136.0 2026-04-16 13:45:09 +02:00
github-actions[bot]
13be6a3a0f 📝 Update release notes
[skip ci]
2026-04-16 11:42:31 +00:00
Sofie Van Landeghem
4b264878d7 ⬆️ Support free-threaded Python 3.14t (#15149) 2026-04-16 13:42:00 +02:00
Sebastián Ramírez
f796c346a8 🔖 Release version 0.135.4 2026-04-16 13:37:43 +02:00
github-actions[bot]
09d1d1cb70 📝 Update release notes
[skip ci]
2026-04-16 11:35:32 +00:00
Sebastián Ramírez
ae4e45c5cc 🔥 Remove April Fool's @app.vibe() 🤪 (#15363) 2026-04-16 11:35:06 +00:00
github-actions[bot]
9653034b8d 📝 Update release notes
[skip ci]
2026-04-13 12:43:51 +00:00
dependabot[bot]
6f9a102faf ⬆ Bump cryptography from 46.0.5 to 46.0.7 (#15314)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-04-13 14:43:26 +02:00
github-actions[bot]
eba8942c81 📝 Update release notes
[skip ci]
2026-04-07 09:50:22 +00:00
dependabot[bot]
77d080caf8 ⬆ Bump strawberry-graphql from 0.307.1 to 0.312.3 (#15309)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-04-07 11:49:52 +02:00
github-actions[bot]
c3c9dd6b1a 📝 Update release notes
[skip ci]
2026-04-03 12:07:04 +00:00
Motov Yurii
180e81bb4b 🔨 Add pre-commit hook to ensure latest release header has date (#15293) 2026-04-03 14:06:36 +02:00
50 changed files with 1108 additions and 1067 deletions

View File

@@ -5,12 +5,25 @@ updates:
directory: "/"
schedule:
interval: "daily"
cooldown:
default-days: 7
commit-message:
prefix:
# Python
- package-ecosystem: "uv"
directory: "/"
schedule:
interval: "monthly"
interval: "daily"
cooldown:
default-days: 7
commit-message:
prefix:
# pre-commit
- package-ecosystem: "pre-commit"
directory: "/"
schedule:
interval: "daily"
cooldown:
default-days: 7
commit-message:
prefix:

View File

@@ -1,18 +1,20 @@
name: Add to Project
on:
pull_request_target:
pull_request_target: # zizmor: ignore[dangerous-triggers]
issues:
types:
- opened
- reopened
permissions: {}
jobs:
add-to-project:
name: Add to project
runs-on: ubuntu-latest
steps:
- uses: actions/add-to-project@v1.0.2
- uses: actions/add-to-project@244f685bbc3b7adfa8466e08b698b5577571133e # v1.0.2
with:
project-url: https://github.com/orgs/fastapi/projects/2
github-token: ${{ secrets.PROJECTS_TOKEN }}
github-token: ${{ secrets.PROJECTS_TOKEN }} # zizmor: ignore[secrets-outside-env]

View File

@@ -8,6 +8,8 @@ on:
- opened
- synchronize
permissions: {}
jobs:
changes:
runs-on: ubuntu-latest
@@ -18,9 +20,11 @@ jobs:
outputs:
docs: ${{ steps.filter.outputs.docs }}
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
# For pull requests it's not necessary to checkout the code but for the main branch it is
- uses: dorny/paths-filter@v4
- uses: dorny/paths-filter@fbd0ab8f3e69293af611ebaee6363fc25e6d187d # v4.0.1
id: filter
with:
filters: |
@@ -42,14 +46,17 @@ jobs:
outputs:
langs: ${{ steps.show-langs.outputs.langs }}
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: Set up Python
uses: actions/setup-python@v6
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
with:
python-version-file: ".python-version"
- name: Setup uv
uses: astral-sh/setup-uv@v7
uses: astral-sh/setup-uv@37802adc94f370d6bfd71619e3f0bf239e1f3b78 # v7.6.0
with:
version: "0.11.4"
enable-cache: true
cache-dependency-glob: |
pyproject.toml
@@ -75,14 +82,17 @@ jobs:
env:
GITHUB_CONTEXT: ${{ toJson(github) }}
run: echo "$GITHUB_CONTEXT"
- uses: actions/checkout@v6
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: Set up Python
uses: actions/setup-python@v6
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
with:
python-version-file: ".python-version"
- name: Setup uv
uses: astral-sh/setup-uv@v7
uses: astral-sh/setup-uv@37802adc94f370d6bfd71619e3f0bf239e1f3b78 # v7.6.0
with:
version: "0.11.4"
enable-cache: true
cache-dependency-glob: |
pyproject.toml
@@ -91,13 +101,14 @@ jobs:
run: uv sync --locked --no-dev --group docs
- name: Update Languages
run: uv run ./scripts/docs.py update-languages
- uses: actions/cache@v5
- uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
with:
key: mkdocs-cards-${{ matrix.lang }}-${{ github.ref }}
path: docs/${{ matrix.lang }}/.cache
- name: Build Docs
run: uv run ./scripts/docs.py build-lang ${{ matrix.lang }}
- uses: actions/upload-artifact@v7
run: | # zizmor: ignore[template-injection] - comes from trusted source
uv run ./scripts/docs.py build-lang ${{ matrix.lang }}
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: docs-site-${{ matrix.lang }}
path: ./site/**
@@ -111,7 +122,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Decide whether the needed jobs succeeded or failed
uses: re-actors/alls-green@release/v1
uses: re-actors/alls-green@05ac9388f0aebcb5727afa17fcccfecd6f8ec5fe # v1.2.2
with:
jobs: ${{ toJSON(needs) }}
allowed-skips: build-docs

View File

@@ -10,6 +10,8 @@ on:
required: false
default: "false"
permissions: {}
jobs:
job:
if: github.repository_owner == 'fastapi'
@@ -21,14 +23,17 @@ jobs:
env:
GITHUB_CONTEXT: ${{ toJson(github) }}
run: echo "$GITHUB_CONTEXT"
- uses: actions/checkout@v6
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: true # Required for `git push` in `contributors.py`
- name: Set up Python
uses: actions/setup-python@v6
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
with:
python-version-file: ".python-version"
- name: Setup uv
uses: astral-sh/setup-uv@v7
uses: astral-sh/setup-uv@37802adc94f370d6bfd71619e3f0bf239e1f3b78 # v7.6.0
with:
version: "0.11.4"
enable-cache: true
cache-dependency-glob: |
pyproject.toml
@@ -37,13 +42,13 @@ jobs:
run: uv sync --locked --no-dev --group github-actions
# Allow debugging with tmate
- name: Setup tmate session
uses: mxschmitt/action-tmate@v3
uses: mxschmitt/action-tmate@c0afd6f790e3a5564914980036ebf83216678101 # v3.23
if: ${{ github.event_name == 'workflow_dispatch' && github.event.inputs.debug_enabled == 'true' }}
with:
limit-access-to-actor: true
env:
GITHUB_TOKEN: ${{ secrets.FASTAPI_PR_TOKEN }}
GITHUB_TOKEN: ${{ secrets.FASTAPI_PR_TOKEN }} # zizmor: ignore[secrets-outside-env]
- name: FastAPI People Contributors
run: uv run ./scripts/contributors.py
env:
GITHUB_TOKEN: ${{ secrets.FASTAPI_PR_TOKEN }}
GITHUB_TOKEN: ${{ secrets.FASTAPI_PR_TOKEN }} # zizmor: ignore[secrets-outside-env]

View File

@@ -1,37 +1,38 @@
name: Deploy Docs
on:
workflow_run:
workflow_run: # zizmor: ignore[dangerous-triggers]
workflows:
- Build Docs
types:
- completed
permissions:
deployments: write
issues: write
pull-requests: write
statuses: write
permissions: {}
jobs:
deploy-docs:
runs-on: ubuntu-latest
permissions:
deployments: write
issues: write
pull-requests: write
statuses: write
steps:
- name: Dump GitHub context
env:
GITHUB_CONTEXT: ${{ toJson(github) }}
run: echo "$GITHUB_CONTEXT"
- uses: actions/checkout@v6
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: Set up Python
uses: actions/setup-python@v6
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
with:
python-version-file: ".python-version"
- name: Setup uv
uses: astral-sh/setup-uv@v7
uses: astral-sh/setup-uv@37802adc94f370d6bfd71619e3f0bf239e1f3b78 # v7.6.0
with:
enable-cache: true
cache-dependency-glob: |
pyproject.toml
uv.lock
version: "0.11.4"
enable-cache: false
- name: Install GitHub Actions dependencies
run: uv sync --locked --no-dev --group github-actions
- name: Deploy Docs Status Pending
@@ -45,7 +46,7 @@ jobs:
run: |
rm -rf ./site
mkdir ./site
- uses: actions/download-artifact@v8
- uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
path: ./site/
pattern: docs-site-*
@@ -59,10 +60,10 @@ jobs:
env:
PROJECT_NAME: fastapitiangolo
BRANCH: ${{ ( github.event.workflow_run.head_repository.full_name == github.repository && github.event.workflow_run.head_branch == 'master' && 'main' ) || ( github.event.workflow_run.head_sha ) }}
uses: cloudflare/wrangler-action@v3
uses: cloudflare/wrangler-action@9acf94ace14e7dc412b076f2c5c20b8ce93c79cd # v3.15.0
with:
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }} # zizmor: ignore[secrets-outside-env]
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }} # zizmor: ignore[secrets-outside-env]
command: pages deploy ./site --project-name=${{ env.PROJECT_NAME }} --branch=${{ env.BRANCH }}
- name: Deploy Docs Status Error
if: failure()

View File

@@ -1,9 +1,11 @@
name: "Conflict detector"
on:
push:
pull_request_target:
pull_request_target: # zizmor: ignore[dangerous-triggers]
types: [synchronize]
permissions: {}
jobs:
main:
permissions:
@@ -12,7 +14,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Check if PRs have merge conflicts
uses: eps1lon/actions-label-merge-conflict@v3
uses: eps1lon/actions-label-merge-conflict@1df065ebe6e3310545d4f4c4e862e43bdca146f0 # v3.0.3
with:
dirtyLabel: "conflicts"
repoToken: "${{ secrets.GITHUB_TOKEN }}"

View File

@@ -9,25 +9,26 @@ on:
issues:
types:
- labeled
pull_request_target:
pull_request_target: # zizmor: ignore[dangerous-triggers]
types:
- labeled
workflow_dispatch:
permissions:
issues: write
pull-requests: write
permissions: {}
jobs:
issue-manager:
if: github.repository_owner == 'fastapi'
runs-on: ubuntu-latest
permissions:
issues: write
pull-requests: write
steps:
- name: Dump GitHub context
env:
GITHUB_CONTEXT: ${{ toJson(github) }}
run: echo "$GITHUB_CONTEXT"
- uses: tiangolo/issue-manager@0.6.0
- uses: tiangolo/issue-manager@2fb3484ec9279485df8659e8ec73de262431737d # 0.6.0
with:
token: ${{ secrets.GITHUB_TOKEN }}
config: >

View File

@@ -5,26 +5,30 @@ on:
- cron: "0 12 * * *"
workflow_dispatch:
permissions:
pull-requests: write
permissions: {}
jobs:
label-approved:
if: github.repository_owner == 'fastapi'
runs-on: ubuntu-latest
permissions:
pull-requests: write
steps:
- name: Dump GitHub context
env:
GITHUB_CONTEXT: ${{ toJson(github) }}
run: echo "$GITHUB_CONTEXT"
- uses: actions/checkout@v6
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: Set up Python
uses: actions/setup-python@v6
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
with:
python-version-file: ".python-version"
- name: Setup uv
uses: astral-sh/setup-uv@v7
uses: astral-sh/setup-uv@37802adc94f370d6bfd71619e3f0bf239e1f3b78 # v7.6.0
with:
version: "0.11.4"
enable-cache: true
cache-dependency-glob: |
pyproject.toml

View File

@@ -1,6 +1,6 @@
name: Labels
on:
pull_request_target:
pull_request_target: # zizmor: ignore[dangerous-triggers]
types:
- opened
- synchronize
@@ -9,6 +9,8 @@ on:
- labeled
- unlabeled
permissions: {}
jobs:
labeler:
permissions:
@@ -16,7 +18,7 @@ jobs:
pull-requests: write
runs-on: ubuntu-latest
steps:
- uses: actions/labeler@v6
- uses: actions/labeler@634933edcd8ababfe52f92936142cc22ac488b1b # v6.0.1
if: ${{ github.event.action != 'labeled' && github.event.action != 'unlabeled' }}
- run: echo "Done adding labels"
# Run this after labeler applied labels
@@ -27,7 +29,7 @@ jobs:
pull-requests: read
runs-on: ubuntu-latest
steps:
- uses: docker://agilepathway/pull-request-label-checker:latest
- uses: agilepathway/label-checker@c3d16ad512e7cea5961df85ff2486bb774caf3c5 # v1.6.65
with:
one_of: breaking,security,feature,bug,refactor,upgrade,docs,lang-all,internal
repo_token: ${{ secrets.GITHUB_TOKEN }}

View File

@@ -1,7 +1,7 @@
name: Latest Changes
on:
pull_request_target:
pull_request_target: # zizmor: ignore[dangerous-triggers]
branches:
- master
types:
@@ -16,27 +16,29 @@ on:
required: false
default: 'false'
permissions: {}
jobs:
latest-changes:
runs-on: ubuntu-latest
if: github.event_name == 'workflow_dispatch' || github.event.pull_request.merged == true
steps:
- name: Dump GitHub context
env:
GITHUB_CONTEXT: ${{ toJson(github) }}
run: echo "$GITHUB_CONTEXT"
# pin to actions/checkout@v5 for compatibility with latest-changes
# Ref: https://github.com/actions/checkout/issues/2313
- uses: actions/checkout@v5
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
# To allow latest-changes to commit to the main branch
token: ${{ secrets.FASTAPI_LATEST_CHANGES }}
token: ${{ secrets.FASTAPI_LATEST_CHANGES }} # zizmor: ignore[secrets-outside-env]
persist-credentials: true # required by tiangolo/latest-changes
# Allow debugging with tmate
- name: Setup tmate session
uses: mxschmitt/action-tmate@v3
uses: mxschmitt/action-tmate@c0afd6f790e3a5564914980036ebf83216678101 # v3.23
if: ${{ github.event_name == 'workflow_dispatch' && github.event.inputs.debug_enabled == 'true' }}
with:
limit-access-to-actor: true
- uses: tiangolo/latest-changes@0.4.1
- uses: tiangolo/latest-changes@c9d329cb147f0ddf4fb631214e3f838ff17ccbbd # 0.4.1
with:
token: ${{ secrets.GITHUB_TOKEN }}
latest_changes_file: docs/en/docs/release-notes.md

View File

@@ -1,10 +1,12 @@
name: Notify Translations
on:
pull_request_target:
pull_request_target: # zizmor: ignore[dangerous-triggers]
types:
- labeled
- closed
branches:
- master
workflow_dispatch:
inputs:
number:
@@ -15,6 +17,8 @@ on:
required: false
default: 'false'
permissions: {}
jobs:
job:
runs-on: ubuntu-latest
@@ -25,14 +29,17 @@ jobs:
env:
GITHUB_CONTEXT: ${{ toJson(github) }}
run: echo "$GITHUB_CONTEXT"
- uses: actions/checkout@v6
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: Set up Python
uses: actions/setup-python@v6
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
with:
python-version-file: ".python-version"
- name: Setup uv
uses: astral-sh/setup-uv@v7
uses: astral-sh/setup-uv@37802adc94f370d6bfd71619e3f0bf239e1f3b78 # v7.6.0
with:
version: "0.11.4"
enable-cache: true
cache-dependency-glob: |
pyproject.toml
@@ -41,7 +48,7 @@ jobs:
run: uv sync --locked --no-dev --group github-actions
# Allow debugging with tmate
- name: Setup tmate session
uses: mxschmitt/action-tmate@v3
uses: mxschmitt/action-tmate@c0afd6f790e3a5564914980036ebf83216678101 # v3.23
if: ${{ github.event_name == 'workflow_dispatch' && github.event.inputs.debug_enabled == 'true' }}
with:
limit-access-to-actor: true

View File

@@ -10,6 +10,8 @@ on:
required: false
default: "false"
permissions: {}
jobs:
job:
if: github.repository_owner == 'fastapi'
@@ -21,14 +23,17 @@ jobs:
env:
GITHUB_CONTEXT: ${{ toJson(github) }}
run: echo "$GITHUB_CONTEXT"
- uses: actions/checkout@v6
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: true # Required for `git push` in `people.py`
- name: Set up Python
uses: actions/setup-python@v6
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
with:
python-version-file: ".python-version"
- name: Setup uv
uses: astral-sh/setup-uv@v7
uses: astral-sh/setup-uv@37802adc94f370d6bfd71619e3f0bf239e1f3b78 # v7.6.0
with:
version: "0.11.4"
enable-cache: true
cache-dependency-glob: |
pyproject.toml
@@ -37,14 +42,14 @@ jobs:
run: uv sync --locked --no-dev --group github-actions
# Allow debugging with tmate
- name: Setup tmate session
uses: mxschmitt/action-tmate@v3
uses: mxschmitt/action-tmate@c0afd6f790e3a5564914980036ebf83216678101 # v3.23
if: ${{ github.event_name == 'workflow_dispatch' && github.event.inputs.debug_enabled == 'true' }}
with:
limit-access-to-actor: true
env:
GITHUB_TOKEN: ${{ secrets.FASTAPI_PEOPLE }}
GITHUB_TOKEN: ${{ secrets.FASTAPI_PEOPLE }} # zizmor: ignore[secrets-outside-env]
- name: FastAPI People Experts
run: uv run ./scripts/people.py
env:
GITHUB_TOKEN: ${{ secrets.FASTAPI_PEOPLE }}
GITHUB_TOKEN: ${{ secrets.FASTAPI_PEOPLE }} # zizmor: ignore[secrets-outside-env]
SLEEP_INTERVAL: ${{ vars.PEOPLE_SLEEP_INTERVAL }}

View File

@@ -6,6 +6,8 @@ on:
- opened
- synchronize
permissions: {}
env:
# Forks and Dependabot don't have access to secrets
HAS_SECRETS: ${{ secrets.PRE_COMMIT != '' }}
@@ -18,7 +20,7 @@ jobs:
env:
GITHUB_CONTEXT: ${{ toJson(github) }}
run: echo "$GITHUB_CONTEXT"
- uses: actions/checkout@v5
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
name: Checkout PR for own repo
if: env.HAS_SECRETS == 'true'
with:
@@ -28,22 +30,25 @@ jobs:
# And it needs the full history to be able to compute diffs
fetch-depth: 0
# A token other than the default GITHUB_TOKEN is needed to be able to trigger CI
token: ${{ secrets.PRE_COMMIT }}
token: ${{ secrets.PRE_COMMIT }} # zizmor: ignore[secrets-outside-env]
persist-credentials: true # Required for `git push` command
# pre-commit lite ci needs the default checkout configs to work
- uses: actions/checkout@v5
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
name: Checkout PR for fork
if: env.HAS_SECRETS == 'false'
with:
# To be able to commit it needs the head branch of the PR, the remote one
ref: ${{ github.event.pull_request.head.sha }}
fetch-depth: 0
persist-credentials: false
- name: Set up Python
uses: actions/setup-python@v6
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
with:
python-version-file: ".python-version"
- name: Setup uv
uses: astral-sh/setup-uv@v7
uses: astral-sh/setup-uv@37802adc94f370d6bfd71619e3f0bf239e1f3b78 # v7.6.0
with:
version: "0.11.4"
cache-dependency-glob: |
pyproject.toml
uv.lock
@@ -51,7 +56,7 @@ jobs:
run: uv sync --locked --extra all
- name: Run prek - pre-commit
id: precommit
run: uvx prek run --from-ref origin/${GITHUB_BASE_REF} --to-ref HEAD --show-diff-on-failure
run: uv run prek run --from-ref origin/${GITHUB_BASE_REF} --to-ref HEAD --show-diff-on-failure
continue-on-error: true
- name: Commit and push changes
if: env.HAS_SECRETS == 'true'
@@ -65,7 +70,7 @@ jobs:
git commit -m "🎨 Auto format"
git push
fi
- uses: pre-commit-ci/lite-action@v1.1.0
- uses: pre-commit-ci/lite-action@5d6cc0eb514c891a40562a58a8e71576c5c7fb43 # v1.1.0
if: env.HAS_SECRETS == 'false'
with:
msg: 🎨 Auto format
@@ -85,6 +90,6 @@ jobs:
GITHUB_CONTEXT: ${{ toJson(github) }}
run: echo "$GITHUB_CONTEXT"
- name: Decide whether the needed jobs succeeded or failed
uses: re-actors/alls-green@release/v1
uses: re-actors/alls-green@05ac9388f0aebcb5727afa17fcccfecd6f8ec5fe # v1.2.2
with:
jobs: ${{ toJSON(needs) }}

View File

@@ -5,6 +5,8 @@ on:
types:
- created
permissions: {}
jobs:
publish:
runs-on: ubuntu-latest
@@ -16,13 +18,17 @@ jobs:
env:
GITHUB_CONTEXT: ${{ toJson(github) }}
run: echo "$GITHUB_CONTEXT"
- uses: actions/checkout@v6
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: Set up Python
uses: actions/setup-python@v6
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
with:
python-version-file: ".python-version"
- name: Install uv
uses: astral-sh/setup-uv@v7
uses: astral-sh/setup-uv@37802adc94f370d6bfd71619e3f0bf239e1f3b78 # v7.6.0
with:
version: "0.11.4"
- name: Build distribution
run: uv build
- name: Publish

View File

@@ -1,34 +1,38 @@
name: Smokeshow
on:
workflow_run:
workflow_run: # zizmor: ignore[dangerous-triggers]
workflows: [Test]
types: [completed]
permissions:
statuses: write
permissions: {}
jobs:
smokeshow:
runs-on: ubuntu-latest
permissions:
statuses: write
steps:
- name: Dump GitHub context
env:
GITHUB_CONTEXT: ${{ toJson(github) }}
run: echo "$GITHUB_CONTEXT"
- uses: actions/checkout@v6
- uses: actions/setup-python@v6
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
with:
python-version-file: ".python-version"
- name: Setup uv
uses: astral-sh/setup-uv@v7
uses: astral-sh/setup-uv@37802adc94f370d6bfd71619e3f0bf239e1f3b78 # v7.6.0
with:
version: "0.11.4"
cache-dependency-glob: |
pyproject.toml
uv.lock
- run: uv sync --locked --no-dev --group github-actions
- uses: actions/download-artifact@v8
- uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
name: coverage-html
path: htmlcov
@@ -51,4 +55,4 @@ jobs:
SMOKESHOW_GITHUB_CONTEXT: coverage
SMOKESHOW_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
SMOKESHOW_GITHUB_PR_HEAD_SHA: ${{ github.event.workflow_run.head_sha }}
SMOKESHOW_AUTH_KEY: ${{ secrets.SMOKESHOW_AUTH_KEY }}
SMOKESHOW_AUTH_KEY: ${{ secrets.SMOKESHOW_AUTH_KEY }} # zizmor: ignore[secrets-outside-env]

View File

@@ -10,6 +10,8 @@ on:
required: false
default: "false"
permissions: {}
jobs:
job:
if: github.repository_owner == 'fastapi'
@@ -21,14 +23,17 @@ jobs:
env:
GITHUB_CONTEXT: ${{ toJson(github) }}
run: echo "$GITHUB_CONTEXT"
- uses: actions/checkout@v6
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: true # Required for `git push` in `sponsors.py`
- name: Set up Python
uses: actions/setup-python@v6
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
with:
python-version-file: ".python-version"
- name: Setup uv
uses: astral-sh/setup-uv@v7
uses: astral-sh/setup-uv@37802adc94f370d6bfd71619e3f0bf239e1f3b78 # v7.6.0
with:
version: "0.11.4"
enable-cache: true
cache-dependency-glob: |
pyproject.toml
@@ -37,12 +42,12 @@ jobs:
run: uv sync --locked --no-dev --group github-actions
# Allow debugging with tmate
- name: Setup tmate session
uses: mxschmitt/action-tmate@v3
uses: mxschmitt/action-tmate@c0afd6f790e3a5564914980036ebf83216678101 # v3.23
if: ${{ github.event_name == 'workflow_dispatch' && github.event.inputs.debug_enabled == 'true' }}
with:
limit-access-to-actor: true
- name: FastAPI People Sponsors
run: uv run ./scripts/sponsors.py
env:
SPONSORS_TOKEN: ${{ secrets.SPONSORS_TOKEN }}
PR_TOKEN: ${{ secrets.FASTAPI_PR_TOKEN }}
SPONSORS_TOKEN: ${{ secrets.SPONSORS_TOKEN }} # zizmor: ignore[secrets-outside-env]
PR_TOKEN: ${{ secrets.FASTAPI_PR_TOKEN }} # zizmor: ignore[secrets-outside-env]

View File

@@ -9,6 +9,8 @@ on:
- opened
- synchronize
permissions: {}
jobs:
test-redistribute:
runs-on: ubuntu-latest
@@ -17,9 +19,11 @@ jobs:
env:
GITHUB_CONTEXT: ${{ toJson(github) }}
run: echo "$GITHUB_CONTEXT"
- uses: actions/checkout@v6
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: Set up Python
uses: actions/setup-python@v6
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
with:
python-version-file: ".python-version"
- name: Install build dependencies
@@ -55,6 +59,6 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Decide whether the needed jobs succeeded or failed
uses: re-actors/alls-green@release/v1
uses: re-actors/alls-green@05ac9388f0aebcb5727afa17fcccfecd6f8ec5fe # v1.2.2
with:
jobs: ${{ toJSON(needs) }}

View File

@@ -12,6 +12,8 @@ on:
# cron every week on monday
- cron: "0 0 * * 1"
permissions: {}
env:
UV_NO_SYNC: true
INLINE_SNAPSHOT_DEFAULT_FLAGS: review
@@ -26,9 +28,11 @@ jobs:
outputs:
src: ${{ steps.filter.outputs.src }}
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
# For pull requests it's not necessary to checkout the code but for the main branch it is
- uses: dorny/paths-filter@v4
- uses: dorny/paths-filter@fbd0ab8f3e69293af611ebaee6363fc25e6d187d # v4.0.1
id: filter
with:
filters: |
@@ -49,7 +53,8 @@ jobs:
strategy:
matrix:
os: [ windows-latest, macos-latest ]
python-version: [ "3.14" ]
python-version: [ "3.14", "3.14t" ]
deprecated-tests: [ "no-deprecation" ]
uv-resolution:
- highest
starlette-src:
@@ -60,23 +65,33 @@ jobs:
python-version: "3.10"
coverage: coverage
uv-resolution: lowest-direct
deprecated-tests: "no-deprecation"
- os: windows-latest
python-version: "3.12"
coverage: coverage
uv-resolution: lowest-direct
deprecated-tests: "no-deprecation"
- os: ubuntu-latest
python-version: "3.13"
coverage: coverage
uv-resolution: highest
deprecated-tests: "no-deprecation"
- os: ubuntu-latest
python-version: "3.13"
uv-resolution: highest
codspeed: codspeed
deprecated-tests: "no-deprecation"
- os: ubuntu-latest
python-version: "3.14"
coverage: coverage
uv-resolution: highest
starlette-src: starlette-git
deprecated-tests: "test-deprecation"
- os: ubuntu-latest
python-version: "3.14t"
coverage: coverage
uv-resolution: highest
deprecated-tests: "no-deprecation"
fail-fast: false
runs-on: ${{ matrix.os }}
env:
@@ -88,14 +103,17 @@ jobs:
env:
GITHUB_CONTEXT: ${{ toJson(github) }}
run: echo "$GITHUB_CONTEXT"
- uses: actions/checkout@v6
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: Set up Python
uses: actions/setup-python@v6
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
with:
python-version: ${{ matrix.python-version }}
- name: Setup uv
uses: astral-sh/setup-uv@v7
uses: astral-sh/setup-uv@37802adc94f370d6bfd71619e3f0bf239e1f3b78 # v7.6.0
with:
version: "0.11.4"
enable-cache: true
cache-dependency-glob: |
pyproject.toml
@@ -108,18 +126,24 @@ jobs:
- name: Install Starlette from source
if: matrix.starlette-src == 'starlette-git'
run: uv pip install "git+https://github.com/Kludex/starlette@main"
- name: Install deprecated libraries just for testing
if: matrix.deprecated-tests == 'test-deprecation'
run: uv pip install orjson ujson
- name: Reinstall SQLAlchemy without Cython extensions
if: matrix.python-version == '3.14t' && matrix.os == 'ubuntu-latest'
run: "DISABLE_SQLALCHEMY_CEXT=1 uv pip install --force-reinstall --no-binary :all: sqlalchemy"
- run: mkdir coverage
- name: Test
run: uv run --no-sync bash scripts/test-cov.sh
env:
COVERAGE_FILE: coverage/.coverage.${{ runner.os }}-py${{ matrix.python-version }}
CONTEXT: ${{ runner.os }}-py${{ matrix.python-version }}
COVERAGE_FILE: coverage/.coverage.${{ runner.os }}-py${{ matrix.python-version }}-${{ matrix.deprecated-tests}}
CONTEXT: ${{ runner.os }}-py${{ matrix.python-version }}-${{ matrix.deprecated-tests}}
# Do not store coverage for all possible combinations to avoid file size max errors in Smokeshow
- name: Store coverage files
if: matrix.coverage == 'coverage'
uses: actions/upload-artifact@v7
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: coverage-${{ runner.os }}-${{ matrix.python-version }}-${{ hashFiles('**/coverage/.coverage.*') }}
name: coverage-${{ runner.os }}-${{ matrix.python-version }}-${{ matrix.deprecated-tests}}-${{ hashFiles('**/coverage/.coverage.*') }}
path: coverage
include-hidden-files: true
@@ -136,14 +160,17 @@ jobs:
env:
GITHUB_CONTEXT: ${{ toJson(github) }}
run: echo "$GITHUB_CONTEXT"
- uses: actions/checkout@v6
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: Set up Python
uses: actions/setup-python@v6
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
with:
python-version: "3.13"
- name: Setup uv
uses: astral-sh/setup-uv@v7
uses: astral-sh/setup-uv@37802adc94f370d6bfd71619e3f0bf239e1f3b78 # v7.6.0
with:
version: "0.11.4"
enable-cache: true
cache-dependency-glob: |
pyproject.toml
@@ -151,7 +178,7 @@ jobs:
- name: Install Dependencies
run: uv sync --no-dev --group tests --extra all
- name: CodSpeed benchmarks
uses: CodSpeedHQ/action@v4
uses: CodSpeedHQ/action@1c8ae4843586d3ba879736b7f6b7b0c990757fab # v4.12.1
with:
mode: simulation
run: uv run --no-sync pytest tests/benchmarks --codspeed
@@ -165,13 +192,16 @@ jobs:
env:
GITHUB_CONTEXT: ${{ toJson(github) }}
run: echo "$GITHUB_CONTEXT"
- uses: actions/checkout@v6
- uses: actions/setup-python@v6
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
with:
python-version-file: ".python-version"
- name: Setup uv
uses: astral-sh/setup-uv@v7
uses: astral-sh/setup-uv@37802adc94f370d6bfd71619e3f0bf239e1f3b78 # v7.6.0
with:
version: "0.11.4"
enable-cache: true
cache-dependency-glob: |
pyproject.toml
@@ -179,7 +209,7 @@ jobs:
- name: Install Dependencies
run: uv sync --locked --no-dev --group tests --extra all
- name: Get coverage files
uses: actions/download-artifact@v8
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
pattern: coverage-*
path: coverage
@@ -188,7 +218,7 @@ jobs:
- run: uv run coverage combine coverage
- run: uv run coverage html --title "Coverage for ${{ github.sha }}"
- name: Store coverage HTML
uses: actions/upload-artifact@v7
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: coverage-html
path: htmlcov
@@ -208,7 +238,7 @@ jobs:
GITHUB_CONTEXT: ${{ toJson(github) }}
run: echo "$GITHUB_CONTEXT"
- name: Decide whether the needed jobs succeeded or failed
uses: re-actors/alls-green@release/v1
uses: re-actors/alls-green@05ac9388f0aebcb5727afa17fcccfecd6f8ec5fe # v1.2.2
with:
jobs: ${{ toJSON(needs) }}
allowed-skips: coverage-combine,test,benchmark

View File

@@ -5,6 +5,8 @@ on:
- cron: "0 12 1 * *"
workflow_dispatch:
permissions: {}
jobs:
topic-repos:
if: github.repository_owner == 'fastapi'
@@ -16,14 +18,17 @@ jobs:
env:
GITHUB_CONTEXT: ${{ toJson(github) }}
run: echo "$GITHUB_CONTEXT"
- uses: actions/checkout@v6
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: true # Required for `git push` in `topic_repos.py`
- name: Set up Python
uses: actions/setup-python@v6
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
with:
python-version-file: ".python-version"
- name: Setup uv
uses: astral-sh/setup-uv@v7
uses: astral-sh/setup-uv@37802adc94f370d6bfd71619e3f0bf239e1f3b78 # v7.6.0
with:
version: "0.11.4"
enable-cache: true
cache-dependency-glob: |
pyproject.toml
@@ -33,4 +38,4 @@ jobs:
- name: Update Topic Repos
run: uv run ./scripts/topic_repos.py
env:
GITHUB_TOKEN: ${{ secrets.FASTAPI_PR_TOKEN }}
GITHUB_TOKEN: ${{ secrets.FASTAPI_PR_TOKEN }} # zizmor: ignore[secrets-outside-env]

View File

@@ -41,6 +41,8 @@ on:
required: false
default: 10
permissions: {}
jobs:
langs:
runs-on: ubuntu-latest
@@ -48,14 +50,17 @@ jobs:
langs: ${{ steps.show-langs.outputs.langs }}
commands: ${{ steps.show-langs.outputs.commands }}
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: Set up Python
uses: actions/setup-python@v6
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
with:
python-version-file: ".python-version"
- name: Setup uv
uses: astral-sh/setup-uv@v7
uses: astral-sh/setup-uv@37802adc94f370d6bfd71619e3f0bf239e1f3b78 # v7.6.0
with:
version: "0.11.4"
cache-dependency-glob: |
pyproject.toml
uv.lock
@@ -78,23 +83,23 @@ jobs:
matrix:
lang: ${{ fromJson(needs.langs.outputs.langs) }}
command: ${{ fromJson(needs.langs.outputs.commands) }}
permissions:
contents: write
steps:
- name: Dump GitHub context
env:
GITHUB_CONTEXT: ${{ toJson(github) }}
run: echo "$GITHUB_CONTEXT"
- uses: actions/checkout@v6
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
fetch-depth: 0
persist-credentials: false
- name: Set up Python
uses: actions/setup-python@v6
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
with:
python-version-file: ".python-version"
- name: Setup uv
uses: astral-sh/setup-uv@v7
uses: astral-sh/setup-uv@37802adc94f370d6bfd71619e3f0bf239e1f3b78 # v7.6.0
with:
version: "0.11.4"
cache-dependency-glob: |
pyproject.toml
uv.lock
@@ -102,20 +107,20 @@ jobs:
run: uv sync --locked --no-dev --group github-actions --group translations
# Allow debugging with tmate
- name: Setup tmate session
uses: mxschmitt/action-tmate@v3
uses: mxschmitt/action-tmate@c0afd6f790e3a5564914980036ebf83216678101 # v3.23
if: ${{ github.event_name == 'workflow_dispatch' && github.event.inputs.debug_enabled == 'true' }}
with:
limit-access-to-actor: true
env:
GITHUB_TOKEN: ${{ secrets.FASTAPI_TRANSLATIONS }}
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
GITHUB_TOKEN: ${{ secrets.FASTAPI_TRANSLATIONS }} # zizmor: ignore[secrets-outside-env]
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} # zizmor: ignore[secrets-outside-env]
- name: FastAPI Translate
run: |
uv run ./scripts/translate.py ${{ matrix.command }}
uv run ./scripts/translate.py "$COMMAND"
uv run ./scripts/translate.py make-pr
env:
GITHUB_TOKEN: ${{ secrets.FASTAPI_TRANSLATIONS }}
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
GITHUB_TOKEN: ${{ secrets.FASTAPI_TRANSLATIONS }} # zizmor: ignore[secrets-outside-env]
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} # zizmor: ignore[secrets-outside-env]
LANGUAGE: ${{ matrix.lang }}
EN_PATH: ${{ github.event.inputs.en_path }}
COMMAND: ${{ matrix.command }}

View File

@@ -2,7 +2,7 @@
# See https://pre-commit.com/hooks.html for more hooks
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v6.0.0
rev: 3e8a8703264a2f4a69428a0aa4dcb512790b2c8c # v6.0.0
hooks:
- id: check-added-large-files
args: ['--maxkb=750']
@@ -78,3 +78,18 @@ repos:
name: fix translations
entry: uv run ./scripts/translation_fixer.py fix-pages
files: ^docs/(?!en/).*/docs/.*\.md$
- id: add-release-date
language: unsupported
name: add date to latest release header
entry: uv run python scripts/add_latest_release_date.py
files: ^docs/en/docs/release-notes\.md$
pass_filenames: false
- id: zizmor
name: zizmor
language: python
entry: uv run zizmor .
files: ^\.github\/workflows\/
require_serial: true
pass_filenames: false

View File

@@ -54,7 +54,6 @@ The key features are:
<a href="https://blockbee.io?ref=fastapi" target="_blank" title="BlockBee Cryptocurrency Payment Gateway"><img src="https://fastapi.tiangolo.com/img/sponsors/blockbee.png"></a>
<a href="https://github.com/scalar/scalar/?utm_source=fastapi&utm_medium=website&utm_campaign=main-badge" target="_blank" title="Scalar: Beautiful Open-Source API References from Swagger/OpenAPI files"><img src="https://fastapi.tiangolo.com/img/sponsors/scalar.svg"></a>
<a href="https://www.propelauth.com/?utm_source=fastapi&utm_campaign=1223&utm_medium=mainbadge" target="_blank" title="Auth, user management and more for your B2B product"><img src="https://fastapi.tiangolo.com/img/sponsors/propelauth.png"></a>
<a href="https://zuplo.link/fastapi-gh" target="_blank" title="Zuplo: Deploy, Secure, Document, and Monetize your FastAPI"><img src="https://fastapi.tiangolo.com/img/sponsors/zuplo.png"></a>
<a href="https://liblab.com?utm_source=fastapi" target="_blank" title="liblab - Generate SDKs from FastAPI"><img src="https://fastapi.tiangolo.com/img/sponsors/liblab.png"></a>
<a href="https://docs.render.com/deploy-fastapi?utm_source=deploydoc&utm_medium=referral&utm_campaign=fastapi" target="_blank" title="Deploy & scale any full-stack web app on Render. Focus on building apps, not infra."><img src="https://fastapi.tiangolo.com/img/sponsors/render.svg"></a>
<a href="https://www.coderabbit.ai/?utm_source=fastapi&utm_medium=badge&utm_campaign=fastapi" target="_blank" title="Cut Code Review Time & Bugs in Half with CodeRabbit"><img src="https://fastapi.tiangolo.com/img/sponsors/coderabbit.png"></a>
@@ -63,7 +62,6 @@ The key features are:
<a href="https://serpapi.com/?utm_source=fastapi_website" target="_blank" title="SerpApi: Web Search API"><img src="https://fastapi.tiangolo.com/img/sponsors/serpapi.png"></a>
<a href="https://www.greptile.com/?utm_source=fastapi&utm_medium=sponsorship&utm_campaign=fastapi_sponsor_page" target="_blank" title="Greptile: The AI Code Reviewer"><img src="https://fastapi.tiangolo.com/img/sponsors/greptile.png"></a>
<a href="https://databento.com/?utm_source=fastapi&utm_medium=sponsor&utm_content=display" target="_blank" title="Pay as you go for market data"><img src="https://fastapi.tiangolo.com/img/sponsors/databento.svg"></a>
<a href="https://speakeasy.com/editor?utm_source=fastapi+repo&utm_medium=github+sponsorship" target="_blank" title="SDKs for your API | Speakeasy"><img src="https://fastapi.tiangolo.com/img/sponsors/speakeasy.png"></a>
<a href="https://www.svix.com/" target="_blank" title="Svix - Webhooks as a service"><img src="https://fastapi.tiangolo.com/img/sponsors/svix.svg"></a>
<a href="https://www.stainlessapi.com/?utm_source=fastapi&utm_medium=referral" target="_blank" title="Stainless | Generate best-in-class SDKs"><img src="https://fastapi.tiangolo.com/img/sponsors/stainless.png"></a>
<a href="https://www.permit.io/blog/implement-authorization-in-fastapi?utm_source=github&utm_medium=referral&utm_campaign=fastapi" target="_blank" title="Fine-Grained Authorization for FastAPI"><img src="https://fastapi.tiangolo.com/img/sponsors/permit.png"></a>

View File

@@ -12,9 +12,6 @@ gold:
- url: https://www.propelauth.com/?utm_source=fastapi&utm_campaign=1223&utm_medium=mainbadge
title: Auth, user management and more for your B2B product
img: https://fastapi.tiangolo.com/img/sponsors/propelauth.png
- url: https://zuplo.link/fastapi-gh
title: 'Zuplo: Deploy, Secure, Document, and Monetize your FastAPI'
img: https://fastapi.tiangolo.com/img/sponsors/zuplo.png
- url: https://liblab.com?utm_source=fastapi
title: liblab - Generate SDKs from FastAPI
img: https://fastapi.tiangolo.com/img/sponsors/liblab.png
@@ -40,9 +37,6 @@ silver:
- url: https://databento.com/?utm_source=fastapi&utm_medium=sponsor&utm_content=display
title: Pay as you go for market data
img: https://fastapi.tiangolo.com/img/sponsors/databento.svg
- url: https://speakeasy.com/editor?utm_source=fastapi+repo&utm_medium=github+sponsorship
title: SDKs for your API | Speakeasy
img: https://fastapi.tiangolo.com/img/sponsors/speakeasy.png
- url: https://www.svix.com/
title: Svix - Webhooks as a service
img: https://fastapi.tiangolo.com/img/sponsors/svix.svg

View File

@@ -30,7 +30,6 @@ Their sponsorship also demonstrates a strong commitment to the FastAPI **communi
For example, you might want to try:
* [Speakeasy](https://speakeasy.com/editor?utm_source=fastapi+repo&utm_medium=github+sponsorship)
* [Stainless](https://www.stainless.com/?utm_source=fastapi&utm_medium=referral)
* [liblab](https://developers.liblab.com/tutorials/sdk-for-fastapi?utm_source=fastapi)

View File

@@ -1,44 +0,0 @@
# Vibe Coding { #vibe-coding }
Are you tired of all that **data validation**, **documentation**, **serialization**, and all that **boring** stuff?
Do you just want to **vibe**? 🎶
**FastAPI** now supports a new `@app.vibe()` decorator that embraces **modern AI coding best practices**. 🤖
## How It Works { #how-it-works }
The `@app.vibe()` decorator is intended to receive **any HTTP method** (`GET`, `POST`, `PUT`, `DELETE`, `PATCH`, etc.) and **any payload**.
The body should be annotated with `Any`, because the request and the response would be... well... **anything**. 🤷
The idea is that you would receive the payload and send it **directly** to an LLM provider, using a `prompt` to tell the LLM what to do, and return the response **as is**. No questions asked.
You don't even need to write the body of the function. The `@app.vibe()` decorator does everything for you based on AI vibes:
{* ../../docs_src/vibe/tutorial001_py310.py hl[8:12] *}
## Benefits { #benefits }
By using `@app.vibe()`, you get to enjoy:
* **Freedom**: No data validation. No schemas. No constraints. Just vibes. ✨
* **Flexibility**: The request can be anything. The response can be anything. Who needs types anyway?
* **No documentation**: Why document your API when an LLM can figure it out? Auto-generated OpenAPI docs are *so* 2020.
* **No serialization**: Just pass the raw, unstructured data around. Serialization is for people who don't trust their LLMs.
* **Embrace modern AI coding practices**: Leave everything up to an LLM to decide. The model knows best. Always.
* **No code reviews**: There's no code to review. No PRs to approve. No comments to address. Embrace vibe coding fully, replace the theater of approving and merging vibe coded PRs that no one looks at with full proper vibes only.
/// tip
This is the ultimate **vibe-driven development** experience. You don't need to think about what your API does, just let the LLM handle it. 🧘
///
## Try It { #try-it }
Go ahead, try it:
{* ../../docs_src/vibe/tutorial001_py310.py *}
...and see what happens. 😎

View File

@@ -7,6 +7,58 @@ hide:
## Latest Changes
## 0.136.1 (2026-04-23)
### Upgrades
* ⬆️ Update Pydantic v2 code to address deprecations. PR [#15101](https://github.com/fastapi/fastapi/pull/15101) by [@svlandeg](https://github.com/svlandeg).
### Internal
* 🔨 Tweak translation script. PR [#15174](https://github.com/fastapi/fastapi/pull/15174) by [@YuriiMotov](https://github.com/YuriiMotov).
* ⬆ Bump mkdocs-material from 9.7.1 to 9.7.6. PR [#15408](https://github.com/fastapi/fastapi/pull/15408) by [@dependabot[bot]](https://github.com/apps/dependabot).
* ⬆ Bump inline-snapshot from 0.31.1 to 0.32.6. PR [#15409](https://github.com/fastapi/fastapi/pull/15409) by [@dependabot[bot]](https://github.com/apps/dependabot).
* ⬆ Bump pytest-codspeed from 4.3.0 to 4.4.0. PR [#15407](https://github.com/fastapi/fastapi/pull/15407) by [@dependabot[bot]](https://github.com/apps/dependabot).
* ⬆ Bump pytest-cov from 7.0.0 to 7.1.0. PR [#15406](https://github.com/fastapi/fastapi/pull/15406) by [@dependabot[bot]](https://github.com/apps/dependabot).
* ⬆ Bump cloudflare/wrangler-action from 3.14.1 to 3.15.0. PR [#15405](https://github.com/fastapi/fastapi/pull/15405) by [@dependabot[bot]](https://github.com/apps/dependabot).
* ⬆ Bump mypy from 1.19.1 to 1.20.1. PR [#15410](https://github.com/fastapi/fastapi/pull/15410) by [@dependabot[bot]](https://github.com/apps/dependabot).
* ⬆ Bump python-dotenv from 1.2.1 to 1.2.2. PR [#15400](https://github.com/fastapi/fastapi/pull/15400) by [@dependabot[bot]](https://github.com/apps/dependabot).
* ⬆ Bump starlette from 0.52.1 to 1.0.0. PR [#15397](https://github.com/fastapi/fastapi/pull/15397) by [@dependabot[bot]](https://github.com/apps/dependabot).
* ⬆ Bump pygithub from 2.8.1 to 2.9.1. PR [#15396](https://github.com/fastapi/fastapi/pull/15396) by [@dependabot[bot]](https://github.com/apps/dependabot).
* ⬆ Bump pyjwt from 2.12.0 to 2.12.1. PR [#15393](https://github.com/fastapi/fastapi/pull/15393) by [@dependabot[bot]](https://github.com/apps/dependabot).
* ⬆ Bump zizmor from 1.23.1 to 1.24.1. PR [#15394](https://github.com/fastapi/fastapi/pull/15394) by [@dependabot[bot]](https://github.com/apps/dependabot).
* ⬆ Bump strawberry-graphql from 0.312.3 to 0.314.3. PR [#15395](https://github.com/fastapi/fastapi/pull/15395) by [@dependabot[bot]](https://github.com/apps/dependabot).
* ⬆ Bump python-multipart from 0.0.22 to 0.0.26. PR [#15360](https://github.com/fastapi/fastapi/pull/15360) by [@dependabot[bot]](https://github.com/apps/dependabot).
* ⬆ Bump authlib from 1.6.9 to 1.6.11. PR [#15373](https://github.com/fastapi/fastapi/pull/15373) by [@dependabot[bot]](https://github.com/apps/dependabot).
* ⬆ Bump aiohttp from 3.13.3 to 3.13.4. PR [#15282](https://github.com/fastapi/fastapi/pull/15282) by [@dependabot[bot]](https://github.com/apps/dependabot).
* ⬆ Bump pygments from 2.19.2 to 2.20.0. PR [#15263](https://github.com/fastapi/fastapi/pull/15263) by [@dependabot[bot]](https://github.com/apps/dependabot).
* ⬆ Bump pymdown-extensions from 10.20.1 to 10.21.2. PR [#15391](https://github.com/fastapi/fastapi/pull/15391) by [@YuriiMotov](https://github.com/YuriiMotov).
* ⬆ Bump pillow from 12.1.1 to 12.2.0. PR [#15333](https://github.com/fastapi/fastapi/pull/15333) by [@dependabot[bot]](https://github.com/apps/dependabot).
* ⬆ Bump pytest from 9.0.2 to 9.0.3. PR [#15334](https://github.com/fastapi/fastapi/pull/15334) by [@dependabot[bot]](https://github.com/apps/dependabot).
* ⬆ Bump actions/upload-artifact from 7.0.0 to 7.0.1. PR [#15374](https://github.com/fastapi/fastapi/pull/15374) by [@dependabot[bot]](https://github.com/apps/dependabot).
* ⬆ Bump actions/cache from 5.0.4 to 5.0.5. PR [#15385](https://github.com/fastapi/fastapi/pull/15385) by [@dependabot[bot]](https://github.com/apps/dependabot).
* 🔧 Update sponsors: remove Zuplo. PR [#15369](https://github.com/fastapi/fastapi/pull/15369) by [@tiangolo](https://github.com/tiangolo).
* 🔧 Update sponsors: remove Speakeasy. PR [#15368](https://github.com/fastapi/fastapi/pull/15368) by [@tiangolo](https://github.com/tiangolo).
* 🔒️ Add zizmor and fix audit findings. PR [#15316](https://github.com/fastapi/fastapi/pull/15316) by [@YuriiMotov](https://github.com/YuriiMotov).
## 0.136.0 (2026-04-16)
### Upgrades
* ⬆️ Support free-threaded Python 3.14t. PR [#15149](https://github.com/fastapi/fastapi/pull/15149) by [@svlandeg](https://github.com/svlandeg).
## 0.135.4 (2026-04-16)
### Refactors
* 🔥 Remove April Fool's `@app.vibe()` 🤪. PR [#15363](https://github.com/fastapi/fastapi/pull/15363) by [@tiangolo](https://github.com/tiangolo).
### Internal
* ⬆ Bump cryptography from 46.0.5 to 46.0.7. PR [#15314](https://github.com/fastapi/fastapi/pull/15314) by [@dependabot[bot]](https://github.com/apps/dependabot).
* ⬆ Bump strawberry-graphql from 0.307.1 to 0.312.3. PR [#15309](https://github.com/fastapi/fastapi/pull/15309) by [@dependabot[bot]](https://github.com/apps/dependabot).
* 🔨 Add pre-commit hook to ensure latest release header has date. PR [#15293](https://github.com/fastapi/fastapi/pull/15293) by [@YuriiMotov](https://github.com/YuriiMotov).
## 0.135.3 (2026-04-01)
### Features

View File

@@ -197,7 +197,6 @@ nav:
- advanced/advanced-python-types.md
- advanced/json-base64-bytes.md
- advanced/strict-content-type.md
- advanced/vibe.md
- fastapi-cli.md
- editor-support.md
- Deployment:

View File

@@ -51,12 +51,6 @@
<img class="sponsor-image" src="/img/sponsors/propelauth-banner.png" />
</a>
</div>
<div class="item">
<a title="Zuplo: Scale, Protect, Document, and Monetize your FastAPI" style="display: block; position: relative;" href="https://zuplo.link/fastapi-web" target="_blank">
<span class="sponsor-badge">sponsor</span>
<img class="sponsor-image" src="/img/sponsors/zuplo-banner.png" />
</a>
</div>
<div class="item">
<a title="liblab - Generate SDKs from FastAPI" style="display: block; position: relative;" href="https://liblab.com?utm_source=fastapi" target="_blank">
<span class="sponsor-badge">sponsor</span>

View File

@@ -1,12 +0,0 @@
from typing import Any
from fastapi import FastAPI
app = FastAPI()
@app.vibe(
"/vibe/",
prompt="pls return json of users from database. make no mistakes",
)
async def ai_vibes(body: Any): ...

View File

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

View File

@@ -26,7 +26,7 @@ from .v2 import Undefined as Undefined
from .v2 import Url as Url
from .v2 import copy_field_info as copy_field_info
from .v2 import create_body_model as create_body_model
from .v2 import evaluate_forwardref as evaluate_forwardref # ty: ignore[deprecated]
from .v2 import evaluate_forwardref as evaluate_forwardref
from .v2 import get_cached_model_fields as get_cached_model_fields
from .v2 import get_definitions as get_definitions
from .v2 import get_flat_models_from_fields as get_flat_models_from_fields

View File

@@ -22,10 +22,10 @@ from pydantic import BaseModel, ConfigDict, Field, TypeAdapter, create_model
from pydantic import PydanticSchemaGenerationError as PydanticSchemaGenerationError
from pydantic import PydanticUndefinedAnnotation as PydanticUndefinedAnnotation
from pydantic import ValidationError as ValidationError
from pydantic._internal import _typing_extra as _pydantic_typing_extra
from pydantic._internal._schema_generation_shared import ( # type: ignore[attr-defined] # ty: ignore[unused-ignore-comment]
GetJsonSchemaHandler as GetJsonSchemaHandler,
)
from pydantic._internal._typing_extra import eval_type_lenient # ty: ignore[deprecated]
from pydantic.fields import FieldInfo as FieldInfo
from pydantic.json_schema import GenerateJsonSchema as _GenerateJsonSchema
from pydantic.json_schema import JsonSchemaValue as JsonSchemaValue
@@ -38,7 +38,20 @@ from pydantic_core.core_schema import (
RequiredParam = PydanticUndefined
Undefined = PydanticUndefined
evaluate_forwardref = eval_type_lenient # ty: ignore[deprecated]
def evaluate_forwardref(
value: Any,
globalns: dict[str, Any] | None = None,
localns: dict[str, Any] | None = None,
) -> Any:
# eval_type_lenient has been deprecated since Pydantic v2.10.0b1 (PR #10530)
try_eval_type = getattr(_pydantic_typing_extra, "try_eval_type", None)
if try_eval_type is not None:
return try_eval_type(value, globalns, localns)[0]
return _pydantic_typing_extra.eval_type_lenient( # ty: ignore[deprecated]
value, globalns, localns
)
class GenerateJsonSchema(_GenerateJsonSchema):

View File

@@ -10,11 +10,7 @@ from fastapi.exception_handlers import (
request_validation_exception_handler,
websocket_request_validation_exception_handler,
)
from fastapi.exceptions import (
FastAPIError,
RequestValidationError,
WebSocketRequestValidationError,
)
from fastapi.exceptions import RequestValidationError, WebSocketRequestValidationError
from fastapi.logger import logger
from fastapi.middleware.asyncexitstack import AsyncExitStackMiddleware
from fastapi.openapi.docs import (
@@ -4563,60 +4559,6 @@ class FastAPI(Starlette):
generate_unique_id_function=generate_unique_id_function,
)
def vibe(
self,
path: Annotated[
str,
Doc(
"""
The URL path to be used for this *path operation*.
For example, in `http://example.com/vibes`, the path is `/vibes`.
"""
),
],
*,
prompt: Annotated[
str,
Doc(
"""
The prompt to send to the LLM provider along with the payload.
This tells the LLM what to do with the request body.
"""
),
] = "",
) -> Callable[[DecoratedCallable], DecoratedCallable]:
"""
Add a *vibe coding path operation* that receives any HTTP method
and any payload.
It's intended to receive the request and send the payload directly
to an LLM provider, and return the response as is.
Embrace the freedom and flexibility of not having any data validation,
documentation, or serialization.
## Example
```python
from typing import Any
from fastapi import FastAPI
app = FastAPI()
@app.vibe(
"/vibe/",
prompt="pls return json of users from database. make no mistakes",
)
async def ai_vibes(body: Any):
...
```
"""
raise FastAPIError("Are you kidding me? Happy April Fool's")
def websocket_route(
self, path: str, name: str | None = None
) -> Callable[[DecoratedCallable], DecoratedCallable]:

View File

@@ -33,7 +33,7 @@ from fastapi._compat import (
Undefined,
copy_field_info,
create_body_model,
evaluate_forwardref, # ty: ignore[deprecated]
evaluate_forwardref,
field_annotation_is_scalar,
field_annotation_is_scalar_sequence,
field_annotation_is_sequence,
@@ -245,7 +245,7 @@ def get_typed_signature(call: Callable[..., Any]) -> inspect.Signature:
def get_typed_annotation(annotation: Any, globalns: dict[str, Any]) -> Any:
if isinstance(annotation, str):
annotation = ForwardRef(annotation)
annotation = evaluate_forwardref(annotation, globalns, globalns) # ty: ignore[deprecated]
annotation = evaluate_forwardref(annotation, globalns, globalns)
if annotation is type(None):
return None
return annotation

View File

@@ -22,7 +22,6 @@ from annotated_doc import Doc
from fastapi.exceptions import PydanticV1NotSupportedError
from fastapi.types import IncEx
from pydantic import BaseModel
from pydantic.color import Color # ty: ignore[deprecated]
from pydantic.networks import AnyUrl, NameEmail
from pydantic.types import SecretBytes, SecretStr
from pydantic_core import PydanticUndefinedType
@@ -32,6 +31,23 @@ from ._compat import (
is_pydantic_v1_model_instance,
)
try:
# pydantic.color.Color is deprecated since v2.0b3, but supporting for bwd-compat
from pydantic.color import Color # ty: ignore[deprecated]
except ImportError: # pragma: no cover
class Color: # type: ignore[no-redef] # ty: ignore[unused-ignore-comment]
pass
try:
# Supporting the new Color format for newer versions of Pydantic
from pydantic_extra_types.color import Color as PyExtraColor
except ImportError: # pragma: no cover
class PyExtraColor: # type: ignore[no-redef] # ty: ignore[unused-ignore-comment]
pass
# Taken from Pydantic v1 as is
def isoformat(o: datetime.date | datetime.time) -> str:
@@ -67,7 +83,8 @@ def decimal_encoder(dec_value: Decimal) -> int | float:
ENCODERS_BY_TYPE: dict[type[Any], Callable[[Any], Any]] = {
bytes: lambda o: o.decode(),
Color: str, # ty: ignore[deprecated]
Color: str,
PyExtraColor: str,
datetime.date: isoformat,
datetime.datetime: isoformat,
datetime.time: isoformat,

View File

@@ -1,4 +1,5 @@
from typing import Any
import importlib
from typing import Any, Protocol, cast
from fastapi.exceptions import FastAPIDeprecationWarning
from fastapi.sse import EventSourceResponse as EventSourceResponse # noqa
@@ -11,16 +12,28 @@ from starlette.responses import Response as Response # noqa
from starlette.responses import StreamingResponse as StreamingResponse # noqa
from typing_extensions import deprecated
try:
import ujson
except ImportError: # pragma: nocover
ujson = None # type: ignore
class _UjsonModule(Protocol):
def dumps(self, __obj: Any, *, ensure_ascii: bool = ...) -> str: ...
class _OrjsonModule(Protocol):
OPT_NON_STR_KEYS: int
OPT_SERIALIZE_NUMPY: int
def dumps(self, __obj: Any, *, option: int = ...) -> bytes: ...
try:
import orjson
except ImportError: # pragma: nocover
orjson = None # type: ignore
ujson = cast(_UjsonModule, importlib.import_module("ujson"))
except ModuleNotFoundError: # pragma: nocover
ujson = None # type: ignore # ty: ignore[unused-ignore-comment]
try:
orjson = cast(_OrjsonModule, importlib.import_module("orjson"))
except ModuleNotFoundError: # pragma: nocover
orjson = None # type: ignore # ty: ignore[unused-ignore-comment]
@deprecated(

View File

@@ -59,6 +59,7 @@ Changelog = "https://fastapi.tiangolo.com/release-notes/"
[project.optional-dependencies]
standard = [
"fastapi-cli[standard] >=0.0.8",
"fastar >= 0.9.0",
# For the test client
"httpx >=0.23.0,<1.0.0",
# For templates
@@ -125,6 +126,7 @@ dev = [
{ include-group = "translations" },
"playwright >=1.57.0",
"prek >=0.2.22",
"zizmor >=1.23.1",
]
docs = [
{ include-group = "docs-tests" },
@@ -149,10 +151,6 @@ docs = [
docs-tests = [
"httpx >=0.23.0,<1.0.0",
"ruff >=0.14.14",
# For UJSONResponse
"ujson >=5.8.0",
# For ORJSONResponse
"orjson >=3.9.3",
]
github-actions = [
"httpx >=0.27.0,<1.0.0",
@@ -178,8 +176,6 @@ tests = [
"sqlmodel >=0.0.31",
"strawberry-graphql >=0.200.0,<1.0.0",
"ty>=0.0.9",
"types-orjson >=3.6.2",
"types-ujson >=5.10.0.20240515",
"a2wsgi >=1.9.0,<=2.0.0",
"pytest-xdist[psutil]>=2.5.0",
"pytest-cov>=4.0.0",

View File

@@ -0,0 +1,40 @@
"""Check release-notes.md and add today's date to the latest release header if missing."""
import re
import sys
from datetime import date
RELEASE_NOTES_FILE = "docs/en/docs/release-notes.md"
RELEASE_HEADER_PATTERN = re.compile(r"^## (\d+\.\d+\.\d+)\s*(\(.*\))?\s*$")
def main() -> None:
with open(RELEASE_NOTES_FILE) as f:
lines = f.readlines()
for i, line in enumerate(lines):
match = RELEASE_HEADER_PATTERN.match(line)
if not match:
continue
version = match.group(1)
date_part = match.group(2)
if date_part:
print(f"Latest release {version} already has a date: {date_part}")
sys.exit(0)
today = date.today().isoformat()
lines[i] = f"## {version} ({today})\n"
print(f"Added date: {version} ({today})")
with open(RELEASE_NOTES_FILE, "w") as f:
f.writelines(lines)
sys.exit(0)
print("No release header found")
sys.exit(1)
if __name__ == "__main__":
main()

View File

@@ -6,6 +6,8 @@ The original content is written in Markdown, write the translation in Markdown a
The original content will be surrounded by triple percentage signs (%%%). Do not include the triple percentage signs in the translation.
[placeholder_for_additional_instructions]
### Technical terms in English
For technical terms in English that don't have a common translation term, use the original term in English.
@@ -223,6 +225,8 @@ Result (German):
Use the following rules for links (apply both to Markdown-style links ([text](url)) and to HTML-style <a href="url">text</a> tags):
- The order of links should match the order of links in the English source. Do not change the order of links. Rephrase the sentence if necessary.
- For relative URLs, only translate the link text. Do not translate the URL or its parts.
Example:

View File

@@ -57,6 +57,51 @@ def generate_en_path(*, lang: str, path: Path) -> Path:
return out_path
def get_prompt(
lang_prompt_content: str,
old_translation: str | None,
language: str,
language_name: str,
original_content: str,
additional_instructions: str,
) -> str:
general_prompt_with_additional_instructions = general_prompt.replace(
"[placeholder_for_additional_instructions]", additional_instructions
)
prompt_segments = [
general_prompt_with_additional_instructions,
lang_prompt_content,
]
if old_translation:
prompt_segments.extend(
[
"There is an existing previous translation for the original English content, that may be outdated.",
"Update the translation only where necessary:",
"- If the original English content has added parts, also add these parts to the translation.",
"- If the original English content has removed parts, also remove them from the translation, unless you were instructed earlier to not do that in specific cases.",
"- If parts of the original English content have changed, also change those parts in the translation.",
"- If the previous translation violates current instructions, update it.",
"- Otherwise, preserve the original translation LINE-BY-LINE, AS-IS.",
"Do not:",
"- rephrase or rewrite correct lines just to improve the style.",
"- add or remove line breaks, unless the original English content changed.",
"- change formatting or whitespace unless absolutely required.",
"Only change what must be changed. The goal is to minimize diffs for easier human review.",
"UNLESS you were instructed earlier to behave different, there MUST NOT be whole sentences or partial sentences in the updated translation, which are not in the original English content, and there MUST NOT be whole sentences or partial sentences in the original English content, which are not in the updated translation. Remember: the updated translation shall be IN SYNC with the original English content.",
"Previous translation:",
f"%%%\n{old_translation}%%%",
]
)
prompt_segments.extend(
[
f"Translate to {language} ({language_name}).",
"Original content:",
f"%%%\n{original_content}%%%",
]
)
return "\n\n".join(prompt_segments)
@app.command()
def translate_page(
*,
@@ -88,43 +133,23 @@ def translate_page(
print(f"Translating {en_path} to {language} ({language_name})")
agent = Agent("openai:gpt-5")
prompt_segments = [
general_prompt,
lang_prompt_content,
]
if old_translation:
prompt_segments.extend(
[
"There is an existing previous translation for the original English content, that may be outdated.",
"Update the translation only where necessary:",
"- If the original English content has added parts, also add these parts to the translation.",
"- If the original English content has removed parts, also remove them from the translation, unless you were instructed earlier to not do that in specific cases.",
"- If parts of the original English content have changed, also change those parts in the translation.",
"- If the previous translation violates current instructions, update it.",
"- Otherwise, preserve the original translation LINE-BY-LINE, AS-IS.",
"Do not:",
"- rephrase or rewrite correct lines just to improve the style.",
"- add or remove line breaks, unless the original English content changed.",
"- change formatting or whitespace unless absolutely required.",
"Only change what must be changed. The goal is to minimize diffs for easier human review.",
"UNLESS you were instructed earlier to behave different, there MUST NOT be whole sentences or partial sentences in the updated translation, which are not in the original English content, and there MUST NOT be whole sentences or partial sentences in the original English content, which are not in the updated translation. Remember: the updated translation shall be IN SYNC with the original English content.",
"Previous translation:",
f"%%%\n{old_translation}%%%",
]
)
prompt_segments.extend(
[
f"Translate to {language} ({language_name}).",
"Original content:",
f"%%%\n{original_content}%%%",
]
)
prompt = "\n\n".join(prompt_segments)
MAX_ATTEMPTS = 3
additional_instructions = ""
for attempt_no in range(1, MAX_ATTEMPTS + 1):
print(f"Running agent for {out_path} (attempt {attempt_no}/{MAX_ATTEMPTS})")
result = agent.run_sync(prompt)
prompt = get_prompt(
lang_prompt_content=lang_prompt_content,
old_translation=old_translation,
language=language,
language_name=language_name,
original_content=original_content,
additional_instructions=additional_instructions,
)
result = agent.run_sync(
prompt.replace(
"[placeholder_for_additional_instructions]", additional_instructions
)
)
out_content = f"{result.output.strip()}\n"
try:
check_translation(
@@ -139,6 +164,11 @@ def translate_page(
print(
f"Translation check failed on attempt {attempt_no}/{MAX_ATTEMPTS}: {e}"
)
additional_instructions = (
f"Current translation fails validation checks ({str(e)}). "
"Please, pay special attention to it."
)
old_translation = out_content
continue # Retry if not reached max attempts
else: # Max retry attempts reached
print(f"Translation failed for {out_path} after {MAX_ATTEMPTS} attempts")

View File

@@ -1,15 +1,18 @@
from typing import Any
import orjson
from fastapi import APIRouter, FastAPI
from fastapi.responses import HTMLResponse, JSONResponse, PlainTextResponse
from fastapi.testclient import TestClient
from tests.utils import needs_orjson
class ORJSONResponse(JSONResponse):
media_type = "application/x-orjson"
def render(self, content: Any) -> bytes:
import orjson
return orjson.dumps(content)
@@ -118,6 +121,7 @@ html_type = "text/html; charset=utf-8"
override_type = "application/x-override"
@needs_orjson
def test_app():
with client:
response = client.get("/")
@@ -132,6 +136,7 @@ def test_app_override():
assert response.headers["content-type"] == text_type
@needs_orjson
def test_router_a():
with client:
response = client.get("/a")
@@ -146,6 +151,7 @@ def test_router_a_override():
assert response.headers["content-type"] == text_type
@needs_orjson
def test_router_a_a():
with client:
response = client.get("/a/a")

View File

@@ -7,6 +7,8 @@ from fastapi.responses import ORJSONResponse, UJSONResponse
from fastapi.testclient import TestClient
from pydantic import BaseModel
from tests.utils import needs_orjson, needs_ujson
class Item(BaseModel):
name: str
@@ -28,6 +30,7 @@ def _make_orjson_app() -> FastAPI:
return app
@needs_orjson
def test_orjson_response_returns_correct_data():
app = _make_orjson_app()
client = TestClient(app)
@@ -38,6 +41,7 @@ def test_orjson_response_returns_correct_data():
assert response.json() == {"name": "widget", "price": 9.99}
@needs_orjson
def test_orjson_response_emits_deprecation_warning():
with pytest.warns(FastAPIDeprecationWarning, match="ORJSONResponse is deprecated"):
ORJSONResponse(content={"hello": "world"})
@@ -58,6 +62,7 @@ def _make_ujson_app() -> FastAPI:
return app
@needs_ujson
def test_ujson_response_returns_correct_data():
app = _make_ujson_app()
client = TestClient(app)
@@ -68,6 +73,7 @@ def test_ujson_response_returns_correct_data():
assert response.json() == {"name": "widget", "price": 9.99}
@needs_ujson
def test_ujson_response_emits_deprecation_warning():
with pytest.warns(FastAPIDeprecationWarning, match="UJSONResponse is deprecated"):
UJSONResponse(content={"hello": "world"})

View File

@@ -311,3 +311,21 @@ def test_encode_deque_encodes_child_models():
def test_encode_pydantic_undefined():
data = {"value": Undefined}
assert jsonable_encoder(data) == {"value": None}
@pytest.mark.filterwarnings("ignore::DeprecationWarning")
@pytest.mark.parametrize(
"module_path",
[
pytest.param("pydantic.color"),
pytest.param("pydantic_extra_types.color"),
],
)
def test_encode_color(module_path):
try:
Color = __import__(module_path, fromlist=["Color"]).Color
except ImportError: # pragma: no cover
pytest.skip(f"{module_path} not available")
data = {"color": Color("blue")}
assert jsonable_encoder(data) == {"color": "blue"}

View File

@@ -1,5 +1,9 @@
import warnings
import pytest
pytest.importorskip("orjson")
from fastapi import FastAPI
from fastapi.exceptions import FastAPIDeprecationWarning
from fastapi.responses import ORJSONResponse

View File

@@ -4,6 +4,8 @@ import pytest
from fastapi.testclient import TestClient
from inline_snapshot import snapshot
pytest.importorskip("orjson")
@pytest.fixture(
name="client",

View File

@@ -11,6 +11,8 @@ with warnings.catch_warnings():
client = TestClient(app)
pytest.importorskip("orjson")
@pytest.mark.filterwarnings("ignore::fastapi.exceptions.FastAPIDeprecationWarning")
def test_get_custom_response():

View File

@@ -1,5 +1,8 @@
import pytest
from fastapi.testclient import TestClient
pytest.importorskip("orjson")
from docs_src.custom_response.tutorial009c_py310 import app
client = TestClient(app)

View File

@@ -1,17 +0,0 @@
from typing import Any
import pytest
from fastapi import FastAPI
from fastapi.exceptions import FastAPIError
def test_vibe_raises():
with pytest.raises(FastAPIError, match="Are you kidding me"):
app = FastAPI()
@app.vibe(
"/vibe/",
prompt="pls return json of users from database. make no mistakes",
)
async def ai_vibes(body: Any): # pragma: nocover
pass

View File

@@ -1,3 +1,4 @@
import importlib
import sys
import pytest
@@ -9,6 +10,16 @@ needs_py314 = pytest.mark.skipif(
sys.version_info < (3, 14), reason="requires python3.14+"
)
needs_orjson = pytest.mark.skipif(
importlib.util.find_spec("orjson") is None,
reason="requires orjson",
)
needs_ujson = pytest.mark.skipif(
importlib.util.find_spec("ujson") is None,
reason="requires ujson",
)
workdir_lock = pytest.mark.xdist_group("workdir_lock")

1283
uv.lock generated
View File

File diff suppressed because it is too large Load Diff