- docs_src/**
- pyproject.toml
- uv.lock
- - mkdocs.yml
- - mkdocs.env.yml
- .github/workflows/build-docs.yml
- .github/workflows/deploy-docs.yml
- - scripts/mkdocs_hooks.py
+ - scripts/docs.py
langs:
needs:
- changes
+ if: ${{ needs.changes.outputs.docs == 'true' }}
runs-on: ubuntu-latest
outputs:
langs: ${{ steps.show-langs.outputs.langs }}
run: uv run ./scripts/docs.py update-languages
- uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
with:
- key: mkdocs-cards-${{ matrix.lang }}-${{ github.ref }}
- path: docs/${{ matrix.lang }}/.cache
+ key: zensical-${{ matrix.lang }}-${{ github.ref }}
+ path: site_zensical_src/${{ matrix.lang }}/.cache
- name: Build Docs
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/**
+ # English owns root static assets. Translated pages reference /img, /css,
+ # and /js, so omit duplicated language-local copies from artifacts.
+ path: |
+ ./site/**
+ !./site/${{ matrix.lang }}/img/**
+ !./site/${{ matrix.lang }}/css/**
+ !./site/${{ matrix.lang }}/js/**
include-hidden-files: true
# https://github.com/marketplace/actions/alls-green#why
docs-all-green: # This job does nothing and is only used for the branch protection
if: always()
needs:
+ - langs
- build-docs
runs-on: ubuntu-latest
steps:
uses: re-actors/alls-green@05ac9388f0aebcb5727afa17fcccfecd6f8ec5fe # v1.2.2
with:
jobs: ${{ toJSON(needs) }}
- allowed-skips: build-docs
+ allowed-skips: langs, build-docs
htmlcov
dist
site
+site_zensical_src
.coverage*
coverage.xml
.netlify
+---
+include_yaml:
+ sponsors: data/sponsors.yml
+---
+
# FastAPI { #fastapi }
<style>
+++ /dev/null
-INHERIT: ../en/mkdocs.yml
$ cd docs/en/
```
-Then run `mkdocs` in that directory:
+Then run `zensical` in that directory:
```console
-$ mkdocs serve --dev-addr 127.0.0.1:8008
+$ zensical serve --dev-addr 127.0.0.1:8008
```
///
### Docs Structure
-The documentation uses [MkDocs](https://www.mkdocs.org/).
+The documentation uses [Zensical](https://zensical.org).
And there are extra tools/scripts in place to handle translations in `./scripts/docs.py`.
+---
+include_yaml:
+ topic_repos: data/topic_repos.yml
+---
+
# External Links
**FastAPI** has a great community constantly growing.
---
hide:
- navigation
+
+include_yaml:
+ github_sponsors: data/github_sponsors.yml
+ people: data/people.yml
+ contributors: data/contributors.yml
+ translation_reviewers: data/translation_reviewers.yml
+ skip_users: data/skip_users.yml
+ members: data/members.yml
+ sponsors_badge: data/sponsors_badge.yml
+ sponsors: data/sponsors.yml
---
# FastAPI People
+---
+include_yaml:
+ sponsors: data/sponsors.yml
+---
+
# FastAPI { #fastapi }
<style>
+++ /dev/null
-# 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
-markdown_extensions:
- pymdownx.highlight:
- linenums: !ENV [LINENUMS, false]
-INHERIT: ../en/mkdocs.env.yml
site_name: FastAPI
site_description: FastAPI framework, high performance, easy to learn, fast to code, ready for production
site_url: https://fastapi.tiangolo.com/
theme:
+ variant: classic
name: material
- custom_dir: ../en/overrides
+ custom_dir: overrides
palette:
- media: (prefers-color-scheme)
toggle:
- search.suggest
- toc.follow
icon:
- repo: fontawesome/brands/github-alt
+ repo: octicons/mark-github-24
logo: img/icon-white.svg
favicon: img/favicon.png
language: en
repo_name: fastapi/fastapi
repo_url: https://github.com/fastapi/fastapi
plugins:
- social:
- cards_layout_options:
- logo: ../en/docs/img/icon-white.svg
- typeset: null
- search: null
- macros:
- include_yaml:
- - github_sponsors: ../en/data/github_sponsors.yml
- - people: ../en/data/people.yml
- - contributors: ../en/data/contributors.yml
- - translators: ../en/data/translators.yml
- - translation_reviewers: ../en/data/translation_reviewers.yml
- - skip_users: ../en/data/skip_users.yml
- - members: ../en/data/members.yml
- - sponsors_badge: ../en/data/sponsors_badge.yml
- - sponsors: ../en/data/sponsors.yml
- - topic_repos: ../en/data/topic_repos.yml
- redirects:
- redirect_maps:
- deployment/deta.md: deployment/cloud.md
- advanced/graphql.md: how-to/graphql.md
- advanced/custom-request-and-route.md: how-to/custom-request-and-route.md
- advanced/conditional-openapi.md: how-to/conditional-openapi.md
- advanced/extending-openapi.md: how-to/extending-openapi.md
- advanced/testing-database.md: how-to/testing-database.md
mkdocstrings:
handlers:
python:
nav:
- FastAPI: index.md
- features.md
-- Learn:
+- "":
- learn/index.md
- python-types.md
- async.md
- environment-variables.md
- virtual-environments.md
- - Tutorial - User Guide:
+ - "":
- tutorial/index.md
- tutorial/first-steps.md
- tutorial/path-params.md
- tutorial/path-operation-configuration.md
- tutorial/encoder.md
- tutorial/body-updates.md
- - Dependencies:
+ - "":
- tutorial/dependencies/index.md
- tutorial/dependencies/classes-as-dependencies.md
- tutorial/dependencies/sub-dependencies.md
- tutorial/dependencies/dependencies-in-path-operation-decorators.md
- tutorial/dependencies/global-dependencies.md
- tutorial/dependencies/dependencies-with-yield.md
- - Security:
+ - "":
- tutorial/security/index.md
- tutorial/security/first-steps.md
- tutorial/security/get-current-user.md
- tutorial/static-files.md
- tutorial/testing.md
- tutorial/debugging.md
- - Advanced User Guide:
+ - "":
- advanced/index.md
- advanced/stream-data.md
- advanced/path-operation-advanced-configuration.md
- advanced/response-headers.md
- advanced/response-change-status-code.md
- advanced/advanced-dependencies.md
- - Advanced Security:
+ - "":
- advanced/security/index.md
- advanced/security/oauth2-scopes.md
- advanced/security/http-basic-auth.md
- advanced/strict-content-type.md
- fastapi-cli.md
- editor-support.md
- - Deployment:
+ - "":
- deployment/index.md
- deployment/versions.md
- deployment/fastapicloud.md
- deployment/cloud.md
- deployment/server-workers.md
- deployment/docker.md
- - How To - Recipes:
+ - "":
- how-to/index.md
- how-to/general.md
- how-to/migrate-from-pydantic-v1-to-pydantic-v2.md
- how-to/configure-swagger-ui.md
- how-to/testing-database.md
- how-to/authentication-error-status-code.md
-- Reference (Code API):
+- "":
- reference/index.md
- reference/fastapi.md
- reference/parameters.md
- reference/response.md
- reference/responses.md
- reference/middleware.md
- - OpenAPI:
+ - "":
- reference/openapi/index.md
- reference/openapi/docs.md
- reference/openapi/models.md
- reference/templating.md
- reference/testclient.md
- fastapi-people.md
-- Resources:
+- "":
- resources/index.md
- help-fastapi.md
- contributing.md
- external-links.md
- newsletter.md
- management-tasks.md
-- About:
+- "":
- about/index.md
- alternatives.md
- history-design-future.md
- management.md
- release-notes.md
markdown_extensions:
- material.extensions.preview:
- targets:
- include:
- - '*'
+ zensical.extensions.macros: null
abbr: null
attr_list: null
footnotes: null
markdown_include_variants: null
extra:
social:
- - icon: fontawesome/brands/github-alt
+ - icon: octicons/mark-github-24
link: https://github.com/fastapi/fastapi
- icon: fontawesome/brands/discord
link: https://discord.gg/VQjSZaeJmf
- - icon: fontawesome/brands/twitter
+ - icon: fontawesome/brands/x-twitter
link: https://x.com/fastapi
+ - icon: fontawesome/brands/bluesky
+ link: https://bsky.app/profile/fastapi.tiangolo.com
- icon: fontawesome/brands/linkedin
link: https://www.linkedin.com/company/fastapi
- - icon: fontawesome/solid/globe
- link: https://tiangolo.com
alternate:
- link: /
name: en - English
- js/termynal.js
- js/custom.js
- js/init_kapa_widget.js
-hooks:
-- ../../scripts/mkdocs_hooks.py
+validation:
+ unresolved_references: false
</div>
{% if not config.extra.generator == false %}
Made with
- <a href="https://squidfunk.github.io/mkdocs-material/" target="_blank" rel="noopener">
- Material for MkDocs
+ <a href="https://zensical.org" target="_blank" rel="noopener">
+ Zensical
</a>
{% endif %}
</div>
+---
+include_yaml:
+ sponsors: data/sponsors.yml
+---
+
# FastAPI { #fastapi }
<style>
+++ /dev/null
-INHERIT: ../en/mkdocs.yml
+---
+include_yaml:
+ sponsors: data/sponsors.yml
+---
+
# FastAPI { #fastapi }
<style>
+++ /dev/null
-INHERIT: ../en/mkdocs.yml
+---
+include_yaml:
+ sponsors: data/sponsors.yml
+---
+
# FastAPI { #fastapi }
<style>
+++ /dev/null
-INHERIT: ../en/mkdocs.yml
+---
+include_yaml:
+ sponsors: data/sponsors.yml
+---
+
# FastAPI { #fastapi }
<style>
+++ /dev/null
-INHERIT: ../en/mkdocs.yml
+---
+include_yaml:
+ sponsors: data/sponsors.yml
+---
+
# FastAPI { #fastapi }
<style>
+++ /dev/null
-INHERIT: ../en/mkdocs.yml
+---
+include_yaml:
+ sponsors: data/sponsors.yml
+---
+
# FastAPI { #fastapi }
<style>
+++ /dev/null
-INHERIT: ../en/mkdocs.yml
+---
+include_yaml:
+ sponsors: data/sponsors.yml
+---
+
# FastAPI { #fastapi }
<style>
+++ /dev/null
-INHERIT: ../en/mkdocs.yml
+---
+include_yaml:
+ sponsors: data/sponsors.yml
+---
+
# FastAPI { #fastapi }
<style>
+++ /dev/null
-INHERIT: ../en/mkdocs.yml
+---
+include_yaml:
+ sponsors: data/sponsors.yml
+---
+
# FastAPI { #fastapi }
<style>
+++ /dev/null
-INHERIT: ../en/mkdocs.yml
+---
+include_yaml:
+ sponsors: data/sponsors.yml
+---
+
# FastAPI { #fastapi }
<style>
+++ /dev/null
-INHERIT: ../en/mkdocs.yml
{ include-group = "docs-tests" },
"black >=25.1.0",
"cairosvg >=2.8.2",
- # for MkDocs live reload
- "click==8.2.1",
"griffe-typingdoc >=0.3.0",
"griffe-warnings-deprecated >=1.1.0",
"jieba >=0.42.1",
"markdown-include-variants >=0.0.8",
"mdx-include >=1.4.1,<2.0.0",
- "mkdocs-macros-plugin >=1.5.0",
- "mkdocs-material >=9.7.0",
- "mkdocs-redirects >=1.2.1,<1.3.0",
- "mkdocstrings[python] >=0.30.1",
+ "mkdocstrings[python] >=1.0.3",
"pillow >=11.3.0",
"python-slugify >=8.0.4",
"pyyaml >=5.3.1,<7.0.0",
"typer >=0.21.1",
+ "zensical >=0.0.42",
]
docs-tests = [
"httpx >=0.23.0,<1.0.0",
from pathlib import Path
from typing import Any
-import mkdocs.utils
import typer
import yaml
from jinja2 import Template
mkdocs_name = "mkdocs.yml"
-missing_translation_snippet = """
-{!../../docs/missing-translation.md!}
-"""
-
non_translated_sections = (
f"reference{os.sep}",
"release-notes.md",
en_docs_path = Path("docs/en")
en_config_path: Path = en_docs_path / mkdocs_name
site_path = Path("site").absolute()
-build_site_path = Path("site_build").absolute()
+zensical_src_path = Path("site_zensical_src").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*$")
def get_en_config() -> dict[str, Any]:
- return mkdocs.utils.yaml_load(en_config_path.read_text(encoding="utf-8"))
+ return yaml.unsafe_load(en_config_path.read_text(encoding="utf-8"))
def get_lang_paths() -> list[Path]:
typer.echo(f"The language was already created: {lang}")
raise typer.Abort()
new_path.mkdir()
- new_config_path: Path = Path(new_path) / mkdocs_name
- new_config_path.write_text("INHERIT: ../en/mkdocs.yml\n", encoding="utf-8")
new_llm_prompt_path: Path = new_path / "llm-prompt.md"
new_llm_prompt_path.write_text("", encoding="utf-8")
print(f"Successfully initialized: {new_path}")
"""
Build the docs for a language.
"""
- lang_path: Path = Path("docs") / lang
- if not lang_path.is_dir():
+ build_zensical_lang_to_stage(lang)
+ copy_zensical_stage_to_site(lang)
+ typer.secho(f"Successfully built docs for: {lang}", color=typer.colors.GREEN)
+
+
+def split_markdown_header(markdown: str) -> tuple[str, str]:
+ prefix = ""
+ if markdown.startswith("---\n"):
+ front_matter_end = markdown.find("\n---\n", 4)
+ if front_matter_end != -1:
+ front_matter_end += len("\n---\n")
+ prefix = markdown[:front_matter_end]
+ markdown = markdown[front_matter_end:]
+ if markdown.startswith("#"):
+ header, separator, body = markdown.partition("\n\n")
+ if separator:
+ return f"{prefix}{header}", body
+ if prefix:
+ return prefix.rstrip("\n"), markdown
+ return "", markdown
+
+
+def add_markdown_notice(markdown: str, notice: str) -> str:
+ header, body = split_markdown_header(markdown)
+ if header:
+ return f"{header}\n\n{notice}\n\n{body}"
+ return f"{notice}\n\n{body}"
+
+
+def is_non_translated_path(path: Path) -> bool:
+ src_path = path.as_posix()
+ return any(src_path.startswith(section) for section in non_translated_sections)
+
+
+def get_en_url(path: Path) -> str:
+ url_path = path.with_suffix("").as_posix()
+ if url_path.endswith("/index"):
+ url_path = url_path.removesuffix("index")
+ elif url_path != "index":
+ url_path = f"{url_path}/"
+ else:
+ url_path = ""
+ return f"https://fastapi.tiangolo.com/{url_path}"
+
+
+def get_zensical_theme_language(lang: str) -> str:
+ if lang == "zh-hant":
+ return "zh-Hant"
+ return lang
+
+
+def stage_zensical_docs(lang: str) -> Path:
+ lang_docs_path = docs_path / lang / "docs"
+ if not lang_docs_path.is_dir():
typer.echo(f"The language translation doesn't seem to exist yet: {lang}")
raise typer.Abort()
- typer.echo(f"Building docs for: {lang}")
- build_site_dist_path = build_site_path / lang
+
+ en_docs_source_path = en_docs_path / "docs"
+ staged_docs_src_path = zensical_src_path / "docs_src"
+ if not staged_docs_src_path.exists():
+ shutil.copytree(Path("docs_src"), staged_docs_src_path, dirs_exist_ok=True)
+ lang_stage_path = zensical_src_path / lang
+ staged_docs_path = lang_stage_path / "content"
+ shutil.rmtree(lang_stage_path, ignore_errors=True)
+ shutil.copytree(en_docs_source_path, staged_docs_path)
+
+ missing_translation = (docs_path / "missing-translation.md").read_text(
+ encoding="utf-8"
+ )
+ translation_banner_path = lang_docs_path / "translation-banner.md"
+ if not translation_banner_path.is_file():
+ translation_banner_path = en_docs_source_path / "translation-banner.md"
+ translation_banner = translation_banner_path.read_text(encoding="utf-8")
+
+ if lang != "en":
+ for staged_file in staged_docs_path.rglob("*.md"):
+ relative_path = staged_file.relative_to(staged_docs_path)
+ translated_file = lang_docs_path / relative_path
+ if translated_file.is_file():
+ markdown = translated_file.read_text(encoding="utf-8")
+ if relative_path.name == "translation-banner.md":
+ staged_file.write_text(markdown, encoding="utf-8")
+ continue
+ en_url = get_en_url(relative_path)
+ banner = translation_banner.replace("ENGLISH_VERSION_URL", en_url)
+ staged_file.write_text(
+ add_markdown_notice(markdown, banner), encoding="utf-8"
+ )
+ elif not is_non_translated_path(relative_path):
+ markdown = staged_file.read_text(encoding="utf-8")
+ staged_file.write_text(
+ add_markdown_notice(markdown, missing_translation),
+ encoding="utf-8",
+ )
+
+ shutil.copytree(en_docs_path / "data", lang_stage_path / "data")
+ shutil.copytree(en_docs_path / "overrides", lang_stage_path / "overrides")
+
+ config = get_updated_config_content()
+ config["docs_dir"] = "content"
+ config["site_dir"] = "site"
+ if lang == "en":
+ config["site_url"] = "https://fastapi.tiangolo.com/"
+ else:
+ config["site_url"] = f"https://fastapi.tiangolo.com/{lang}/"
+ config.setdefault("theme", {})
+ config["theme"]["language"] = get_zensical_theme_language(lang)
+ if lang != "en":
+ # The root English build owns shared static assets; translated builds should
+ # reference those root paths instead of emitting language-local copies.
+ if "logo" in config["theme"]:
+ config["theme"]["logo"] = "/" + config["theme"]["logo"].lstrip("/")
+ if "favicon" in config["theme"]:
+ config["theme"]["favicon"] = "/" + config["theme"]["favicon"].lstrip("/")
+ config["extra_css"] = ["/" + path.lstrip("/") for path in config["extra_css"]]
+ config["extra_javascript"] = [
+ "/" + path.lstrip("/") for path in config["extra_javascript"]
+ ]
+ config_path = lang_stage_path / mkdocs_name
+ config_path.write_text(
+ yaml.dump(config, sort_keys=False, width=200, allow_unicode=True),
+ encoding="utf-8",
+ )
+ return config_path
+
+
+def build_zensical_config(config_path: Path) -> None:
+ subprocess.run(
+ ["zensical", "build", "--config-file", config_path.name],
+ check=True,
+ cwd=config_path.parent,
+ )
+
+
+def build_zensical_lang_to_stage(lang: str) -> Path:
+ typer.echo(f"Building Zensical docs for: {lang}")
+ config_path = stage_zensical_docs(lang)
+ config = yaml.unsafe_load(config_path.read_text(encoding="utf-8"))
+ build_site_dist_path = config_path.parent / config["site_dir"]
+ shutil.rmtree(build_site_dist_path, ignore_errors=True)
+ build_zensical_config(config_path)
+ return build_site_dist_path
+
+
+def copy_zensical_stage_to_site(lang: str) -> None:
+ build_site_dist_path = zensical_src_path / lang / "site"
if lang == "en":
dist_path = site_path
- # Don't remove en dist_path as it might already contain other languages.
- # When running build_all(), that function already removes site_path.
- # All this is only relevant locally, on GitHub Actions all this is done through
- # artifacts and multiple workflows, so it doesn't matter if directories are
- # removed or not.
else:
dist_path = site_path / lang
shutil.rmtree(dist_path, ignore_errors=True)
- current_dir = os.getcwd()
- os.chdir(lang_path)
- shutil.rmtree(build_site_dist_path, ignore_errors=True)
- subprocess.run(["mkdocs", "build", "--site-dir", build_site_dist_path], check=True)
shutil.copytree(build_site_dist_path, dist_path, dirs_exist_ok=True)
- os.chdir(current_dir)
- typer.secho(f"Successfully built docs for: {lang}", color=typer.colors.GREEN)
index_sponsors_template = """
match_start = re.search(r"<!-- sponsors -->", content)
match_end = re.search(r"<!-- /sponsors -->", content)
sponsors_data_path = en_docs_path / "data" / "sponsors.yml"
- sponsors = mkdocs.utils.yaml_load(sponsors_data_path.read_text(encoding="utf-8"))
+ sponsors = yaml.safe_load(sponsors_data_path.read_text(encoding="utf-8"))
if not (match_start and match_end):
raise RuntimeError("Couldn't auto-generate sponsors section")
if not match_pre:
@app.command()
def build_all() -> None:
"""
- Build mkdocs site for en, and then build each language inside, end result is located
- at directory ./site/ with each language inside.
+ Build the full translated docs site into ./site/.
"""
update_languages()
shutil.rmtree(site_path, ignore_errors=True)
+ shutil.rmtree(zensical_src_path, ignore_errors=True)
+ shutil.copytree(Path("docs_src"), zensical_src_path / "docs_src")
langs = [
lang.name
for lang in get_lang_paths()
if (lang.is_dir() and lang.name in SUPPORTED_LANGS)
]
- cpu_count = os.cpu_count() or 1
- process_pool_size = cpu_count * 4
+ process_pool_size = min(4, len(langs), os.cpu_count() or 1)
typer.echo(f"Using process pool size: {process_pool_size}")
with Pool(process_pool_size) as p:
- p.map(build_lang, langs)
+ p.map(build_zensical_lang_to_stage, langs)
+ if "en" in langs:
+ copy_zensical_stage_to_site("en")
+ for lang in langs:
+ if lang != "en":
+ copy_zensical_stage_to_site(lang)
+ typer.secho("Successfully built all docs", color=typer.colors.GREEN)
@app.command()
def update_languages() -> None:
"""
- Update the mkdocs.yml file Languages section including all the available languages.
+ Update the docs config Languages section including all the available languages.
"""
old_config = get_en_config()
updated_config = get_updated_config_content()
"""
A quick server to preview a built site with translations.
- For development, prefer the command live (or just mkdocs serve).
+ For development, prefer the command live.
This is here only to preview a site with translations already built.
@app.command()
-def live(
- lang: str = typer.Argument(
- None, callback=lang_callback, autocompletion=complete_existing_lang
- ),
- dirty: bool = False,
-) -> None:
+def live() -> None:
"""
- Serve with livereload a docs site for a specific language.
-
- This only shows the actual translated files, not the placeholders created with
- build-all.
-
- Takes an optional LANG argument with the name of the language to serve, by default
- en.
+ Serve the English docs with livereload from the source files.
"""
- # Enable line numbers during local development to make it easier to highlight
- if lang is None:
- lang = "en"
- lang_path: Path = docs_path / lang
- # Enable line numbers during local development to make it easier to highlight
- args = ["mkdocs", "serve", "--dev-addr", "127.0.0.1:8008"]
- if dirty:
- args.append("--dirty")
subprocess.run(
- args, env={**os.environ, "LINENUMS": "true"}, cwd=lang_path, check=True
+ [
+ "zensical",
+ "serve",
+ "--config-file",
+ mkdocs_name,
+ "--dev-addr",
+ "127.0.0.1:8008",
+ ],
+ cwd=en_docs_path,
+ check=True,
)
# Language names sourced from https://quickref.me/iso-639-1
# Contributors may wish to update or change these, e.g. to fix capitalization.
language_names_path = Path(__file__).parent / "../docs/language_names.yml"
- local_language_names: dict[str, str] = mkdocs.utils.yaml_load(
+ local_language_names: dict[str, str] = yaml.safe_load(
language_names_path.read_text(encoding="utf-8")
)
for lang_path in get_lang_paths():
+++ /dev/null
-from functools import lru_cache
-from pathlib import Path
-from typing import Any
-
-import material
-from mkdocs.config.defaults import MkDocsConfig
-from mkdocs.structure.files import File, Files
-from mkdocs.structure.nav import Link, Navigation, Section
-from mkdocs.structure.pages import Page
-
-non_translated_sections = [
- "reference/",
- "release-notes.md",
- "fastapi-people.md",
- "external-links.md",
- "newsletter.md",
- "management-tasks.md",
- "management.md",
-]
-
-
-@lru_cache
-def get_missing_translation_content(docs_dir: str) -> str:
- docs_dir_path = Path(docs_dir)
- missing_translation_path = docs_dir_path.parent.parent / "missing-translation.md"
- return missing_translation_path.read_text(encoding="utf-8")
-
-
-@lru_cache
-def get_translation_banner_content(docs_dir: str) -> str:
- docs_dir_path = Path(docs_dir)
- translation_banner_path = docs_dir_path / "translation-banner.md"
- if not translation_banner_path.is_file():
- translation_banner_path = (
- docs_dir_path.parent.parent / "en" / "docs" / "translation-banner.md"
- )
- return translation_banner_path.read_text(encoding="utf-8")
-
-
-@lru_cache
-def get_mkdocs_material_langs() -> list[str]:
- material_path = Path(material.__file__).parent
- material_langs_path = material_path / "templates" / "partials" / "languages"
- langs = [file.stem for file in material_langs_path.glob("*.html")]
- return langs
-
-
-class EnFile(File):
- pass
-
-
-def on_config(config: MkDocsConfig, **kwargs: Any) -> MkDocsConfig:
- available_langs = get_mkdocs_material_langs()
- dir_path = Path(config.docs_dir)
- lang = dir_path.parent.name
- if lang in available_langs:
- config.theme["language"] = lang
- if not (config.site_url or "").endswith(f"{lang}/") and lang != "en":
- config.site_url = f"{config.site_url}{lang}/"
- return config
-
-
-def resolve_file(*, item: str, files: Files, config: MkDocsConfig) -> None:
- item_path = Path(config.docs_dir) / item
- if not item_path.is_file():
- en_src_dir = (Path(config.docs_dir) / "../../en/docs").resolve()
- potential_path = en_src_dir / item
- if potential_path.is_file():
- files.append(
- EnFile(
- path=item,
- src_dir=str(en_src_dir),
- dest_dir=config.site_dir,
- use_directory_urls=config.use_directory_urls,
- )
- )
-
-
-def resolve_files(*, items: list[Any], files: Files, config: MkDocsConfig) -> None:
- for item in items:
- if isinstance(item, str):
- resolve_file(item=item, files=files, config=config)
- elif isinstance(item, dict):
- assert len(item) == 1
- values = list(item.values())
- if not values:
- continue
- if isinstance(values[0], str):
- resolve_file(item=values[0], files=files, config=config)
- elif isinstance(values[0], list):
- resolve_files(items=values[0], files=files, config=config)
- else:
- raise ValueError(f"Unexpected value: {values}")
-
-
-def on_files(files: Files, *, config: MkDocsConfig) -> Files:
- resolve_files(items=config.nav or [], files=files, config=config)
- if "logo" in config.theme:
- resolve_file(item=config.theme["logo"], files=files, config=config)
- if "favicon" in config.theme:
- resolve_file(item=config.theme["favicon"], files=files, config=config)
- resolve_files(items=config.extra_css, files=files, config=config)
- resolve_files(items=config.extra_javascript, files=files, config=config)
- return files
-
-
-def generate_renamed_section_items(
- items: list[Page | Section | Link], *, config: MkDocsConfig
-) -> list[Page | Section | Link]:
- new_items: list[Page | Section | Link] = []
- for item in items:
- if isinstance(item, Section):
- new_title = item.title
- new_children = generate_renamed_section_items(item.children, config=config)
- first_child = new_children[0]
- if isinstance(first_child, Page):
- if first_child.file.src_path.endswith("index.md"):
- # Read the source so that the title is parsed and available
- first_child.read_source(config=config)
- new_title = first_child.title or new_title
- # Creating a new section makes it render it collapsed by default
- # no idea why, so, let's just modify the existing one
- # new_section = Section(title=new_title, children=new_children)
- item.title = new_title.split("{ #")[0]
- item.children = new_children
- new_items.append(item)
- else:
- new_items.append(item)
- return new_items
-
-
-def on_nav(
- nav: Navigation, *, config: MkDocsConfig, files: Files, **kwargs: Any
-) -> Navigation:
- new_items = generate_renamed_section_items(nav.items, config=config)
- return Navigation(items=new_items, pages=nav.pages)
-
-
-def on_pre_page(page: Page, *, config: MkDocsConfig, files: Files) -> Page:
- return page
-
-
-def on_page_markdown(
- markdown: str, *, page: Page, config: MkDocsConfig, files: Files
-) -> str:
- # Set metadata["social"]["cards_layout_options"]["title"] to clean title (without
- # permalink)
- title = page.title
- clean_title = title.split("{ #")[0]
- if clean_title:
- page.meta.setdefault("social", {})
- page.meta["social"].setdefault("cards_layout_options", {})
- page.meta["social"]["cards_layout_options"]["title"] = clean_title
-
- if isinstance(page.file, EnFile):
- for excluded_section in non_translated_sections:
- if page.file.src_path.startswith(excluded_section):
- return markdown
- missing_translation_content = get_missing_translation_content(config.docs_dir)
- header = ""
- body = markdown
- if markdown.startswith("#"):
- header, _, body = markdown.partition("\n\n")
- return f"{header}\n\n{missing_translation_content}\n\n{body}"
-
- docs_dir_path = Path(config.docs_dir)
- en_docs_dir_path = docs_dir_path.parent.parent / "en/docs"
-
- if docs_dir_path == en_docs_dir_path:
- return markdown
-
- # For translated pages add translation banner
- translation_banner_content = get_translation_banner_content(config.docs_dir)
- en_url = "https://fastapi.tiangolo.com/" + page.url.lstrip("/")
- translation_banner_content = translation_banner_content.replace(
- "ENGLISH_VERSION_URL", en_url
- )
- header = ""
- body = markdown
- if markdown.startswith("#"):
- header, _, body = markdown.partition("\n\n")
- return f"{header}\n\n{translation_banner_content}\n\n{body}"
{ url = "https://files.pythonhosted.org/packages/57/2f/55fca558f925a51db046e5b929deb317ddb05afed74b22d89f4eca578980/authlib-1.6.11-py2.py3-none-any.whl", hash = "sha256:c8687a9a26451c51a34a06fa17bb97cb15bba46a6a626755e2d7f50da8bff3e3", size = 244469, upload-time = "2026-04-16T07:22:48.413Z" },
]
-[[package]]
-name = "babel"
-version = "2.18.0"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/7d/b2/51899539b6ceeeb420d40ed3cd4b7a40519404f9baf3d4ac99dc413a834b/babel-2.18.0.tar.gz", hash = "sha256:b80b99a14bd085fcacfa15c9165f651fbb3406e66cc603abf11c5750937c992d", size = 9959554, upload-time = "2026-02-01T12:30:56.078Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/77/f5/21d2de20e8b8b0408f0681956ca2c69f1320a3848ac50e6e7f39c6159675/babel-2.18.0-py3-none-any.whl", hash = "sha256:e2b422b277c2b9a9630c1d7903c2a00d0830c409c59ac8cae9081c92f1aeba35", size = 10196845, upload-time = "2026-02-01T12:30:53.445Z" },
-]
-
[[package]]
name = "backports-tarfile"
version = "1.2.0"
{ url = "https://files.pythonhosted.org/packages/b9/fa/123043af240e49752f1c4bd24da5053b6bd00cad78c2be53c0d1e8b975bc/backports.tarfile-1.2.0-py3-none-any.whl", hash = "sha256:77e284d754527b01fb1e6fa8a1afe577858ebe4e9dad8919e34c862cb399bc34", size = 30181, upload-time = "2024-05-28T17:01:53.112Z" },
]
-[[package]]
-name = "backrefs"
-version = "6.1"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/86/e3/bb3a439d5cb255c4774724810ad8073830fac9c9dee123555820c1bcc806/backrefs-6.1.tar.gz", hash = "sha256:3bba1749aafe1db9b915f00e0dd166cba613b6f788ffd63060ac3485dc9be231", size = 7011962, upload-time = "2025-11-15T14:52:08.323Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/3b/ee/c216d52f58ea75b5e1841022bbae24438b19834a29b163cb32aa3a2a7c6e/backrefs-6.1-py310-none-any.whl", hash = "sha256:2a2ccb96302337ce61ee4717ceacfbf26ba4efb1d55af86564b8bbaeda39cac1", size = 381059, upload-time = "2025-11-15T14:51:59.758Z" },
- { url = "https://files.pythonhosted.org/packages/e6/9a/8da246d988ded941da96c7ed945d63e94a445637eaad985a0ed88787cb89/backrefs-6.1-py311-none-any.whl", hash = "sha256:e82bba3875ee4430f4de4b6db19429a27275d95a5f3773c57e9e18abc23fd2b7", size = 392854, upload-time = "2025-11-15T14:52:01.194Z" },
- { url = "https://files.pythonhosted.org/packages/37/c9/fd117a6f9300c62bbc33bc337fd2b3c6bfe28b6e9701de336b52d7a797ad/backrefs-6.1-py312-none-any.whl", hash = "sha256:c64698c8d2269343d88947c0735cb4b78745bd3ba590e10313fbf3f78c34da5a", size = 398770, upload-time = "2025-11-15T14:52:02.584Z" },
- { url = "https://files.pythonhosted.org/packages/eb/95/7118e935b0b0bd3f94dfec2d852fd4e4f4f9757bdb49850519acd245cd3a/backrefs-6.1-py313-none-any.whl", hash = "sha256:4c9d3dc1e2e558965202c012304f33d4e0e477e1c103663fd2c3cc9bb18b0d05", size = 400726, upload-time = "2025-11-15T14:52:04.093Z" },
- { url = "https://files.pythonhosted.org/packages/1d/72/6296bad135bfafd3254ae3648cd152980a424bd6fed64a101af00cc7ba31/backrefs-6.1-py314-none-any.whl", hash = "sha256:13eafbc9ccd5222e9c1f0bec563e6d2a6d21514962f11e7fc79872fd56cbc853", size = 412584, upload-time = "2025-11-15T14:52:05.233Z" },
- { url = "https://files.pythonhosted.org/packages/02/e3/a4fa1946722c4c7b063cc25043a12d9ce9b4323777f89643be74cef2993c/backrefs-6.1-py39-none-any.whl", hash = "sha256:a9e99b8a4867852cad177a6430e31b0f6e495d65f8c6c134b68c14c3c95bf4b0", size = 381058, upload-time = "2025-11-15T14:52:06.698Z" },
-]
-
[[package]]
name = "beartype"
version = "0.22.9"
{ url = "https://files.pythonhosted.org/packages/1c/7c/996760c30f1302704af57c66ff2d723f7d656d0d0b93563b5528a51484bb/cyclopts-4.5.1-py3-none-any.whl", hash = "sha256:0642c93601e554ca6b7b9abd81093847ea4448b2616280f2a0952416574e8c7a", size = 199807, upload-time = "2026-01-25T15:23:55.219Z" },
]
+[[package]]
+name = "deepmerge"
+version = "2.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/a8/3a/b0ba594708f1ad0bc735884b3ad854d3ca3bdc1d741e56e40bbda6263499/deepmerge-2.0.tar.gz", hash = "sha256:5c3d86081fbebd04dd5de03626a0607b809a98fb6ccba5770b62466fe940ff20", size = 19890, upload-time = "2024-08-30T05:31:50.308Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/2d/82/e5d2c1c67d19841e9edc74954c827444ae826978499bde3dfc1d007c8c11/deepmerge-2.0-py3-none-any.whl", hash = "sha256:6de9ce507115cff0bed95ff0ce9ecc31088ef50cbdf09bc90a09349a318b3d00", size = 13475, upload-time = "2024-08-30T05:31:48.659Z" },
+]
+
[[package]]
name = "defusedxml"
version = "0.7.1"
{ name = "anyio", extra = ["trio"] },
{ name = "black" },
{ name = "cairosvg" },
- { name = "click" },
{ name = "coverage", extra = ["toml"] },
{ name = "dirty-equals" },
{ name = "flask" },
{ name = "jieba" },
{ name = "markdown-include-variants" },
{ name = "mdx-include" },
- { name = "mkdocs-macros-plugin" },
- { name = "mkdocs-material" },
- { name = "mkdocs-redirects" },
{ name = "mkdocstrings", extra = ["python"] },
{ name = "mypy" },
{ name = "pillow" },
{ name = "strawberry-graphql" },
{ name = "ty" },
{ name = "typer" },
+ { name = "zensical" },
{ name = "zizmor" },
]
docs = [
{ name = "black" },
{ name = "cairosvg" },
- { name = "click" },
{ name = "griffe-typingdoc" },
{ name = "griffe-warnings-deprecated" },
{ name = "httpx" },
{ name = "jieba" },
{ name = "markdown-include-variants" },
{ name = "mdx-include" },
- { name = "mkdocs-macros-plugin" },
- { name = "mkdocs-material" },
- { name = "mkdocs-redirects" },
{ name = "mkdocstrings", extra = ["python"] },
{ name = "pillow" },
{ name = "python-slugify" },
{ name = "pyyaml" },
{ name = "ruff" },
{ name = "typer" },
+ { name = "zensical" },
]
docs-tests = [
{ name = "httpx" },
{ name = "anyio", extras = ["trio"], specifier = ">=3.2.1,<5.0.0" },
{ name = "black", specifier = ">=25.1.0" },
{ name = "cairosvg", specifier = ">=2.8.2" },
- { name = "click", specifier = "==8.2.1" },
{ name = "coverage", extras = ["toml"], specifier = ">=7.13,<8.0" },
{ name = "dirty-equals", specifier = ">=0.9.0" },
{ name = "flask", specifier = ">=3.0.0,<4.0.0" },
{ name = "jieba", specifier = ">=0.42.1" },
{ name = "markdown-include-variants", specifier = ">=0.0.8" },
{ name = "mdx-include", specifier = ">=1.4.1,<2.0.0" },
- { name = "mkdocs-macros-plugin", specifier = ">=1.5.0" },
- { name = "mkdocs-material", specifier = ">=9.7.0" },
- { name = "mkdocs-redirects", specifier = ">=1.2.1,<1.3.0" },
- { name = "mkdocstrings", extras = ["python"], specifier = ">=0.30.1" },
+ { name = "mkdocstrings", extras = ["python"], specifier = ">=1.0.3" },
{ name = "mypy", specifier = ">=1.14.1" },
{ name = "pillow", specifier = ">=11.3.0" },
{ name = "playwright", specifier = ">=1.57.0" },
{ name = "strawberry-graphql", specifier = ">=0.200.0,<1.0.0" },
{ name = "ty", specifier = ">=0.0.25" },
{ name = "typer", specifier = ">=0.21.1" },
+ { name = "zensical", specifier = ">=0.0.42" },
{ name = "zizmor", specifier = ">=1.23.1" },
]
docs = [
{ name = "black", specifier = ">=25.1.0" },
{ name = "cairosvg", specifier = ">=2.8.2" },
- { name = "click", specifier = "==8.2.1" },
{ name = "griffe-typingdoc", specifier = ">=0.3.0" },
{ name = "griffe-warnings-deprecated", specifier = ">=1.1.0" },
{ name = "httpx", specifier = ">=0.23.0,<1.0.0" },
{ name = "jieba", specifier = ">=0.42.1" },
{ name = "markdown-include-variants", specifier = ">=0.0.8" },
{ name = "mdx-include", specifier = ">=1.4.1,<2.0.0" },
- { name = "mkdocs-macros-plugin", specifier = ">=1.5.0" },
- { name = "mkdocs-material", specifier = ">=9.7.0" },
- { name = "mkdocs-redirects", specifier = ">=1.2.1,<1.3.0" },
- { name = "mkdocstrings", extras = ["python"], specifier = ">=0.30.1" },
+ { name = "mkdocstrings", extras = ["python"], specifier = ">=1.0.3" },
{ name = "pillow", specifier = ">=11.3.0" },
{ name = "python-slugify", specifier = ">=8.0.4" },
{ name = "pyyaml", specifier = ">=5.3.1,<7.0.0" },
{ name = "ruff", specifier = ">=0.14.14" },
{ name = "typer", specifier = ">=0.21.1" },
+ { name = "zensical", specifier = ">=0.0.42" },
]
docs-tests = [
{ name = "httpx", specifier = ">=0.23.0,<1.0.0" },
{ url = "https://files.pythonhosted.org/packages/cb/44/870d44b30e1dcfb6a65932e3e1506c103a8a5aea9103c337e7a53180322c/hf_xet-1.2.0-cp37-abi3-win_amd64.whl", hash = "sha256:e6584a52253f72c9f52f9e549d5895ca7a471608495c4ecaa6cc73dba2b24d69", size = 2905735, upload-time = "2025-10-24T19:04:35.928Z" },
]
-[[package]]
-name = "hjson"
-version = "3.1.0"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/82/e5/0b56d723a76ca67abadbf7fb71609fb0ea7e6926e94fcca6c65a85b36a0e/hjson-3.1.0.tar.gz", hash = "sha256:55af475a27cf83a7969c808399d7bccdec8fb836a07ddbd574587593b9cdcf75", size = 40541, upload-time = "2022-08-13T02:53:01.919Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/1f/7f/13cd798d180af4bf4c0ceddeefba2b864a63c71645abc0308b768d67bb81/hjson-3.1.0-py3-none-any.whl", hash = "sha256:65713cdcf13214fb554eb8b4ef803419733f4f5e551047c9b711098ab7186b89", size = 54018, upload-time = "2022-08-13T02:52:59.899Z" },
-]
-
[[package]]
name = "httpcore"
version = "1.0.9"
{ url = "https://files.pythonhosted.org/packages/9f/d4/029f984e8d3f3b6b726bd33cafc473b75e9e44c0f7e80a5b29abc466bdea/mkdocs_get_deps-0.2.0-py3-none-any.whl", hash = "sha256:2bf11d0b133e77a0dd036abeeb06dec8775e46efa526dc70667d8863eefc6134", size = 9521, upload-time = "2023-11-20T17:51:08.587Z" },
]
-[[package]]
-name = "mkdocs-macros-plugin"
-version = "1.5.0"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "hjson" },
- { name = "jinja2" },
- { name = "mkdocs" },
- { name = "packaging" },
- { name = "pathspec" },
- { name = "python-dateutil" },
- { name = "pyyaml" },
- { name = "requests" },
- { name = "super-collections" },
- { name = "termcolor" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/92/15/e6a44839841ebc9c5872fa0e6fad1c3757424e4fe026093b68e9f386d136/mkdocs_macros_plugin-1.5.0.tar.gz", hash = "sha256:12aa45ce7ecb7a445c66b9f649f3dd05e9b92e8af6bc65e4acd91d26f878c01f", size = 37730, upload-time = "2025-11-13T08:08:55.545Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/51/62/9fffba5bb9ed3d31a932ad35038ba9483d59850256ee0fea7f1187173983/mkdocs_macros_plugin-1.5.0-py3-none-any.whl", hash = "sha256:c10fabd812bf50f9170609d0ed518e54f1f0e12c334ac29141723a83c881dd6f", size = 44626, upload-time = "2025-11-13T08:08:53.878Z" },
-]
-
-[[package]]
-name = "mkdocs-material"
-version = "9.7.6"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "babel" },
- { name = "backrefs" },
- { name = "colorama" },
- { name = "jinja2" },
- { name = "markdown" },
- { name = "mkdocs" },
- { name = "mkdocs-material-extensions" },
- { name = "paginate" },
- { name = "pygments" },
- { name = "pymdown-extensions" },
- { name = "requests" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/45/29/6d2bcf41ae40802c4beda2432396fff97b8456fb496371d1bc7aad6512ec/mkdocs_material-9.7.6.tar.gz", hash = "sha256:00bdde50574f776d328b1862fe65daeaf581ec309bd150f7bff345a098c64a69", size = 4097959, upload-time = "2026-03-19T15:41:58.161Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/2c/01/bc663630c510822c95c47a66af9fa7a443c295b47d5f041e5e6ae62ef659/mkdocs_material-9.7.6-py3-none-any.whl", hash = "sha256:71b84353921b8ea1ba84fe11c50912cc512da8fe0881038fcc9a0761c0e635ba", size = 9305470, upload-time = "2026-03-19T15:41:55.217Z" },
-]
-
-[[package]]
-name = "mkdocs-material-extensions"
-version = "1.3.1"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/79/9b/9b4c96d6593b2a541e1cb8b34899a6d021d208bb357042823d4d2cabdbe7/mkdocs_material_extensions-1.3.1.tar.gz", hash = "sha256:10c9511cea88f568257f960358a467d12b970e1f7b2c0e5fb2bb48cab1928443", size = 11847, upload-time = "2023-11-22T19:09:45.208Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/5b/54/662a4743aa81d9582ee9339d4ffa3c8fd40a4965e033d77b9da9774d3960/mkdocs_material_extensions-1.3.1-py3-none-any.whl", hash = "sha256:adff8b62700b25cb77b53358dad940f3ef973dd6db797907c49e3c2ef3ab4e31", size = 8728, upload-time = "2023-11-22T19:09:43.465Z" },
-]
-
-[[package]]
-name = "mkdocs-redirects"
-version = "1.2.2"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "mkdocs" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/f1/a8/6d44a6cf07e969c7420cb36ab287b0669da636a2044de38a7d2208d5a758/mkdocs_redirects-1.2.2.tar.gz", hash = "sha256:3094981b42ffab29313c2c1b8ac3969861109f58b2dd58c45fc81cd44bfa0095", size = 7162, upload-time = "2024-11-07T14:57:21.109Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/c4/ec/38443b1f2a3821bbcb24e46cd8ba979154417794d54baf949fefde1c2146/mkdocs_redirects-1.2.2-py3-none-any.whl", hash = "sha256:7dbfa5647b79a3589da4401403d69494bd1f4ad03b9c15136720367e1f340ed5", size = 6142, upload-time = "2024-11-07T14:57:19.143Z" },
-]
-
[[package]]
name = "mkdocstrings"
-version = "1.0.2"
+version = "1.0.4"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "jinja2" },
{ name = "mkdocs-autorefs" },
{ name = "pymdown-extensions" },
]
-sdist = { url = "https://files.pythonhosted.org/packages/63/4d/1ca8a9432579184599714aaeb36591414cc3d3bfd9d494f6db540c995ae4/mkdocstrings-1.0.2.tar.gz", hash = "sha256:48edd0ccbcb9e30a3121684e165261a9d6af4d63385fc4f39a54a49ac3b32ea8", size = 101048, upload-time = "2026-01-24T15:57:25.735Z" }
+sdist = { url = "https://files.pythonhosted.org/packages/1d/5d/f888d4d3eb31359b327bc9b17a212d6ef03fe0b0682fbb3fc2cb849fb12b/mkdocstrings-1.0.4.tar.gz", hash = "sha256:3969a6515b77db65fd097b53c1b7aa4ae840bd71a2ee62a6a3e89503446d7172", size = 100088, upload-time = "2026-04-15T09:16:53.376Z" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/57/32/407a9a5fdd7d8ecb4af8d830b9bcdf47ea68f916869b3f44bac31f081250/mkdocstrings-1.0.2-py3-none-any.whl", hash = "sha256:41897815a8026c3634fe5d51472c3a569f92ded0ad8c7a640550873eea3b6817", size = 35443, upload-time = "2026-01-24T15:57:23.933Z" },
+ { url = "https://files.pythonhosted.org/packages/6e/94/be70f8ee9c45f2f62b39a1f0e9303bc20e138a8f3b8e50ffd89498e177e1/mkdocstrings-1.0.4-py3-none-any.whl", hash = "sha256:63464b4b29053514f32a1dbbf604e52876d5e638111b0c295ab7ed3cac73ca9b", size = 35560, upload-time = "2026-04-15T09:16:51.436Z" },
]
[package.optional-dependencies]
{ url = "https://files.pythonhosted.org/packages/20/12/38679034af332785aac8774540895e234f4d07f7545804097de4b666afd8/packaging-25.0-py3-none-any.whl", hash = "sha256:29572ef2b1f17581046b3a2227d5c611fb25ec70ca1ba8554b24b0e69331a484", size = 66469, upload-time = "2025-04-19T11:48:57.875Z" },
]
-[[package]]
-name = "paginate"
-version = "0.5.7"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/ec/46/68dde5b6bc00c1296ec6466ab27dddede6aec9af1b99090e1107091b3b84/paginate-0.5.7.tar.gz", hash = "sha256:22bd083ab41e1a8b4f3690544afb2c60c25e5c9a63a30fa2f483f6c60c8e5945", size = 19252, upload-time = "2024-08-25T14:17:24.139Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/90/96/04b8e52da071d28f5e21a805b19cb9390aa17a47462ac87f5e2696b9566d/paginate-0.5.7-py2.py3-none-any.whl", hash = "sha256:b885e2af73abcf01d9559fd5216b57ef722f8c42affbb63942377668e35c7591", size = 13746, upload-time = "2024-08-25T14:17:22.55Z" },
-]
-
[[package]]
name = "pathable"
version = "0.4.4"
{ url = "https://files.pythonhosted.org/packages/be/25/13773a2944cc5975d44db58233b3610ddc88d4be49e6576adf7ed4b62250/strawberry_graphql-0.314.3-py3-none-any.whl", hash = "sha256:4ef4442cea79014487acd7a0d1a2ce55c9d2a42dcd34a307d4c01f2ab477ecfa", size = 324471, upload-time = "2026-04-08T18:04:44.088Z" },
]
-[[package]]
-name = "super-collections"
-version = "0.6.2"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "hjson" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/e0/de/a0c3d1244912c260638f0f925e190e493ccea37ecaea9bbad7c14413b803/super_collections-0.6.2.tar.gz", hash = "sha256:0c8d8abacd9fad2c7c1c715f036c29f5db213f8cac65f24d45ecba12b4da187a", size = 31315, upload-time = "2025-09-30T00:37:08.067Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/17/43/47c7cf84b3bd74a8631b02d47db356656bb8dff6f2e61a4c749963814d0d/super_collections-0.6.2-py3-none-any.whl", hash = "sha256:291b74d26299e9051d69ad9d89e61b07b6646f86a57a2f5ab3063d206eee9c56", size = 16173, upload-time = "2025-09-30T00:37:07.104Z" },
-]
-
[[package]]
name = "temporalio"
version = "1.26.0"
{ url = "https://files.pythonhosted.org/packages/73/ae/b48f95715333080afb75a4504487cbe142cae1268afc482d06692d605ae6/yarl-1.22.0-py3-none-any.whl", hash = "sha256:1380560bdba02b6b6c90de54133c81c9f2a453dee9912fe58c1dcced1edb7cff", size = 46814, upload-time = "2025-10-06T14:12:53.872Z" },
]
+[[package]]
+name = "zensical"
+version = "0.0.42"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "click" },
+ { name = "deepmerge" },
+ { name = "jinja2" },
+ { name = "markdown" },
+ { name = "pygments" },
+ { name = "pymdown-extensions" },
+ { name = "pyyaml" },
+ { name = "tomli" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/7a/dd/04e89ab92aed1ef9e36c76ef095fb587ffcbe4162aa7f3fe6d63aafade4a/zensical-0.0.42.tar.gz", hash = "sha256:cc346b833868a59412fe8d8498a152be90be9f3d8fb87e1f1a1c2e1146cbae1b", size = 3931093, upload-time = "2026-05-15T10:22:45.354Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/fb/19/2ca4e52769307959f7485d4c5da7b24787339787c1cbc371885cef448e50/zensical-0.0.42-cp310-abi3-macosx_10_12_x86_64.whl", hash = "sha256:bffd7a34b570fa3ccadf1d23babb0f7c4851c6b626e4fc8ed9f21c2eaae85968", size = 12705326, upload-time = "2026-05-15T10:22:07.905Z" },
+ { url = "https://files.pythonhosted.org/packages/2c/82/0832b0d2c0c2800174141d5519a017105d3dace9194e2c29730e7a676adf/zensical-0.0.42-cp310-abi3-macosx_11_0_arm64.whl", hash = "sha256:ee1a79789f9462ef44a4b6ebbfc8b5bf4b2447607da8bc5b35bc9c4ce4ea2370", size = 12568663, upload-time = "2026-05-15T10:22:11.072Z" },
+ { url = "https://files.pythonhosted.org/packages/ac/87/272b3998322958ca38f09323d2347cb121dfc851477c36962b71319242a5/zensical-0.0.42-cp310-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5e9a5d508ce8d1b07d8417f0623be476f6b37d445ab4356481a71e613a7979d6", size = 12948460, upload-time = "2026-05-15T10:22:13.792Z" },
+ { url = "https://files.pythonhosted.org/packages/ae/1b/e5f153401f162f48cae2d58e96b95fd39ba5bd1728fb5881a60e502f4e1d/zensical-0.0.42-cp310-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:9fbc0951a676e48afe7df3a9b2a30958dcf9c426ed2480972d3c04d6de485ba3", size = 12913460, upload-time = "2026-05-15T10:22:16.791Z" },
+ { url = "https://files.pythonhosted.org/packages/9b/4f/5186b4204bdfdf132851b7515a37b9602bfc153fb601db5fb244339bae52/zensical-0.0.42-cp310-abi3-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8f0e96e53f39b9e4b929a25d9df70bd7fa8217166a854e2c8f3185983dd01500", size = 13276704, upload-time = "2026-05-15T10:22:19.819Z" },
+ { url = "https://files.pythonhosted.org/packages/f2/df/b57b5fcc631ac7a4b4c6834d8cf0b88d3fca37c9db42fc6bbf9f097200ed/zensical-0.0.42-cp310-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b7d586e57436d603e88acd856864f99f0771aef24bf6560b2de238417bd3817c", size = 12987069, upload-time = "2026-05-15T10:22:22.537Z" },
+ { url = "https://files.pythonhosted.org/packages/a3/3a/b326a44a065d98e89b472645ad33037201e3385340c2e6e35627b18ab3fa/zensical-0.0.42-cp310-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:3c026f023330d67f986a94b68ffd36dc5066882e697e1125c37308d8d684135c", size = 13124195, upload-time = "2026-05-15T10:22:25.543Z" },
+ { url = "https://files.pythonhosted.org/packages/1b/1e/823740a662e357a8826dc8eeb87e06705e64219b2774430bc555f7c53d57/zensical-0.0.42-cp310-abi3-musllinux_1_2_armv7l.whl", hash = "sha256:e5908bc09cf5c1c50c9504241e37f89955daf3e89ba1b9d71c17972578b24804", size = 13182981, upload-time = "2026-05-15T10:22:28.89Z" },
+ { url = "https://files.pythonhosted.org/packages/80/6d/9fe261267ac36a7d57051d790022408e9043bc925c9ad21971a1e5b6c3e8/zensical-0.0.42-cp310-abi3-musllinux_1_2_i686.whl", hash = "sha256:c0bf96b55f0a44e8716bcb334a16380ed56772b555145da775a7d8ac8678cb6f", size = 13332666, upload-time = "2026-05-15T10:22:32.249Z" },
+ { url = "https://files.pythonhosted.org/packages/9b/57/9b0e4f131a7ad15cf1aca081748ea7336c084fb8e16be202a6bed32f595c/zensical-0.0.42-cp310-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:47cd99583738a8ab03fac4080741275c56e741a06dc8edfb541f4c1649a5ae69", size = 13270817, upload-time = "2026-05-15T10:22:35.388Z" },
+ { url = "https://files.pythonhosted.org/packages/bb/fd/bdb85cc444e4146e8970a22e48a903bfed5bf83276ad7d755caa415dda64/zensical-0.0.42-cp310-abi3-win32.whl", hash = "sha256:83090e53fba061967ecb3dff81500b1900f288bae108bf54084a2aeb6648ebd0", size = 12256227, upload-time = "2026-05-15T10:22:38.869Z" },
+ { url = "https://files.pythonhosted.org/packages/e0/b9/09d1f735c8e6d3eb61d176ed5ebcf658b65b126d7d4bbc03a7d366a1e17d/zensical-0.0.42-cp310-abi3-win_amd64.whl", hash = "sha256:2e4304e103f9cd5c637045bbae1ff29de3009ab01b16e99c2fd6d4bbceb7a3ee", size = 12486598, upload-time = "2026-05-15T10:22:42.158Z" },
+]
+
[[package]]
name = "zipp"
version = "3.23.0"