Browse Source

Merge branch 'master' into fix/ref

pull/14361/head
Sofie Van Landeghem 8 months ago
committed by GitHub
parent
commit
c5e6fb8fcb
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 1
      .github/labeler.yml
  2. 22
      .github/workflows/build-docs.yml
  3. 2
      .github/workflows/contributors.yml
  4. 2
      .github/workflows/deploy-docs.yml
  5. 2
      .github/workflows/label-approved.yml
  6. 2
      .github/workflows/latest-changes.yml
  7. 2
      .github/workflows/notify-translations.yml
  8. 2
      .github/workflows/people.yml
  9. 88
      .github/workflows/pre-commit.yml
  10. 2
      .github/workflows/publish.yml
  11. 2
      .github/workflows/smokeshow.yml
  12. 2
      .github/workflows/sponsors.yml
  13. 2
      .github/workflows/test-redistribute.yml
  14. 6
      .github/workflows/test.yml
  15. 2
      .github/workflows/topic-repos.yml
  16. 2
      .github/workflows/translate.yml
  17. 3
      .gitignore
  18. 34
      .pre-commit-config.yaml
  19. 8
      docs/en/docs/css/custom.css
  20. 2
      docs/en/docs/css/termynal.css
  21. 2
      docs/en/docs/deployment/cloud.md
  22. 17
      docs/en/docs/release-notes.md
  23. 1
      docs/en/mkdocs.env.yml
  24. 10
      docs/en/mkdocs.insiders.yml
  25. 0
      docs/en/mkdocs.no-insiders.yml
  26. 10
      docs/en/mkdocs.yml
  27. 2
      fastapi/__init__.py
  28. 3
      fastapi/dependencies/utils.py
  29. 4
      fastapi/params.py
  30. 2
      pyproject.toml
  31. 3
      requirements-docs-insiders.txt
  32. 4
      requirements-docs.txt
  33. 2
      requirements.txt
  34. 130
      scripts/docs.py
  35. 25
      tests/test_depends_hashable.py
  36. 2
      tests/test_tutorial/test_sql_databases/test_tutorial001.py
  37. 2
      tests/test_tutorial/test_sql_databases/test_tutorial002.py

1
.github/labeler.yml

@ -17,6 +17,7 @@ lang-all:
- docs/*/docs/** - docs/*/docs/**
- all-globs-to-all-files: - all-globs-to-all-files:
- '!docs/en/docs/**' - '!docs/en/docs/**'
- '!docs/*/**/_*.md'
- '!fastapi/**' - '!fastapi/**'
- '!pyproject.toml' - '!pyproject.toml'

22
.github/workflows/build-docs.yml

@ -21,7 +21,7 @@ jobs:
outputs: outputs:
docs: ${{ steps.filter.outputs.docs }} docs: ${{ steps.filter.outputs.docs }}
steps: steps:
- uses: actions/checkout@v5 - uses: actions/checkout@v6
# For pull requests it's not necessary to checkout the code but for the main branch it is # For pull requests it's not necessary to checkout the code but for the main branch it is
- uses: dorny/paths-filter@v3 - uses: dorny/paths-filter@v3
id: filter id: filter
@ -32,12 +32,9 @@ jobs:
- docs/** - docs/**
- docs_src/** - docs_src/**
- requirements-docs.txt - requirements-docs.txt
- requirements-docs-insiders.txt
- pyproject.toml - pyproject.toml
- mkdocs.yml - mkdocs.yml
- mkdocs.insiders.yml - mkdocs.env.yml
- mkdocs.maybe-insiders.yml
- mkdocs.no-insiders.yml
- .github/workflows/build-docs.yml - .github/workflows/build-docs.yml
- .github/workflows/deploy-docs.yml - .github/workflows/deploy-docs.yml
- scripts/mkdocs_hooks.py - scripts/mkdocs_hooks.py
@ -48,7 +45,7 @@ jobs:
outputs: outputs:
langs: ${{ steps.show-langs.outputs.langs }} langs: ${{ steps.show-langs.outputs.langs }}
steps: steps:
- uses: actions/checkout@v5 - uses: actions/checkout@v6
- name: Set up Python - name: Set up Python
uses: actions/setup-python@v6 uses: actions/setup-python@v6
with: with:
@ -63,12 +60,6 @@ jobs:
pyproject.toml pyproject.toml
- name: Install docs extras - name: Install docs extras
run: uv pip install -r requirements-docs.txt run: uv pip install -r requirements-docs.txt
# Install MkDocs Material Insiders here just to put it in the cache for the rest of the steps
- name: Install Material for MkDocs Insiders
if: ( github.event_name != 'pull_request' || github.secret_source == 'Actions' )
run: uv pip install -r requirements-docs-insiders.txt
env:
TOKEN: ${{ secrets.FASTAPI_MKDOCS_MATERIAL_INSIDERS }}
- name: Verify Docs - name: Verify Docs
run: python ./scripts/docs.py verify-docs run: python ./scripts/docs.py verify-docs
- name: Export Language Codes - name: Export Language Codes
@ -90,7 +81,7 @@ jobs:
env: env:
GITHUB_CONTEXT: ${{ toJson(github) }} GITHUB_CONTEXT: ${{ toJson(github) }}
run: echo "$GITHUB_CONTEXT" run: echo "$GITHUB_CONTEXT"
- uses: actions/checkout@v5 - uses: actions/checkout@v6
- name: Set up Python - name: Set up Python
uses: actions/setup-python@v6 uses: actions/setup-python@v6
with: with:
@ -105,11 +96,6 @@ jobs:
pyproject.toml pyproject.toml
- name: Install docs extras - name: Install docs extras
run: uv pip install -r requirements-docs.txt run: uv pip install -r requirements-docs.txt
- name: Install Material for MkDocs Insiders
if: ( github.event_name != 'pull_request' || github.secret_source == 'Actions' )
run: uv pip install -r requirements-docs-insiders.txt
env:
TOKEN: ${{ secrets.FASTAPI_MKDOCS_MATERIAL_INSIDERS }}
- name: Update Languages - name: Update Languages
run: python ./scripts/docs.py update-languages run: python ./scripts/docs.py update-languages
- uses: actions/cache@v4 - uses: actions/cache@v4

2
.github/workflows/contributors.yml

@ -24,7 +24,7 @@ jobs:
env: env:
GITHUB_CONTEXT: ${{ toJson(github) }} GITHUB_CONTEXT: ${{ toJson(github) }}
run: echo "$GITHUB_CONTEXT" run: echo "$GITHUB_CONTEXT"
- uses: actions/checkout@v5 - uses: actions/checkout@v6
- name: Set up Python - name: Set up Python
uses: actions/setup-python@v6 uses: actions/setup-python@v6
with: with:

2
.github/workflows/deploy-docs.yml

@ -23,7 +23,7 @@ jobs:
env: env:
GITHUB_CONTEXT: ${{ toJson(github) }} GITHUB_CONTEXT: ${{ toJson(github) }}
run: echo "$GITHUB_CONTEXT" run: echo "$GITHUB_CONTEXT"
- uses: actions/checkout@v5 - uses: actions/checkout@v6
- name: Set up Python - name: Set up Python
uses: actions/setup-python@v6 uses: actions/setup-python@v6
with: with:

2
.github/workflows/label-approved.yml

@ -20,7 +20,7 @@ jobs:
env: env:
GITHUB_CONTEXT: ${{ toJson(github) }} GITHUB_CONTEXT: ${{ toJson(github) }}
run: echo "$GITHUB_CONTEXT" run: echo "$GITHUB_CONTEXT"
- uses: actions/checkout@v5 - uses: actions/checkout@v6
- name: Set up Python - name: Set up Python
uses: actions/setup-python@v6 uses: actions/setup-python@v6
with: with:

2
.github/workflows/latest-changes.yml

@ -24,7 +24,7 @@ jobs:
env: env:
GITHUB_CONTEXT: ${{ toJson(github) }} GITHUB_CONTEXT: ${{ toJson(github) }}
run: echo "$GITHUB_CONTEXT" run: echo "$GITHUB_CONTEXT"
- uses: actions/checkout@v5 - uses: actions/checkout@v6
with: with:
# To allow latest-changes to commit to the main branch # To allow latest-changes to commit to the main branch
token: ${{ secrets.FASTAPI_LATEST_CHANGES }} token: ${{ secrets.FASTAPI_LATEST_CHANGES }}

2
.github/workflows/notify-translations.yml

@ -28,7 +28,7 @@ jobs:
env: env:
GITHUB_CONTEXT: ${{ toJson(github) }} GITHUB_CONTEXT: ${{ toJson(github) }}
run: echo "$GITHUB_CONTEXT" run: echo "$GITHUB_CONTEXT"
- uses: actions/checkout@v5 - uses: actions/checkout@v6
- name: Set up Python - name: Set up Python
uses: actions/setup-python@v6 uses: actions/setup-python@v6
with: with:

2
.github/workflows/people.yml

@ -24,7 +24,7 @@ jobs:
env: env:
GITHUB_CONTEXT: ${{ toJson(github) }} GITHUB_CONTEXT: ${{ toJson(github) }}
run: echo "$GITHUB_CONTEXT" run: echo "$GITHUB_CONTEXT"
- uses: actions/checkout@v5 - uses: actions/checkout@v6
- name: Set up Python - name: Set up Python
uses: actions/setup-python@v6 uses: actions/setup-python@v6
with: with:

88
.github/workflows/pre-commit.yml

@ -0,0 +1,88 @@
name: pre-commit
on:
pull_request:
types:
- opened
- synchronize
env:
IS_FORK: ${{ github.event.pull_request.head.repo.full_name != github.repository }}
jobs:
pre-commit:
runs-on: ubuntu-latest
steps:
- name: Dump GitHub context
env:
GITHUB_CONTEXT: ${{ toJson(github) }}
run: echo "$GITHUB_CONTEXT"
- uses: actions/checkout@v5
name: Checkout PR for own repo
if: env.IS_FORK == 'false'
with:
# To be able to commit it needs more than the last commit
ref: ${{ github.head_ref }}
# A token other than the default GITHUB_TOKEN is needed to be able to trigger CI
token: ${{ secrets.PRE_COMMIT }}
# pre-commit lite ci needs the default checkout configs to work
- uses: actions/checkout@v5
name: Checkout PR for fork
if: env.IS_FORK == 'true'
- name: Set up Python
uses: actions/setup-python@v6
with:
python-version: "3.14"
- name: Setup uv
uses: astral-sh/setup-uv@v7
with:
cache-dependency-glob: |
requirements**.txt
pyproject.toml
uv.lock
- name: Install Dependencies
run: |
uv venv
uv pip install -r requirements.txt
- name: Run pre-commit
id: precommit
run: |
# Fetch the base branch for comparison
git fetch origin ${{ github.base_ref }}
uvx pre-commit 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.IS_FORK == 'false'
run: |
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
git add -A
if git diff --staged --quiet; then
echo "No changes to commit"
else
git commit -m "🎨 Auto format"
git push
fi
- uses: pre-commit-ci/[email protected]
if: env.IS_FORK == 'true'
with:
msg: 🎨 Auto format
- name: Error out on pre-commit errors
if: steps.precommit.outcome == 'failure'
run: exit 1
# https://github.com/marketplace/actions/alls-green#why
pre-commit-alls-green: # This job does nothing and is only used for the branch protection
if: always()
needs:
- pre-commit
runs-on: ubuntu-latest
steps:
- name: Dump GitHub context
env:
GITHUB_CONTEXT: ${{ toJson(github) }}
run: echo "$GITHUB_CONTEXT"
- name: Decide whether the needed jobs succeeded or failed
uses: re-actors/alls-green@release/v1
with:
jobs: ${{ toJSON(needs) }}

2
.github/workflows/publish.yml

@ -20,7 +20,7 @@ jobs:
env: env:
GITHUB_CONTEXT: ${{ toJson(github) }} GITHUB_CONTEXT: ${{ toJson(github) }}
run: echo "$GITHUB_CONTEXT" run: echo "$GITHUB_CONTEXT"
- uses: actions/checkout@v5 - uses: actions/checkout@v6
- name: Set up Python - name: Set up Python
uses: actions/setup-python@v6 uses: actions/setup-python@v6
with: with:

2
.github/workflows/smokeshow.yml

@ -21,7 +21,7 @@ jobs:
env: env:
GITHUB_CONTEXT: ${{ toJson(github) }} GITHUB_CONTEXT: ${{ toJson(github) }}
run: echo "$GITHUB_CONTEXT" run: echo "$GITHUB_CONTEXT"
- uses: actions/checkout@v5 - uses: actions/checkout@v6
- uses: actions/setup-python@v6 - uses: actions/setup-python@v6
with: with:
python-version: '3.9' python-version: '3.9'

2
.github/workflows/sponsors.yml

@ -24,7 +24,7 @@ jobs:
env: env:
GITHUB_CONTEXT: ${{ toJson(github) }} GITHUB_CONTEXT: ${{ toJson(github) }}
run: echo "$GITHUB_CONTEXT" run: echo "$GITHUB_CONTEXT"
- uses: actions/checkout@v5 - uses: actions/checkout@v6
- name: Set up Python - name: Set up Python
uses: actions/setup-python@v6 uses: actions/setup-python@v6
with: with:

2
.github/workflows/test-redistribute.yml

@ -22,7 +22,7 @@ jobs:
env: env:
GITHUB_CONTEXT: ${{ toJson(github) }} GITHUB_CONTEXT: ${{ toJson(github) }}
run: echo "$GITHUB_CONTEXT" run: echo "$GITHUB_CONTEXT"
- uses: actions/checkout@v5 - uses: actions/checkout@v6
- name: Set up Python - name: Set up Python
uses: actions/setup-python@v6 uses: actions/setup-python@v6
with: with:

6
.github/workflows/test.yml

@ -23,7 +23,7 @@ jobs:
env: env:
GITHUB_CONTEXT: ${{ toJson(github) }} GITHUB_CONTEXT: ${{ toJson(github) }}
run: echo "$GITHUB_CONTEXT" run: echo "$GITHUB_CONTEXT"
- uses: actions/checkout@v5 - uses: actions/checkout@v6
- name: Set up Python - name: Set up Python
uses: actions/setup-python@v6 uses: actions/setup-python@v6
with: with:
@ -65,7 +65,7 @@ jobs:
env: env:
GITHUB_CONTEXT: ${{ toJson(github) }} GITHUB_CONTEXT: ${{ toJson(github) }}
run: echo "$GITHUB_CONTEXT" run: echo "$GITHUB_CONTEXT"
- uses: actions/checkout@v5 - uses: actions/checkout@v6
- name: Set up Python - name: Set up Python
uses: actions/setup-python@v6 uses: actions/setup-python@v6
with: with:
@ -111,7 +111,7 @@ jobs:
env: env:
GITHUB_CONTEXT: ${{ toJson(github) }} GITHUB_CONTEXT: ${{ toJson(github) }}
run: echo "$GITHUB_CONTEXT" run: echo "$GITHUB_CONTEXT"
- uses: actions/checkout@v5 - uses: actions/checkout@v6
- uses: actions/setup-python@v6 - uses: actions/setup-python@v6
with: with:
python-version: '3.8' python-version: '3.8'

2
.github/workflows/topic-repos.yml

@ -19,7 +19,7 @@ jobs:
env: env:
GITHUB_CONTEXT: ${{ toJson(github) }} GITHUB_CONTEXT: ${{ toJson(github) }}
run: echo "$GITHUB_CONTEXT" run: echo "$GITHUB_CONTEXT"
- uses: actions/checkout@v5 - uses: actions/checkout@v6
- name: Set up Python - name: Set up Python
uses: actions/setup-python@v6 uses: actions/setup-python@v6
with: with:

2
.github/workflows/translate.yml

@ -42,7 +42,7 @@ jobs:
env: env:
GITHUB_CONTEXT: ${{ toJson(github) }} GITHUB_CONTEXT: ${{ toJson(github) }}
run: echo "$GITHUB_CONTEXT" run: echo "$GITHUB_CONTEXT"
- uses: actions/checkout@v5 - uses: actions/checkout@v6
- name: Set up Python - name: Set up Python
uses: actions/setup-python@v6 uses: actions/setup-python@v6
with: with:

3
.gitignore

@ -28,3 +28,6 @@ archive.zip
# macOS # macOS
.DS_Store .DS_Store
# Ignore while the setup still depends on requirements.txt files
uv.lock

34
.pre-commit-config.yaml

@ -1,25 +1,29 @@
# See https://pre-commit.com for more information # See https://pre-commit.com for more information
# See https://pre-commit.com/hooks.html for more hooks # See https://pre-commit.com/hooks.html for more hooks
default_language_version:
python: python3.10
repos: repos:
- repo: https://github.com/pre-commit/pre-commit-hooks - repo: https://github.com/pre-commit/pre-commit-hooks
rev: v6.0.0 rev: v6.0.0
hooks: hooks:
- id: check-added-large-files - id: check-added-large-files
- id: check-toml - id: check-toml
- id: check-yaml - id: check-yaml
args: args:
- --unsafe - --unsafe
- id: end-of-file-fixer - id: end-of-file-fixer
- id: trailing-whitespace - id: trailing-whitespace
- repo: https://github.com/astral-sh/ruff-pre-commit - repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.14.3 rev: v0.14.3
hooks: hooks:
- id: ruff - id: ruff
args: args:
- --fix - --fix
- id: ruff-format - id: ruff-format
ci: - repo: local
autofix_commit_msg: 🎨 [pre-commit.ci] Auto format from pre-commit.com hooks hooks:
autoupdate_commit_msg: ⬆ [pre-commit.ci] pre-commit autoupdate - id: local-script
language: unsupported
name: local script
entry: uv run ./scripts/docs.py add-permalinks-pages
args:
- --update-existing
files: ^docs/en/docs/.*\.md$

8
docs/en/docs/css/custom.css

@ -1,3 +1,11 @@
/* Fira Code, including characters used by Rich output, like the "heavy right-pointing angle bracket ornament", not included in Google Fonts */
@import url(https://cdn.jsdelivr.net/npm/firacode@6.2.0/distr/fira_code.css);
/* Override default code font in Material for MkDocs to Fira Code */
:root {
--md-code-font: "Fira Code", monospace;
}
.termynal-comment { .termynal-comment {
color: #4a968f; color: #4a968f;
font-style: italic; font-style: italic;

2
docs/en/docs/css/termynal.css

@ -20,7 +20,7 @@
/* font-size: 18px; */ /* font-size: 18px; */
font-size: 15px; font-size: 15px;
/* font-family: 'Fira Mono', Consolas, Menlo, Monaco, 'Courier New', Courier, monospace; */ /* font-family: 'Fira Mono', Consolas, Menlo, Monaco, 'Courier New', Courier, monospace; */
font-family: 'Roboto Mono', 'Fira Mono', Consolas, Menlo, Monaco, 'Courier New', Courier, monospace; font-family: var(--md-code-font-family), 'Roboto Mono', 'Fira Mono', Consolas, Menlo, Monaco, 'Courier New', Courier, monospace;
border-radius: 4px; border-radius: 4px;
padding: 75px 45px 35px; padding: 75px 45px 35px;
position: relative; position: relative;

2
docs/en/docs/deployment/cloud.md

@ -4,7 +4,7 @@ You can use virtually **any cloud provider** to deploy your FastAPI application.
In most of the cases, the main cloud providers have guides to deploy FastAPI with them. In most of the cases, the main cloud providers have guides to deploy FastAPI with them.
## FastAPI Cloud ## FastAPI Cloud { #fastapi-cloud }
**<a href="https://fastapicloud.com" class="external-link" target="_blank">FastAPI Cloud</a>** is built by the same author and team behind **FastAPI**. **<a href="https://fastapicloud.com" class="external-link" target="_blank">FastAPI Cloud</a>** is built by the same author and team behind **FastAPI**.

17
docs/en/docs/release-notes.md

@ -7,8 +7,25 @@ hide:
## Latest Changes ## Latest Changes
### Internal
* 🛠️ Add `add-permalinks` and `add-permalinks-page` to `scripts/docs.py`. PR [#14033](https://github.com/fastapi/fastapi/pull/14033) by [@YuriiMotov](https://github.com/YuriiMotov).
* 🔧 Upgrade Material for MkDocs and remove insiders. PR [#14375](https://github.com/fastapi/fastapi/pull/14375) by [@tiangolo](https://github.com/tiangolo).
## 0.121.3
### Refactors
* ♻️ Make the result of `Depends()` and `Security()` hashable, as a workaround for other tools interacting with these internal parts. PR [#14372](https://github.com/fastapi/fastapi/pull/14372) by [@tiangolo](https://github.com/tiangolo).
### Upgrades
* ⬆️ Bump Starlette to <`0.51.0`. PR [#14282](https://github.com/fastapi/fastapi/pull/14282) by [@musicinmybrain](https://github.com/musicinmybrain).
### Docs ### Docs
* 📝 Add missing hash part. PR [#14369](https://github.com/fastapi/fastapi/pull/14369) by [@nilslindemann](https://github.com/nilslindemann).
* 📝 Fix typos in code comments. PR [#14364](https://github.com/fastapi/fastapi/pull/14364) by [@Edge-Seven](https://github.com/Edge-Seven).
* 📝 Add docs for using FastAPI Cloud. PR [#14359](https://github.com/fastapi/fastapi/pull/14359) by [@tiangolo](https://github.com/tiangolo). * 📝 Add docs for using FastAPI Cloud. PR [#14359](https://github.com/fastapi/fastapi/pull/14359) by [@tiangolo](https://github.com/tiangolo).
## 0.121.2 ## 0.121.2

1
docs/en/mkdocs.maybe-insiders.yml → docs/en/mkdocs.env.yml

@ -1,6 +1,5 @@
# Define this here and not in the main mkdocs.yml file because that one is auto # Define this here and not in the main mkdocs.yml file because that one is auto
# updated and written, and the script would remove the env var # updated and written, and the script would remove the env var
INHERIT: !ENV [INSIDERS_FILE, '../en/mkdocs.no-insiders.yml']
markdown_extensions: markdown_extensions:
pymdownx.highlight: pymdownx.highlight:
linenums: !ENV [LINENUMS, false] linenums: !ENV [LINENUMS, false]

10
docs/en/mkdocs.insiders.yml

@ -1,10 +0,0 @@
plugins:
social:
cards_layout_options:
logo: ../en/docs/img/icon-white.svg
typeset:
markdown_extensions:
material.extensions.preview:
targets:
include:
- "*"

0
docs/en/mkdocs.no-insiders.yml

10
docs/en/mkdocs.yml

@ -1,4 +1,4 @@
INHERIT: ../en/mkdocs.maybe-insiders.yml INHERIT: ../en/mkdocs.env.yml
site_name: FastAPI site_name: FastAPI
site_description: FastAPI framework, high performance, easy to learn, fast to code, ready for production site_description: FastAPI framework, high performance, easy to learn, fast to code, ready for production
site_url: https://fastapi.tiangolo.com/ site_url: https://fastapi.tiangolo.com/
@ -52,6 +52,10 @@ theme:
repo_name: fastapi/fastapi repo_name: fastapi/fastapi
repo_url: https://github.com/fastapi/fastapi repo_url: https://github.com/fastapi/fastapi
plugins: plugins:
social:
cards_layout_options:
logo: ../en/docs/img/icon-white.svg
typeset:
search: null search: null
macros: macros:
include_yaml: include_yaml:
@ -253,6 +257,10 @@ nav:
- management.md - management.md
- release-notes.md - release-notes.md
markdown_extensions: markdown_extensions:
material.extensions.preview:
targets:
include:
- "*"
abbr: null abbr: null
attr_list: null attr_list: null
footnotes: null footnotes: null

2
fastapi/__init__.py

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

3
fastapi/dependencies/utils.py

@ -1,3 +1,4 @@
import dataclasses
import inspect import inspect
from contextlib import AsyncExitStack, contextmanager from contextlib import AsyncExitStack, contextmanager
from copy import copy, deepcopy from copy import copy, deepcopy
@ -428,7 +429,7 @@ def analyze_param(
if depends is not None and depends.dependency is None: if depends is not None and depends.dependency is None:
# Copy `depends` before mutating it # Copy `depends` before mutating it
depends = copy(depends) depends = copy(depends)
depends.dependency = type_annotation depends = dataclasses.replace(depends, dependency=type_annotation)
# Handle non-param type annotations like Request # Handle non-param type annotations like Request
if lenient_issubclass( if lenient_issubclass(

4
fastapi/params.py

@ -762,13 +762,13 @@ class File(Form): # type: ignore[misc]
) )
@dataclass @dataclass(frozen=True)
class Depends: class Depends:
dependency: Optional[Callable[..., Any]] = None dependency: Optional[Callable[..., Any]] = None
use_cache: bool = True use_cache: bool = True
scope: Union[Literal["function", "request"], None] = None scope: Union[Literal["function", "request"], None] = None
@dataclass @dataclass(frozen=True)
class Security(Depends): class Security(Depends):
scopes: Optional[Sequence[str]] = None scopes: Optional[Sequence[str]] = None

2
pyproject.toml

@ -45,7 +45,7 @@ classifiers = [
"Topic :: Internet :: WWW/HTTP", "Topic :: Internet :: WWW/HTTP",
] ]
dependencies = [ dependencies = [
"starlette>=0.40.0,<0.50.0", "starlette>=0.40.0,<0.51.0",
"pydantic>=1.7.4,!=1.8,!=1.8.1,!=2.0.0,!=2.0.1,!=2.1.0,<3.0.0", "pydantic>=1.7.4,!=1.8,!=1.8.1,!=2.0.0,!=2.0.1,!=2.1.0,<3.0.0",
"typing-extensions>=4.8.0", "typing-extensions>=4.8.0",
"annotated-doc>=0.0.2", "annotated-doc>=0.0.2",

3
requirements-docs-insiders.txt

@ -1,3 +0,0 @@
git+https://${TOKEN}@github.com/squidfunk/[email protected]
git+https://${TOKEN}@github.com/pawamoy-insiders/griffe-typing-deprecated.git
git+https://${TOKEN}@github.com/pawamoy-insiders/mkdocstrings-python.git

4
requirements-docs.txt

@ -1,6 +1,6 @@
-e . -e .
-r requirements-docs-tests.txt -r requirements-docs-tests.txt
mkdocs-material==9.6.16 mkdocs-material==9.7.0
mdx-include >=1.4.1,<2.0.0 mdx-include >=1.4.1,<2.0.0
mkdocs-redirects>=1.2.1,<1.3.0 mkdocs-redirects>=1.2.1,<1.3.0
typer == 0.16.0 typer == 0.16.0
@ -13,7 +13,9 @@ pillow==11.3.0
cairosvg==2.8.2 cairosvg==2.8.2
mkdocstrings[python]==0.30.1 mkdocstrings[python]==0.30.1
griffe-typingdoc==0.3.0 griffe-typingdoc==0.3.0
griffe-warnings-deprecated==1.1.0
# For griffe, it formats with black # For griffe, it formats with black
black==25.1.0 black==25.1.0
mkdocs-macros-plugin==1.4.1 mkdocs-macros-plugin==1.4.1
markdown-include-variants==0.0.5 markdown-include-variants==0.0.5
python-slugify==8.0.4

2
requirements.txt

@ -1,6 +1,6 @@
-e .[all] -e .[all]
-r requirements-tests.txt -r requirements-tests.txt
-r requirements-docs.txt -r requirements-docs.txt
pre-commit >=2.17.0,<5.0.0 pre-commit >=4.5.0,<5.0.0
# For generating screenshots # For generating screenshots
playwright playwright

130
scripts/docs.py

@ -4,9 +4,8 @@ import os
import re import re
import shutil import shutil
import subprocess import subprocess
from functools import lru_cache from html.parser import HTMLParser
from http.server import HTTPServer, SimpleHTTPRequestHandler from http.server import HTTPServer, SimpleHTTPRequestHandler
from importlib import metadata
from multiprocessing import Pool from multiprocessing import Pool
from pathlib import Path from pathlib import Path
from typing import Any, Dict, List, Optional, Union from typing import Any, Dict, List, Optional, Union
@ -16,6 +15,7 @@ import typer
import yaml import yaml
from jinja2 import Template from jinja2 import Template
from ruff.__main__ import find_ruff_bin from ruff.__main__ import find_ruff_bin
from slugify import slugify as py_slugify
logging.basicConfig(level=logging.INFO) logging.basicConfig(level=logging.INFO)
@ -27,8 +27,8 @@ missing_translation_snippet = """
{!../../docs/missing-translation.md!} {!../../docs/missing-translation.md!}
""" """
non_translated_sections = [ non_translated_sections = (
"reference/", f"reference{os.sep}",
"release-notes.md", "release-notes.md",
"fastapi-people.md", "fastapi-people.md",
"external-links.md", "external-links.md",
@ -36,7 +36,7 @@ non_translated_sections = [
"management-tasks.md", "management-tasks.md",
"management.md", "management.md",
"contributing.md", "contributing.md",
] )
docs_path = Path("docs") docs_path = Path("docs")
en_docs_path = Path("docs/en") en_docs_path = Path("docs/en")
@ -44,13 +44,39 @@ en_config_path: Path = en_docs_path / mkdocs_name
site_path = Path("site").absolute() site_path = Path("site").absolute()
build_site_path = Path("site_build").absolute() build_site_path = Path("site_build").absolute()
header_pattern = re.compile(r"^(#{1,6}) (.+?)(?:\s*\{\s*(#.*)\s*\})?\s*$")
header_with_permalink_pattern = re.compile(r"^(#{1,6}) (.+?)(\s*\{\s*#.*\s*\})\s*$") header_with_permalink_pattern = re.compile(r"^(#{1,6}) (.+?)(\s*\{\s*#.*\s*\})\s*$")
code_block3_pattern = re.compile(r"^\s*```")
code_block4_pattern = re.compile(r"^\s*````")
@lru_cache class VisibleTextExtractor(HTMLParser):
def is_mkdocs_insiders() -> bool: """Extract visible text from a string with HTML tags."""
version = metadata.version("mkdocs-material")
return "insiders" in version def __init__(self):
super().__init__()
self.text_parts = []
def handle_data(self, data):
self.text_parts.append(data)
def extract_visible_text(self, html: str) -> str:
self.reset()
self.text_parts = []
self.feed(html)
return "".join(self.text_parts).strip()
def slugify(text: str) -> str:
return py_slugify(
text,
replacements=[
("`", ""), # `dict`s -> dicts
("'s", "s"), # it's -> its
("'t", "t"), # don't -> dont
("**", ""), # **FastAPI**s -> FastAPIs
],
)
def get_en_config() -> Dict[str, Any]: def get_en_config() -> Dict[str, Any]:
@ -77,9 +103,7 @@ def complete_existing_lang(incomplete: str):
@app.callback() @app.callback()
def callback() -> None: def callback() -> None:
if is_mkdocs_insiders(): # For MacOS with Cairo
os.environ["INSIDERS_FILE"] = "../en/mkdocs.insiders.yml"
# For MacOS with insiders and Cairo
os.environ["DYLD_FALLBACK_LIBRARY_PATH"] = "/opt/homebrew/lib" os.environ["DYLD_FALLBACK_LIBRARY_PATH"] = "/opt/homebrew/lib"
@ -115,10 +139,6 @@ def build_lang(
""" """
Build the docs for a language. Build the docs for a language.
""" """
insiders_env_file = os.environ.get("INSIDERS_FILE")
print(f"Insiders file {insiders_env_file}")
if is_mkdocs_insiders():
print("Using insiders")
lang_path: Path = Path("docs") / lang lang_path: Path = Path("docs") / lang
if not lang_path.is_dir(): if not lang_path.is_dir():
typer.echo(f"The language translation doesn't seem to exist yet: {lang}") typer.echo(f"The language translation doesn't seem to exist yet: {lang}")
@ -440,5 +460,83 @@ def generate_docs_src_versions_for_file(file_path: Path) -> None:
version_file.write_text(content_format, encoding="utf-8") version_file.write_text(content_format, encoding="utf-8")
@app.command()
def add_permalinks_page(path: Path, update_existing: bool = False):
"""
Add or update header permalinks in specific page of En docs.
"""
if not path.is_relative_to(en_docs_path / "docs"):
raise RuntimeError(f"Path must be inside {en_docs_path}")
rel_path = path.relative_to(en_docs_path / "docs")
# Skip excluded sections
if str(rel_path).startswith(non_translated_sections):
return
visible_text_extractor = VisibleTextExtractor()
updated_lines = []
in_code_block3 = False
in_code_block4 = False
permalinks = set()
with path.open("r", encoding="utf-8") as f:
lines = f.readlines()
for line in lines:
# Handle codeblocks start and end
if not (in_code_block3 or in_code_block4):
if code_block4_pattern.match(line):
in_code_block4 = True
elif code_block3_pattern.match(line):
in_code_block3 = True
else:
if in_code_block4 and code_block4_pattern.match(line):
in_code_block4 = False
elif in_code_block3 and code_block3_pattern.match(line):
in_code_block3 = False
# Process Headers only outside codeblocks
if not (in_code_block3 or in_code_block4):
match = header_pattern.match(line)
if match:
hashes, title, _permalink = match.groups()
if (not _permalink) or update_existing:
slug = slugify(visible_text_extractor.extract_visible_text(title))
if slug in permalinks:
# If the slug is already used, append a number to make it unique
count = 1
original_slug = slug
while slug in permalinks:
slug = f"{original_slug}_{count}"
count += 1
permalinks.add(slug)
line = f"{hashes} {title} {{ #{slug} }}\n"
updated_lines.append(line)
with path.open("w", encoding="utf-8") as f:
f.writelines(updated_lines)
@app.command()
def add_permalinks_pages(pages: List[Path], update_existing: bool = False) -> None:
"""
Add or update header permalinks in specific pages of En docs.
"""
for md_file in pages:
add_permalinks_page(md_file, update_existing=update_existing)
@app.command()
def add_permalinks(update_existing: bool = False) -> None:
"""
Add or update header permalinks in all pages of En docs.
"""
for md_file in en_docs_path.rglob("*.md"):
add_permalinks_page(md_file, update_existing=update_existing)
if __name__ == "__main__": if __name__ == "__main__":
app() app()

25
tests/test_depends_hashable.py

@ -0,0 +1,25 @@
# This is more or less a workaround to make Depends and Security hashable
# as other tools that use them depend on that
# Ref: https://github.com/fastapi/fastapi/pull/14320
from fastapi import Depends, Security
def dep():
pass
def test_depends_hashable():
dep() # just for coverage
d1 = Depends(dep)
d2 = Depends(dep)
d3 = Depends(dep, scope="function")
d4 = Depends(dep, scope="function")
s1 = Security(dep)
s2 = Security(dep)
assert hash(d1) == hash(d2)
assert hash(s1) == hash(s2)
assert hash(d1) != hash(d3)
assert hash(d3) == hash(d4)

2
tests/test_tutorial/test_sql_databases/test_tutorial001.py

@ -45,7 +45,7 @@ def get_client(request: pytest.FixtureRequest):
with TestClient(mod.app) as c: with TestClient(mod.app) as c:
yield c yield c
# Clean up connection explicitely to avoid resource warning # Clean up connection explicitly to avoid resource warning
mod.engine.dispose() mod.engine.dispose()

2
tests/test_tutorial/test_sql_databases/test_tutorial002.py

@ -45,7 +45,7 @@ def get_client(request: pytest.FixtureRequest):
with TestClient(mod.app) as c: with TestClient(mod.app) as c:
yield c yield c
# Clean up connection explicitely to avoid resource warning # Clean up connection explicitly to avoid resource warning
mod.engine.dispose() mod.engine.dispose()

Loading…
Cancel
Save