mirror of
https://github.com/fastapi/fastapi.git
synced 2026-05-30 19:29:24 -04:00
🔧 Migrate docs from MkDocs to Zensical (#15563)
This commit is contained in:
committed by
GitHub
parent
6f9dcdf61a
commit
31ced9d49e
20
.github/workflows/build-docs.yml
vendored
20
.github/workflows/build-docs.yml
vendored
@@ -34,14 +34,13 @@ jobs:
|
||||
- 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 }}
|
||||
@@ -103,21 +102,28 @@ jobs:
|
||||
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:
|
||||
@@ -125,4 +131,4 @@ jobs:
|
||||
uses: re-actors/alls-green@05ac9388f0aebcb5727afa17fcccfecd6f8ec5fe # v1.2.2
|
||||
with:
|
||||
jobs: ${{ toJSON(needs) }}
|
||||
allowed-skips: build-docs
|
||||
allowed-skips: langs, build-docs
|
||||
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -7,6 +7,7 @@ __pycache__
|
||||
htmlcov
|
||||
dist
|
||||
site
|
||||
site_zensical_src
|
||||
.coverage*
|
||||
coverage.xml
|
||||
.netlify
|
||||
|
||||
@@ -1,3 +1,8 @@
|
||||
---
|
||||
include_yaml:
|
||||
sponsors: data/sponsors.yml
|
||||
---
|
||||
|
||||
# FastAPI { #fastapi }
|
||||
|
||||
<style>
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
INHERIT: ../en/mkdocs.yml
|
||||
@@ -100,10 +100,10 @@ Go into the language directory, for the main docs in English it's at `docs/en/`:
|
||||
$ 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
|
||||
```
|
||||
|
||||
///
|
||||
@@ -129,7 +129,7 @@ Completion will take effect once you restart the terminal.
|
||||
|
||||
### 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`.
|
||||
|
||||
|
||||
@@ -1,3 +1,8 @@
|
||||
---
|
||||
include_yaml:
|
||||
topic_repos: data/topic_repos.yml
|
||||
---
|
||||
|
||||
# External Links
|
||||
|
||||
**FastAPI** has a great community constantly growing.
|
||||
|
||||
@@ -1,6 +1,16 @@
|
||||
---
|
||||
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
|
||||
|
||||
@@ -1,3 +1,8 @@
|
||||
---
|
||||
include_yaml:
|
||||
sponsors: data/sponsors.yml
|
||||
---
|
||||
|
||||
# FastAPI { #fastapi }
|
||||
|
||||
<style>
|
||||
|
||||
@@ -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]
|
||||
@@ -1,10 +1,10 @@
|
||||
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:
|
||||
@@ -45,38 +45,13 @@ theme:
|
||||
- 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:
|
||||
@@ -102,13 +77,13 @@ plugins:
|
||||
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
|
||||
@@ -137,14 +112,14 @@ nav:
|
||||
- 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
|
||||
@@ -161,7 +136,7 @@ nav:
|
||||
- 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
|
||||
@@ -173,7 +148,7 @@ nav:
|
||||
- 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
|
||||
@@ -199,7 +174,7 @@ nav:
|
||||
- advanced/strict-content-type.md
|
||||
- fastapi-cli.md
|
||||
- editor-support.md
|
||||
- Deployment:
|
||||
- "":
|
||||
- deployment/index.md
|
||||
- deployment/versions.md
|
||||
- deployment/fastapicloud.md
|
||||
@@ -209,7 +184,7 @@ nav:
|
||||
- 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
|
||||
@@ -222,7 +197,7 @@ nav:
|
||||
- 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
|
||||
@@ -238,7 +213,7 @@ nav:
|
||||
- reference/response.md
|
||||
- reference/responses.md
|
||||
- reference/middleware.md
|
||||
- OpenAPI:
|
||||
- "":
|
||||
- reference/openapi/index.md
|
||||
- reference/openapi/docs.md
|
||||
- reference/openapi/models.md
|
||||
@@ -248,7 +223,7 @@ nav:
|
||||
- reference/templating.md
|
||||
- reference/testclient.md
|
||||
- fastapi-people.md
|
||||
- Resources:
|
||||
- "":
|
||||
- resources/index.md
|
||||
- help-fastapi.md
|
||||
- contributing.md
|
||||
@@ -256,7 +231,7 @@ nav:
|
||||
- external-links.md
|
||||
- newsletter.md
|
||||
- management-tasks.md
|
||||
- About:
|
||||
- "":
|
||||
- about/index.md
|
||||
- alternatives.md
|
||||
- history-design-future.md
|
||||
@@ -264,10 +239,7 @@ nav:
|
||||
- management.md
|
||||
- release-notes.md
|
||||
markdown_extensions:
|
||||
material.extensions.preview:
|
||||
targets:
|
||||
include:
|
||||
- '*'
|
||||
zensical.extensions.macros: null
|
||||
abbr: null
|
||||
attr_list: null
|
||||
footnotes: null
|
||||
@@ -312,16 +284,16 @@ markdown_extensions:
|
||||
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
|
||||
@@ -354,5 +326,5 @@ extra_javascript:
|
||||
- js/termynal.js
|
||||
- js/custom.js
|
||||
- js/init_kapa_widget.js
|
||||
hooks:
|
||||
- ../../scripts/mkdocs_hooks.py
|
||||
validation:
|
||||
unresolved_references: false
|
||||
|
||||
@@ -4,8 +4,8 @@
|
||||
</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>
|
||||
|
||||
@@ -1,3 +1,8 @@
|
||||
---
|
||||
include_yaml:
|
||||
sponsors: data/sponsors.yml
|
||||
---
|
||||
|
||||
# FastAPI { #fastapi }
|
||||
|
||||
<style>
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
INHERIT: ../en/mkdocs.yml
|
||||
@@ -1,3 +1,8 @@
|
||||
---
|
||||
include_yaml:
|
||||
sponsors: data/sponsors.yml
|
||||
---
|
||||
|
||||
# FastAPI { #fastapi }
|
||||
|
||||
<style>
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
INHERIT: ../en/mkdocs.yml
|
||||
@@ -1,3 +1,8 @@
|
||||
---
|
||||
include_yaml:
|
||||
sponsors: data/sponsors.yml
|
||||
---
|
||||
|
||||
# FastAPI { #fastapi }
|
||||
|
||||
<style>
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
INHERIT: ../en/mkdocs.yml
|
||||
@@ -1,3 +1,8 @@
|
||||
---
|
||||
include_yaml:
|
||||
sponsors: data/sponsors.yml
|
||||
---
|
||||
|
||||
# FastAPI { #fastapi }
|
||||
|
||||
<style>
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
INHERIT: ../en/mkdocs.yml
|
||||
@@ -1,3 +1,8 @@
|
||||
---
|
||||
include_yaml:
|
||||
sponsors: data/sponsors.yml
|
||||
---
|
||||
|
||||
# FastAPI { #fastapi }
|
||||
|
||||
<style>
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
INHERIT: ../en/mkdocs.yml
|
||||
@@ -1,3 +1,8 @@
|
||||
---
|
||||
include_yaml:
|
||||
sponsors: data/sponsors.yml
|
||||
---
|
||||
|
||||
# FastAPI { #fastapi }
|
||||
|
||||
<style>
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
INHERIT: ../en/mkdocs.yml
|
||||
@@ -1,3 +1,8 @@
|
||||
---
|
||||
include_yaml:
|
||||
sponsors: data/sponsors.yml
|
||||
---
|
||||
|
||||
# FastAPI { #fastapi }
|
||||
|
||||
<style>
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
INHERIT: ../en/mkdocs.yml
|
||||
@@ -1,3 +1,8 @@
|
||||
---
|
||||
include_yaml:
|
||||
sponsors: data/sponsors.yml
|
||||
---
|
||||
|
||||
# FastAPI { #fastapi }
|
||||
|
||||
<style>
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
INHERIT: ../en/mkdocs.yml
|
||||
@@ -1,3 +1,8 @@
|
||||
---
|
||||
include_yaml:
|
||||
sponsors: data/sponsors.yml
|
||||
---
|
||||
|
||||
# FastAPI { #fastapi }
|
||||
|
||||
<style>
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
INHERIT: ../en/mkdocs.yml
|
||||
@@ -1,3 +1,8 @@
|
||||
---
|
||||
include_yaml:
|
||||
sponsors: data/sponsors.yml
|
||||
---
|
||||
|
||||
# FastAPI { #fastapi }
|
||||
|
||||
<style>
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
INHERIT: ../en/mkdocs.yml
|
||||
@@ -132,21 +132,17 @@ docs = [
|
||||
{ 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",
|
||||
|
||||
228
scripts/docs.py
228
scripts/docs.py
@@ -10,7 +10,6 @@ from multiprocessing import Pool
|
||||
from pathlib import Path
|
||||
from typing import Any
|
||||
|
||||
import mkdocs.utils
|
||||
import typer
|
||||
import yaml
|
||||
from jinja2 import Template
|
||||
@@ -39,10 +38,6 @@ app = typer.Typer()
|
||||
|
||||
mkdocs_name = "mkdocs.yml"
|
||||
|
||||
missing_translation_snippet = """
|
||||
{!../../docs/missing-translation.md!}
|
||||
"""
|
||||
|
||||
non_translated_sections = (
|
||||
f"reference{os.sep}",
|
||||
"release-notes.md",
|
||||
@@ -58,7 +53,7 @@ docs_path = Path("docs")
|
||||
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*$")
|
||||
@@ -105,7 +100,7 @@ def slugify(text: str) -> str:
|
||||
|
||||
|
||||
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]:
|
||||
@@ -142,8 +137,6 @@ def new_lang(lang: str = typer.Argument(..., callback=lang_callback)):
|
||||
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}")
|
||||
@@ -159,29 +152,158 @@ def build_lang(
|
||||
"""
|
||||
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 = """
|
||||
@@ -223,7 +345,7 @@ def generate_readme_content() -> str:
|
||||
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:
|
||||
@@ -265,27 +387,33 @@ def generate_readme() -> None:
|
||||
@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()
|
||||
@@ -305,7 +433,7 @@ def serve() -> None:
|
||||
"""
|
||||
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.
|
||||
|
||||
@@ -323,31 +451,21 @@ def serve() -> None:
|
||||
|
||||
|
||||
@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,
|
||||
)
|
||||
|
||||
|
||||
@@ -358,7 +476,7 @@ def get_updated_config_content() -> dict[str, Any]:
|
||||
# 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():
|
||||
|
||||
@@ -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
generated
186
uv.lock
generated
@@ -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" },
|
||||
]
|
||||
|
||||
[[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"
|
||||
@@ -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" },
|
||||
]
|
||||
|
||||
[[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"
|
||||
@@ -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" },
|
||||
]
|
||||
|
||||
[[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"
|
||||
@@ -1117,7 +1103,6 @@ dev = [
|
||||
{ name = "anyio", extra = ["trio"] },
|
||||
{ name = "black" },
|
||||
{ name = "cairosvg" },
|
||||
{ name = "click" },
|
||||
{ name = "coverage", extra = ["toml"] },
|
||||
{ name = "dirty-equals" },
|
||||
{ name = "flask" },
|
||||
@@ -1129,9 +1114,6 @@ dev = [
|
||||
{ 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" },
|
||||
@@ -1154,27 +1136,25 @@ dev = [
|
||||
{ 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" },
|
||||
@@ -1260,7 +1240,6 @@ dev = [
|
||||
{ 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" },
|
||||
@@ -1272,10 +1251,7 @@ dev = [
|
||||
{ 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" },
|
||||
@@ -1297,27 +1273,25 @@ dev = [
|
||||
{ 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" },
|
||||
@@ -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" },
|
||||
]
|
||||
|
||||
[[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"
|
||||
@@ -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" },
|
||||
]
|
||||
|
||||
[[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" },
|
||||
@@ -2927,9 +2828,9 @@ dependencies = [
|
||||
{ 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]
|
||||
@@ -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" },
|
||||
]
|
||||
|
||||
[[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"
|
||||
@@ -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" },
|
||||
]
|
||||
|
||||
[[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"
|
||||
@@ -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" },
|
||||
]
|
||||
|
||||
[[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"
|
||||
|
||||
Reference in New Issue
Block a user