Browse Source

🔧 Migrate docs from MkDocs to Zensical (#15563)

pull/15567/head
Sebastián Ramírez 2 weeks ago
committed by GitHub
parent
commit
31ced9d49e
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 20
      .github/workflows/build-docs.yml
  2. 1
      .gitignore
  3. 5
      docs/de/docs/index.md
  4. 1
      docs/de/mkdocs.yml
  5. 6
      docs/en/docs/contributing.md
  6. 5
      docs/en/docs/external-links.md
  7. 10
      docs/en/docs/fastapi-people.md
  8. 5
      docs/en/docs/index.md
  9. 5
      docs/en/mkdocs.env.yml
  10. 72
      docs/en/mkdocs.yml
  11. 4
      docs/en/overrides/partials/copyright.html
  12. 5
      docs/es/docs/index.md
  13. 1
      docs/es/mkdocs.yml
  14. 5
      docs/fr/docs/index.md
  15. 1
      docs/fr/mkdocs.yml
  16. 5
      docs/ja/docs/index.md
  17. 1
      docs/ja/mkdocs.yml
  18. 5
      docs/ko/docs/index.md
  19. 1
      docs/ko/mkdocs.yml
  20. 5
      docs/pt/docs/index.md
  21. 1
      docs/pt/mkdocs.yml
  22. 5
      docs/ru/docs/index.md
  23. 1
      docs/ru/mkdocs.yml
  24. 5
      docs/tr/docs/index.md
  25. 1
      docs/tr/mkdocs.yml
  26. 5
      docs/uk/docs/index.md
  27. 1
      docs/uk/mkdocs.yml
  28. 5
      docs/zh-hant/docs/index.md
  29. 1
      docs/zh-hant/mkdocs.yml
  30. 5
      docs/zh/docs/index.md
  31. 1
      docs/zh/mkdocs.yml
  32. 8
      pyproject.toml
  33. 228
      scripts/docs.py
  34. 182
      scripts/mkdocs_hooks.py
  35. 186
      uv.lock

20
.github/workflows/build-docs.yml

@ -34,14 +34,13 @@ jobs:
- docs_src/** - docs_src/**
- pyproject.toml - pyproject.toml
- uv.lock - uv.lock
- mkdocs.yml
- mkdocs.env.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/docs.py
langs: langs:
needs: needs:
- changes - changes
if: ${{ needs.changes.outputs.docs == 'true' }}
runs-on: ubuntu-latest runs-on: ubuntu-latest
outputs: outputs:
langs: ${{ steps.show-langs.outputs.langs }} langs: ${{ steps.show-langs.outputs.langs }}
@ -103,21 +102,28 @@ jobs:
run: uv run ./scripts/docs.py update-languages run: uv run ./scripts/docs.py update-languages
- uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 - uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
with: with:
key: mkdocs-cards-${{ matrix.lang }}-${{ github.ref }} key: zensical-${{ matrix.lang }}-${{ github.ref }}
path: docs/${{ matrix.lang }}/.cache path: site_zensical_src/${{ matrix.lang }}/.cache
- name: Build Docs - name: Build Docs
run: | # zizmor: ignore[template-injection] - comes from trusted source run: | # zizmor: ignore[template-injection] - comes from trusted source
uv run ./scripts/docs.py build-lang ${{ matrix.lang }} uv run ./scripts/docs.py build-lang ${{ matrix.lang }}
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with: with:
name: docs-site-${{ matrix.lang }} 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 include-hidden-files: true
# https://github.com/marketplace/actions/alls-green#why # https://github.com/marketplace/actions/alls-green#why
docs-all-green: # This job does nothing and is only used for the branch protection docs-all-green: # This job does nothing and is only used for the branch protection
if: always() if: always()
needs: needs:
- langs
- build-docs - build-docs
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
@ -125,4 +131,4 @@ jobs:
uses: re-actors/alls-green@05ac9388f0aebcb5727afa17fcccfecd6f8ec5fe # v1.2.2 uses: re-actors/alls-green@05ac9388f0aebcb5727afa17fcccfecd6f8ec5fe # v1.2.2
with: with:
jobs: ${{ toJSON(needs) }} jobs: ${{ toJSON(needs) }}
allowed-skips: build-docs allowed-skips: langs, build-docs

1
.gitignore

@ -7,6 +7,7 @@ __pycache__
htmlcov htmlcov
dist dist
site site
site_zensical_src
.coverage* .coverage*
coverage.xml coverage.xml
.netlify .netlify

5
docs/de/docs/index.md

@ -1,3 +1,8 @@
---
include_yaml:
sponsors: data/sponsors.yml
---
# FastAPI { #fastapi } # FastAPI { #fastapi }
<style> <style>

1
docs/de/mkdocs.yml

@ -1 +0,0 @@
INHERIT: ../en/mkdocs.yml

6
docs/en/docs/contributing.md

@ -100,10 +100,10 @@ Go into the language directory, for the main docs in English it's at `docs/en/`:
$ cd docs/en/ $ cd docs/en/
``` ```
Then run `mkdocs` in that directory: Then run `zensical` in that directory:
```console ```console
$ mkdocs serve --dev-addr 127.0.0.1:8008 $ zensical serve --dev-addr 127.0.0.1:8008
``` ```
/// ///
@ -129,7 +129,7 @@ Completion will take effect once you restart the terminal.
### Docs Structure ### 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`. And there are extra tools/scripts in place to handle translations in `./scripts/docs.py`.

5
docs/en/docs/external-links.md

@ -1,3 +1,8 @@
---
include_yaml:
topic_repos: data/topic_repos.yml
---
# External Links # External Links
**FastAPI** has a great community constantly growing. **FastAPI** has a great community constantly growing.

10
docs/en/docs/fastapi-people.md

@ -1,6 +1,16 @@
--- ---
hide: hide:
- navigation - 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 # FastAPI People

5
docs/en/docs/index.md

@ -1,3 +1,8 @@
---
include_yaml:
sponsors: data/sponsors.yml
---
# FastAPI { #fastapi } # FastAPI { #fastapi }
<style> <style>

5
docs/en/mkdocs.env.yml

@ -1,5 +0,0 @@
# 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]

72
docs/en/mkdocs.yml

@ -1,10 +1,10 @@
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/
theme: theme:
variant: classic
name: material name: material
custom_dir: ../en/overrides custom_dir: overrides
palette: palette:
- media: (prefers-color-scheme) - media: (prefers-color-scheme)
toggle: toggle:
@ -45,38 +45,13 @@ theme:
- search.suggest - search.suggest
- toc.follow - toc.follow
icon: icon:
repo: fontawesome/brands/github-alt repo: octicons/mark-github-24
logo: img/icon-white.svg logo: img/icon-white.svg
favicon: img/favicon.png favicon: img/favicon.png
language: en language: en
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: 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: mkdocstrings:
handlers: handlers:
python: python:
@ -102,13 +77,13 @@ plugins:
nav: nav:
- FastAPI: index.md - FastAPI: index.md
- features.md - features.md
- Learn: - "":
- learn/index.md - learn/index.md
- python-types.md - python-types.md
- async.md - async.md
- environment-variables.md - environment-variables.md
- virtual-environments.md - virtual-environments.md
- Tutorial - User Guide: - "":
- tutorial/index.md - tutorial/index.md
- tutorial/first-steps.md - tutorial/first-steps.md
- tutorial/path-params.md - tutorial/path-params.md
@ -137,14 +112,14 @@ nav:
- tutorial/path-operation-configuration.md - tutorial/path-operation-configuration.md
- tutorial/encoder.md - tutorial/encoder.md
- tutorial/body-updates.md - tutorial/body-updates.md
- Dependencies: - "":
- tutorial/dependencies/index.md - tutorial/dependencies/index.md
- tutorial/dependencies/classes-as-dependencies.md - tutorial/dependencies/classes-as-dependencies.md
- tutorial/dependencies/sub-dependencies.md - tutorial/dependencies/sub-dependencies.md
- tutorial/dependencies/dependencies-in-path-operation-decorators.md - tutorial/dependencies/dependencies-in-path-operation-decorators.md
- tutorial/dependencies/global-dependencies.md - tutorial/dependencies/global-dependencies.md
- tutorial/dependencies/dependencies-with-yield.md - tutorial/dependencies/dependencies-with-yield.md
- Security: - "":
- tutorial/security/index.md - tutorial/security/index.md
- tutorial/security/first-steps.md - tutorial/security/first-steps.md
- tutorial/security/get-current-user.md - tutorial/security/get-current-user.md
@ -161,7 +136,7 @@ nav:
- tutorial/static-files.md - tutorial/static-files.md
- tutorial/testing.md - tutorial/testing.md
- tutorial/debugging.md - tutorial/debugging.md
- Advanced User Guide: - "":
- advanced/index.md - advanced/index.md
- advanced/stream-data.md - advanced/stream-data.md
- advanced/path-operation-advanced-configuration.md - advanced/path-operation-advanced-configuration.md
@ -173,7 +148,7 @@ nav:
- advanced/response-headers.md - advanced/response-headers.md
- advanced/response-change-status-code.md - advanced/response-change-status-code.md
- advanced/advanced-dependencies.md - advanced/advanced-dependencies.md
- Advanced Security: - "":
- advanced/security/index.md - advanced/security/index.md
- advanced/security/oauth2-scopes.md - advanced/security/oauth2-scopes.md
- advanced/security/http-basic-auth.md - advanced/security/http-basic-auth.md
@ -199,7 +174,7 @@ nav:
- advanced/strict-content-type.md - advanced/strict-content-type.md
- fastapi-cli.md - fastapi-cli.md
- editor-support.md - editor-support.md
- Deployment: - "":
- deployment/index.md - deployment/index.md
- deployment/versions.md - deployment/versions.md
- deployment/fastapicloud.md - deployment/fastapicloud.md
@ -209,7 +184,7 @@ nav:
- deployment/cloud.md - deployment/cloud.md
- deployment/server-workers.md - deployment/server-workers.md
- deployment/docker.md - deployment/docker.md
- How To - Recipes: - "":
- how-to/index.md - how-to/index.md
- how-to/general.md - how-to/general.md
- how-to/migrate-from-pydantic-v1-to-pydantic-v2.md - how-to/migrate-from-pydantic-v1-to-pydantic-v2.md
@ -222,7 +197,7 @@ nav:
- how-to/configure-swagger-ui.md - how-to/configure-swagger-ui.md
- how-to/testing-database.md - how-to/testing-database.md
- how-to/authentication-error-status-code.md - how-to/authentication-error-status-code.md
- Reference (Code API): - "":
- reference/index.md - reference/index.md
- reference/fastapi.md - reference/fastapi.md
- reference/parameters.md - reference/parameters.md
@ -238,7 +213,7 @@ nav:
- reference/response.md - reference/response.md
- reference/responses.md - reference/responses.md
- reference/middleware.md - reference/middleware.md
- OpenAPI: - "":
- reference/openapi/index.md - reference/openapi/index.md
- reference/openapi/docs.md - reference/openapi/docs.md
- reference/openapi/models.md - reference/openapi/models.md
@ -248,7 +223,7 @@ nav:
- reference/templating.md - reference/templating.md
- reference/testclient.md - reference/testclient.md
- fastapi-people.md - fastapi-people.md
- Resources: - "":
- resources/index.md - resources/index.md
- help-fastapi.md - help-fastapi.md
- contributing.md - contributing.md
@ -256,7 +231,7 @@ nav:
- external-links.md - external-links.md
- newsletter.md - newsletter.md
- management-tasks.md - management-tasks.md
- About: - "":
- about/index.md - about/index.md
- alternatives.md - alternatives.md
- history-design-future.md - history-design-future.md
@ -264,10 +239,7 @@ nav:
- management.md - management.md
- release-notes.md - release-notes.md
markdown_extensions: markdown_extensions:
material.extensions.preview: zensical.extensions.macros: null
targets:
include:
- '*'
abbr: null abbr: null
attr_list: null attr_list: null
footnotes: null footnotes: null
@ -312,16 +284,16 @@ markdown_extensions:
markdown_include_variants: null markdown_include_variants: null
extra: extra:
social: social:
- icon: fontawesome/brands/github-alt - icon: octicons/mark-github-24
link: https://github.com/fastapi/fastapi link: https://github.com/fastapi/fastapi
- icon: fontawesome/brands/discord - icon: fontawesome/brands/discord
link: https://discord.gg/VQjSZaeJmf link: https://discord.gg/VQjSZaeJmf
- icon: fontawesome/brands/twitter - icon: fontawesome/brands/x-twitter
link: https://x.com/fastapi link: https://x.com/fastapi
- icon: fontawesome/brands/bluesky
link: https://bsky.app/profile/fastapi.tiangolo.com
- icon: fontawesome/brands/linkedin - icon: fontawesome/brands/linkedin
link: https://www.linkedin.com/company/fastapi link: https://www.linkedin.com/company/fastapi
- icon: fontawesome/solid/globe
link: https://tiangolo.com
alternate: alternate:
- link: / - link: /
name: en - English name: en - English
@ -354,5 +326,5 @@ extra_javascript:
- js/termynal.js - js/termynal.js
- js/custom.js - js/custom.js
- js/init_kapa_widget.js - js/init_kapa_widget.js
hooks: validation:
- ../../scripts/mkdocs_hooks.py unresolved_references: false

4
docs/en/overrides/partials/copyright.html

@ -4,8 +4,8 @@
</div> </div>
{% if not config.extra.generator == false %} {% if not config.extra.generator == false %}
Made with Made with
<a href="https://squidfunk.github.io/mkdocs-material/" target="_blank" rel="noopener"> <a href="https://zensical.org" target="_blank" rel="noopener">
Material for MkDocs Zensical
</a> </a>
{% endif %} {% endif %}
</div> </div>

5
docs/es/docs/index.md

@ -1,3 +1,8 @@
---
include_yaml:
sponsors: data/sponsors.yml
---
# FastAPI { #fastapi } # FastAPI { #fastapi }
<style> <style>

1
docs/es/mkdocs.yml

@ -1 +0,0 @@
INHERIT: ../en/mkdocs.yml

5
docs/fr/docs/index.md

@ -1,3 +1,8 @@
---
include_yaml:
sponsors: data/sponsors.yml
---
# FastAPI { #fastapi } # FastAPI { #fastapi }
<style> <style>

1
docs/fr/mkdocs.yml

@ -1 +0,0 @@
INHERIT: ../en/mkdocs.yml

5
docs/ja/docs/index.md

@ -1,3 +1,8 @@
---
include_yaml:
sponsors: data/sponsors.yml
---
# FastAPI { #fastapi } # FastAPI { #fastapi }
<style> <style>

1
docs/ja/mkdocs.yml

@ -1 +0,0 @@
INHERIT: ../en/mkdocs.yml

5
docs/ko/docs/index.md

@ -1,3 +1,8 @@
---
include_yaml:
sponsors: data/sponsors.yml
---
# FastAPI { #fastapi } # FastAPI { #fastapi }
<style> <style>

1
docs/ko/mkdocs.yml

@ -1 +0,0 @@
INHERIT: ../en/mkdocs.yml

5
docs/pt/docs/index.md

@ -1,3 +1,8 @@
---
include_yaml:
sponsors: data/sponsors.yml
---
# FastAPI { #fastapi } # FastAPI { #fastapi }
<style> <style>

1
docs/pt/mkdocs.yml

@ -1 +0,0 @@
INHERIT: ../en/mkdocs.yml

5
docs/ru/docs/index.md

@ -1,3 +1,8 @@
---
include_yaml:
sponsors: data/sponsors.yml
---
# FastAPI { #fastapi } # FastAPI { #fastapi }
<style> <style>

1
docs/ru/mkdocs.yml

@ -1 +0,0 @@
INHERIT: ../en/mkdocs.yml

5
docs/tr/docs/index.md

@ -1,3 +1,8 @@
---
include_yaml:
sponsors: data/sponsors.yml
---
# FastAPI { #fastapi } # FastAPI { #fastapi }
<style> <style>

1
docs/tr/mkdocs.yml

@ -1 +0,0 @@
INHERIT: ../en/mkdocs.yml

5
docs/uk/docs/index.md

@ -1,3 +1,8 @@
---
include_yaml:
sponsors: data/sponsors.yml
---
# FastAPI { #fastapi } # FastAPI { #fastapi }
<style> <style>

1
docs/uk/mkdocs.yml

@ -1 +0,0 @@
INHERIT: ../en/mkdocs.yml

5
docs/zh-hant/docs/index.md

@ -1,3 +1,8 @@
---
include_yaml:
sponsors: data/sponsors.yml
---
# FastAPI { #fastapi } # FastAPI { #fastapi }
<style> <style>

1
docs/zh-hant/mkdocs.yml

@ -1 +0,0 @@
INHERIT: ../en/mkdocs.yml

5
docs/zh/docs/index.md

@ -1,3 +1,8 @@
---
include_yaml:
sponsors: data/sponsors.yml
---
# FastAPI { #fastapi } # FastAPI { #fastapi }
<style> <style>

1
docs/zh/mkdocs.yml

@ -1 +0,0 @@
INHERIT: ../en/mkdocs.yml

8
pyproject.toml

@ -132,21 +132,17 @@ docs = [
{ include-group = "docs-tests" }, { include-group = "docs-tests" },
"black >=25.1.0", "black >=25.1.0",
"cairosvg >=2.8.2", "cairosvg >=2.8.2",
# for MkDocs live reload
"click==8.2.1",
"griffe-typingdoc >=0.3.0", "griffe-typingdoc >=0.3.0",
"griffe-warnings-deprecated >=1.1.0", "griffe-warnings-deprecated >=1.1.0",
"jieba >=0.42.1", "jieba >=0.42.1",
"markdown-include-variants >=0.0.8", "markdown-include-variants >=0.0.8",
"mdx-include >=1.4.1,<2.0.0", "mdx-include >=1.4.1,<2.0.0",
"mkdocs-macros-plugin >=1.5.0", "mkdocstrings[python] >=1.0.3",
"mkdocs-material >=9.7.0",
"mkdocs-redirects >=1.2.1,<1.3.0",
"mkdocstrings[python] >=0.30.1",
"pillow >=11.3.0", "pillow >=11.3.0",
"python-slugify >=8.0.4", "python-slugify >=8.0.4",
"pyyaml >=5.3.1,<7.0.0", "pyyaml >=5.3.1,<7.0.0",
"typer >=0.21.1", "typer >=0.21.1",
"zensical >=0.0.42",
] ]
docs-tests = [ docs-tests = [
"httpx >=0.23.0,<1.0.0", "httpx >=0.23.0,<1.0.0",

228
scripts/docs.py

@ -10,7 +10,6 @@ from multiprocessing import Pool
from pathlib import Path from pathlib import Path
from typing import Any from typing import Any
import mkdocs.utils
import typer import typer
import yaml import yaml
from jinja2 import Template from jinja2 import Template
@ -39,10 +38,6 @@ app = typer.Typer()
mkdocs_name = "mkdocs.yml" mkdocs_name = "mkdocs.yml"
missing_translation_snippet = """
{!../../docs/missing-translation.md!}
"""
non_translated_sections = ( non_translated_sections = (
f"reference{os.sep}", f"reference{os.sep}",
"release-notes.md", "release-notes.md",
@ -58,7 +53,7 @@ docs_path = Path("docs")
en_docs_path = Path("docs/en") en_docs_path = Path("docs/en")
en_config_path: Path = en_docs_path / mkdocs_name 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() zensical_src_path = Path("site_zensical_src").absolute()
header_pattern = re.compile(r"^(#{1,6}) (.+?)(?:\s*\{\s*(#.*)\s*\})?\s*$") 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*$")
@ -105,7 +100,7 @@ def slugify(text: str) -> str:
def get_en_config() -> dict[str, Any]: 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]: def get_lang_paths() -> list[Path]:
@ -142,8 +137,6 @@ def new_lang(lang: str = typer.Argument(..., callback=lang_callback)):
typer.echo(f"The language was already created: {lang}") typer.echo(f"The language was already created: {lang}")
raise typer.Abort() raise typer.Abort()
new_path.mkdir() 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: Path = new_path / "llm-prompt.md"
new_llm_prompt_path.write_text("", encoding="utf-8") new_llm_prompt_path.write_text("", encoding="utf-8")
print(f"Successfully initialized: {new_path}") print(f"Successfully initialized: {new_path}")
@ -159,29 +152,158 @@ def build_lang(
""" """
Build the docs for a language. Build the docs for a language.
""" """
lang_path: Path = Path("docs") / lang build_zensical_lang_to_stage(lang)
if not lang_path.is_dir(): 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}") typer.echo(f"The language translation doesn't seem to exist yet: {lang}")
raise typer.Abort() 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": if lang == "en":
dist_path = site_path 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: else:
dist_path = site_path / lang dist_path = site_path / lang
shutil.rmtree(dist_path, ignore_errors=True) 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) 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 = """ index_sponsors_template = """
@ -223,7 +345,7 @@ def generate_readme_content() -> str:
match_start = re.search(r"<!-- sponsors -->", content) match_start = re.search(r"<!-- sponsors -->", content)
match_end = re.search(r"<!-- /sponsors -->", content) match_end = re.search(r"<!-- /sponsors -->", content)
sponsors_data_path = en_docs_path / "data" / "sponsors.yml" 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): if not (match_start and match_end):
raise RuntimeError("Couldn't auto-generate sponsors section") raise RuntimeError("Couldn't auto-generate sponsors section")
if not match_pre: if not match_pre:
@ -265,27 +387,33 @@ def generate_readme() -> None:
@app.command() @app.command()
def build_all() -> None: def build_all() -> None:
""" """
Build mkdocs site for en, and then build each language inside, end result is located Build the full translated docs site into ./site/.
at directory ./site/ with each language inside.
""" """
update_languages() update_languages()
shutil.rmtree(site_path, ignore_errors=True) 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 = [ langs = [
lang.name lang.name
for lang in get_lang_paths() for lang in get_lang_paths()
if (lang.is_dir() and lang.name in SUPPORTED_LANGS) if (lang.is_dir() and lang.name in SUPPORTED_LANGS)
] ]
cpu_count = os.cpu_count() or 1 process_pool_size = min(4, len(langs), os.cpu_count() or 1)
process_pool_size = cpu_count * 4
typer.echo(f"Using process pool size: {process_pool_size}") typer.echo(f"Using process pool size: {process_pool_size}")
with Pool(process_pool_size) as p: 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() @app.command()
def update_languages() -> None: 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() old_config = get_en_config()
updated_config = get_updated_config_content() updated_config = get_updated_config_content()
@ -305,7 +433,7 @@ def serve() -> None:
""" """
A quick server to preview a built site with translations. 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. This is here only to preview a site with translations already built.
@ -323,31 +451,21 @@ def serve() -> None:
@app.command() @app.command()
def live( def live() -> None:
lang: str = typer.Argument(
None, callback=lang_callback, autocompletion=complete_existing_lang
),
dirty: bool = False,
) -> None:
""" """
Serve with livereload a docs site for a specific language. Serve the English docs with livereload from the source files.
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.
""" """
# 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( 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,
) )
@ -358,7 +476,7 @@ def get_updated_config_content() -> dict[str, Any]:
# Language names sourced from https://quickref.me/iso-639-1 # Language names sourced from https://quickref.me/iso-639-1
# Contributors may wish to update or change these, e.g. to fix capitalization. # Contributors may wish to update or change these, e.g. to fix capitalization.
language_names_path = Path(__file__).parent / "../docs/language_names.yml" 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") language_names_path.read_text(encoding="utf-8")
) )
for lang_path in get_lang_paths(): for lang_path in get_lang_paths():

182
scripts/mkdocs_hooks.py

@ -1,182 +0,0 @@
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}"

186
uv.lock

@ -336,15 +336,6 @@ wheels = [
{ 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" }, { 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]] [[package]]
name = "backports-tarfile" name = "backports-tarfile"
version = "1.2.0" version = "1.2.0"
@ -354,20 +345,6 @@ wheels = [
{ 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" }, { 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]] [[package]]
name = "beartype" name = "beartype"
version = "0.22.9" version = "0.22.9"
@ -959,6 +936,15 @@ wheels = [
{ 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" }, { 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]] [[package]]
name = "defusedxml" name = "defusedxml"
version = "0.7.1" version = "0.7.1"
@ -1117,7 +1103,6 @@ dev = [
{ name = "anyio", extra = ["trio"] }, { name = "anyio", extra = ["trio"] },
{ name = "black" }, { name = "black" },
{ name = "cairosvg" }, { name = "cairosvg" },
{ name = "click" },
{ name = "coverage", extra = ["toml"] }, { name = "coverage", extra = ["toml"] },
{ name = "dirty-equals" }, { name = "dirty-equals" },
{ name = "flask" }, { name = "flask" },
@ -1129,9 +1114,6 @@ dev = [
{ name = "jieba" }, { name = "jieba" },
{ name = "markdown-include-variants" }, { name = "markdown-include-variants" },
{ name = "mdx-include" }, { name = "mdx-include" },
{ name = "mkdocs-macros-plugin" },
{ name = "mkdocs-material" },
{ name = "mkdocs-redirects" },
{ name = "mkdocstrings", extra = ["python"] }, { name = "mkdocstrings", extra = ["python"] },
{ name = "mypy" }, { name = "mypy" },
{ name = "pillow" }, { name = "pillow" },
@ -1154,27 +1136,25 @@ dev = [
{ name = "strawberry-graphql" }, { name = "strawberry-graphql" },
{ name = "ty" }, { name = "ty" },
{ name = "typer" }, { name = "typer" },
{ name = "zensical" },
{ name = "zizmor" }, { name = "zizmor" },
] ]
docs = [ docs = [
{ name = "black" }, { name = "black" },
{ name = "cairosvg" }, { name = "cairosvg" },
{ name = "click" },
{ name = "griffe-typingdoc" }, { name = "griffe-typingdoc" },
{ name = "griffe-warnings-deprecated" }, { name = "griffe-warnings-deprecated" },
{ name = "httpx" }, { name = "httpx" },
{ name = "jieba" }, { name = "jieba" },
{ name = "markdown-include-variants" }, { name = "markdown-include-variants" },
{ name = "mdx-include" }, { name = "mdx-include" },
{ name = "mkdocs-macros-plugin" },
{ name = "mkdocs-material" },
{ name = "mkdocs-redirects" },
{ name = "mkdocstrings", extra = ["python"] }, { name = "mkdocstrings", extra = ["python"] },
{ name = "pillow" }, { name = "pillow" },
{ name = "python-slugify" }, { name = "python-slugify" },
{ name = "pyyaml" }, { name = "pyyaml" },
{ name = "ruff" }, { name = "ruff" },
{ name = "typer" }, { name = "typer" },
{ name = "zensical" },
] ]
docs-tests = [ docs-tests = [
{ name = "httpx" }, { name = "httpx" },
@ -1260,7 +1240,6 @@ dev = [
{ name = "anyio", extras = ["trio"], specifier = ">=3.2.1,<5.0.0" }, { name = "anyio", extras = ["trio"], specifier = ">=3.2.1,<5.0.0" },
{ name = "black", specifier = ">=25.1.0" }, { name = "black", specifier = ">=25.1.0" },
{ name = "cairosvg", specifier = ">=2.8.2" }, { name = "cairosvg", specifier = ">=2.8.2" },
{ name = "click", specifier = "==8.2.1" },
{ name = "coverage", extras = ["toml"], specifier = ">=7.13,<8.0" }, { name = "coverage", extras = ["toml"], specifier = ">=7.13,<8.0" },
{ name = "dirty-equals", specifier = ">=0.9.0" }, { name = "dirty-equals", specifier = ">=0.9.0" },
{ name = "flask", specifier = ">=3.0.0,<4.0.0" }, { name = "flask", specifier = ">=3.0.0,<4.0.0" },
@ -1272,10 +1251,7 @@ dev = [
{ name = "jieba", specifier = ">=0.42.1" }, { name = "jieba", specifier = ">=0.42.1" },
{ name = "markdown-include-variants", specifier = ">=0.0.8" }, { name = "markdown-include-variants", specifier = ">=0.0.8" },
{ name = "mdx-include", specifier = ">=1.4.1,<2.0.0" }, { name = "mdx-include", specifier = ">=1.4.1,<2.0.0" },
{ name = "mkdocs-macros-plugin", specifier = ">=1.5.0" }, { name = "mkdocstrings", extras = ["python"], specifier = ">=1.0.3" },
{ 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 = "mypy", specifier = ">=1.14.1" }, { name = "mypy", specifier = ">=1.14.1" },
{ name = "pillow", specifier = ">=11.3.0" }, { name = "pillow", specifier = ">=11.3.0" },
{ name = "playwright", specifier = ">=1.57.0" }, { name = "playwright", specifier = ">=1.57.0" },
@ -1297,27 +1273,25 @@ dev = [
{ name = "strawberry-graphql", specifier = ">=0.200.0,<1.0.0" }, { name = "strawberry-graphql", specifier = ">=0.200.0,<1.0.0" },
{ name = "ty", specifier = ">=0.0.25" }, { name = "ty", specifier = ">=0.0.25" },
{ name = "typer", specifier = ">=0.21.1" }, { name = "typer", specifier = ">=0.21.1" },
{ name = "zensical", specifier = ">=0.0.42" },
{ name = "zizmor", specifier = ">=1.23.1" }, { name = "zizmor", specifier = ">=1.23.1" },
] ]
docs = [ docs = [
{ name = "black", specifier = ">=25.1.0" }, { name = "black", specifier = ">=25.1.0" },
{ name = "cairosvg", specifier = ">=2.8.2" }, { name = "cairosvg", specifier = ">=2.8.2" },
{ name = "click", specifier = "==8.2.1" },
{ name = "griffe-typingdoc", specifier = ">=0.3.0" }, { name = "griffe-typingdoc", specifier = ">=0.3.0" },
{ name = "griffe-warnings-deprecated", specifier = ">=1.1.0" }, { name = "griffe-warnings-deprecated", specifier = ">=1.1.0" },
{ name = "httpx", specifier = ">=0.23.0,<1.0.0" }, { name = "httpx", specifier = ">=0.23.0,<1.0.0" },
{ name = "jieba", specifier = ">=0.42.1" }, { name = "jieba", specifier = ">=0.42.1" },
{ name = "markdown-include-variants", specifier = ">=0.0.8" }, { name = "markdown-include-variants", specifier = ">=0.0.8" },
{ name = "mdx-include", specifier = ">=1.4.1,<2.0.0" }, { name = "mdx-include", specifier = ">=1.4.1,<2.0.0" },
{ name = "mkdocs-macros-plugin", specifier = ">=1.5.0" }, { name = "mkdocstrings", extras = ["python"], specifier = ">=1.0.3" },
{ 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 = "pillow", specifier = ">=11.3.0" }, { name = "pillow", specifier = ">=11.3.0" },
{ name = "python-slugify", specifier = ">=8.0.4" }, { name = "python-slugify", specifier = ">=8.0.4" },
{ name = "pyyaml", specifier = ">=5.3.1,<7.0.0" }, { name = "pyyaml", specifier = ">=5.3.1,<7.0.0" },
{ name = "ruff", specifier = ">=0.14.14" }, { name = "ruff", specifier = ">=0.14.14" },
{ name = "typer", specifier = ">=0.21.1" }, { name = "typer", specifier = ">=0.21.1" },
{ name = "zensical", specifier = ">=0.0.42" },
] ]
docs-tests = [ docs-tests = [
{ name = "httpx", specifier = ">=0.23.0,<1.0.0" }, { name = "httpx", specifier = ">=0.23.0,<1.0.0" },
@ -2074,15 +2048,6 @@ wheels = [
{ 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" }, { 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]] [[package]]
name = "httpcore" name = "httpcore"
version = "1.0.9" version = "1.0.9"
@ -2851,73 +2816,9 @@ wheels = [
{ 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" }, { 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]] [[package]]
name = "mkdocstrings" name = "mkdocstrings"
version = "1.0.2" version = "1.0.4"
source = { registry = "https://pypi.org/simple" } source = { registry = "https://pypi.org/simple" }
dependencies = [ dependencies = [
{ name = "jinja2" }, { name = "jinja2" },
@ -2927,9 +2828,9 @@ dependencies = [
{ name = "mkdocs-autorefs" }, { name = "mkdocs-autorefs" },
{ name = "pymdown-extensions" }, { 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 = [ 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] [package.optional-dependencies]
@ -3352,15 +3253,6 @@ wheels = [
{ 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" }, { 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]] [[package]]
name = "pathable" name = "pathable"
version = "0.4.4" version = "0.4.4"
@ -5159,18 +5051,6 @@ wheels = [
{ 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" }, { 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]] [[package]]
name = "temporalio" name = "temporalio"
version = "1.26.0" version = "1.26.0"
@ -6024,6 +5904,36 @@ wheels = [
{ 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" }, { 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]] [[package]]
name = "zipp" name = "zipp"
version = "3.23.0" version = "3.23.0"

Loading…
Cancel
Save