From a217195ea7f845d943cfe87dd971e122e2694277 Mon Sep 17 00:00:00 2001 From: jonathan343 Date: Fri, 26 Jun 2026 00:50:29 -0400 Subject: [PATCH 1/2] Migrate from mkdocs to zensical --- .gitignore | 3 +- Makefile | 11 +- docs/hooks/copyright.py | 6 - docs/javascript/nav-expand.js | 78 ++++++++---- mkdocs.yml | 98 --------------- requirements-docs.in | 6 +- requirements-docs.txt | 81 ++++++++++++ scripts/docs/generate_nav.py | 97 +++++++++----- zensical.toml | 229 ++++++++++++++++++++++++++++++++++ 9 files changed, 443 insertions(+), 166 deletions(-) delete mode 100644 docs/hooks/copyright.py delete mode 100644 mkdocs.yml create mode 100644 requirements-docs.txt create mode 100644 zensical.toml diff --git a/.gitignore b/.gitignore index 2b2959f..f4e85e7 100644 --- a/.gitignore +++ b/.gitignore @@ -1,9 +1,8 @@ # macOS system files .DS_Store -# MkDocs build artifacts +# Docs build artifacts site/ -docs/SUMMARY.md docs/clients/ **/docs/client/ diff --git a/Makefile b/Makefile index af29938..68347b2 100644 --- a/Makefile +++ b/Makefile @@ -1,24 +1,27 @@ DOCS_PORT ?= 8000 PYTHON_VERSION := 3.12 -.PHONY: docs docs-serve docs-clean docs-install venv +.PHONY: docs docs-serve docs-clean docs-install docs-lock venv venv: uv venv --python $(PYTHON_VERSION) +docs-lock: venv + uv pip compile requirements-docs.in -o requirements-docs.txt + docs-install: venv - uv pip install -r requirements-docs.in + uv pip install -r requirements-docs.txt uv pip install -e clients/* docs-clean: - rm -rf site docs/clients docs/SUMMARY.md + rm -rf site docs/clients docs-generate: uv run python scripts/docs/generate_all_doc_stubs.py uv run python scripts/docs/generate_nav.py docs: docs-generate - uv run mkdocs build + uv run zensical build docs-serve: @[ -d site ] || $(MAKE) docs diff --git a/docs/hooks/copyright.py b/docs/hooks/copyright.py deleted file mode 100644 index 1260def..0000000 --- a/docs/hooks/copyright.py +++ /dev/null @@ -1,6 +0,0 @@ -from datetime import datetime - - -def on_config(config, **kwargs): - config.copyright = f"Copyright © {datetime.now().year}, Amazon Web Services, Inc" - return config diff --git a/docs/javascript/nav-expand.js b/docs/javascript/nav-expand.js index 54832c2..87ba5b0 100644 --- a/docs/javascript/nav-expand.js +++ b/docs/javascript/nav-expand.js @@ -1,29 +1,59 @@ /** - * Keep API Reference nav expanded on /clients/ pages and highlight active client. - * Uses Material for MkDocs document$ observable for instant navigation compatibility. + * Keep the active client highlighted and its nav section expanded across all + * of its API reference pages (/clients//...). + * + * Zensical already auto-expands "Available Clients" and highlights the active + * client on that client's own index page, but not on its nested symbol pages + * (operations/, enums/, structures/, unions/, errors/). This restores that + * orientation so the sidebar stays anchored while browsing a client's API. */ -function expandClientsNav() { - if (!location.pathname.includes("/clients/")) return; - document.querySelectorAll(".md-nav__item--nested").forEach(function (item) { - var link = item.querySelector(":scope > .md-nav__link"); - if (link && link.textContent.trim().includes("Available Clients")) { - // Expand "All Available Clients" drop down - var toggle = item.querySelector(":scope > .md-nav__toggle"); - if (toggle) toggle.checked = true; - item.setAttribute("data-md-state", "expanded"); - - // Highlight active client - var navItems = item.querySelectorAll(".md-nav__item .md-nav__link"); - navItems.forEach(function (navLink) { - if (navLink.href && location.pathname.includes(navLink.pathname)) { - navLink.classList.add("md-nav__link--active"); - } - }); +function highlightActiveClient() { + // Match ".../clients//..." and capture the client segment. + var match = location.pathname.match(/\/clients\/([^/]+)\//); + if (!match) return; + + // Reconstruct the client's index path, e.g. "/clients/polly/", preserving + // any site base path (the docs are served under a sub-path in production). + var clientRoot = + location.pathname.slice(0, match.index) + "/clients/" + match[1] + "/"; + + // Scope to the primary sidebar only. The secondary "On this page" TOC uses + // the same link classes, and its same-page anchors would otherwise all match + // clientRoot on a client's index page and get highlighted. + var sidebar = document.querySelector(".md-nav--primary"); + if (!sidebar) return; + + // The client appears exactly once in the sidebar, so find it and stop — + // avoids scanning the rest of the nav (hundreds of links once many clients + // exist). + var links = sidebar.querySelectorAll(".md-nav__link[href]"); + var link = null; + for (var i = 0; i < links.length; i++) { + if (links[i].pathname === clientRoot && !links[i].hash) { + link = links[i]; + break; } - }); + } + if (!link) return; + + // Highlight the client whose pages we're currently viewing. + link.classList.add("md-nav__link--active"); + + // Expand every collapsible ancestor (incl. "Available Clients") so the + // highlighted link is visible. Expansion is driven by the toggle checkbox. + var item = link.closest(".md-nav__item--nested"); + while (item) { + var toggle = item.querySelector(":scope > .md-nav__toggle"); + if (toggle) toggle.checked = true; + var parent = item.parentElement; + item = parent && parent.closest(".md-nav__item--nested"); + } } -// Subscribe to Material's document$ observable for instant navigation support -document$.subscribe(expandClientsNav); -// Also run on initial page load -expandClientsNav(); +// Re-run on every instant-navigation page load; fall back to a one-time run if +// the document$ observable isn't available. +if (typeof document$ !== "undefined" && document$.subscribe) { + document$.subscribe(highlightActiveClient); +} else { + highlightActiveClient(); +} diff --git a/mkdocs.yml b/mkdocs.yml deleted file mode 100644 index 5a99545..0000000 --- a/mkdocs.yml +++ /dev/null @@ -1,98 +0,0 @@ -site_name: AWS SDK for Python -site_description: Documentation for AWS SDK for Python Clients - -repo_name: awslabs/aws-sdk-python -repo_url: https://github.com/awslabs/aws-sdk-python - -hooks: - - docs/hooks/copyright.py - -theme: - name: material - logo: assets/aws-logo-white.svg - favicon: "" - palette: - # Palette toggle for automatic mode - - media: "(prefers-color-scheme)" - scheme: default - toggle: - icon: material/brightness-auto - name: Switch to light mode - primary: white - # Palette toggle for light mode - - media: "(prefers-color-scheme: light)" - scheme: default - toggle: - icon: material/brightness-7 - name: Switch to dark mode - primary: white - # Palette toggle for dark mode - - media: "(prefers-color-scheme: dark)" - scheme: slate - toggle: - icon: material/brightness-4 - name: Switch to system preference - primary: black - features: - - navigation.indexes - - navigation.instant - - navigation.top - - search.suggest - - search.highlight - - content.code.copy - -plugins: - - search - - literate-nav: - nav_file: SUMMARY.md - - mkdocstrings: - handlers: - python: - options: - show_source: false - show_signature: true - show_signature_annotations: true - show_root_heading: true - show_root_full_path: false - show_object_full_path: false - show_symbol_type_heading: true - show_symbol_type_toc: true - show_if_no_docstring: true - show_category_heading: true - group_by_category: true - separate_signature: true - signature_crossrefs: true - filters: - - "!^_" - - "!^deserialize" - - "!^serialize" - -markdown_extensions: - - pymdownx.highlight - - pymdownx.inlinehilite - - pymdownx.snippets: - check_paths: true - - pymdownx.superfences - - pymdownx.tabbed: - alternate_style: true - - admonition - - def_list - - toc: - permalink: true - toc_depth: 3 - -extra: - social: - - icon: fontawesome/brands/github - link: https://github.com/awslabs/aws-sdk-python - -extra_javascript: - - path: javascript/nav-expand.js - defer: true - -extra_css: - - stylesheets/extra.css - -validation: - nav: - omitted_files: ignore diff --git a/requirements-docs.in b/requirements-docs.in index 655a4bf..e1fdc7d 100644 --- a/requirements-docs.in +++ b/requirements-docs.in @@ -1,4 +1,2 @@ -mkdocs==1.6.1 -mkdocstrings[python]==1.0.0 -mkdocs-material==9.7.0 -mkdocs-literate-nav==0.6.1 +mkdocstrings[python]~=1.0.4 +zensical~=0.0.46 diff --git a/requirements-docs.txt b/requirements-docs.txt new file mode 100644 index 0000000..61ae194 --- /dev/null +++ b/requirements-docs.txt @@ -0,0 +1,81 @@ +# This file was autogenerated by uv via the following command: +# uv pip compile requirements-docs.in -o requirements-docs.txt +click==8.4.2 + # via + # mkdocs + # zensical +deepmerge==2.1.0 + # via zensical +ghp-import==2.1.0 + # via mkdocs +griffelib==2.1.0 + # via mkdocstrings-python +jinja2==3.1.6 + # via + # mkdocs + # mkdocstrings + # zensical +markdown==3.10.2 + # via + # mkdocs + # mkdocs-autorefs + # mkdocstrings + # pymdown-extensions + # zensical +markupsafe==3.0.3 + # via + # jinja2 + # mkdocs + # mkdocs-autorefs + # mkdocstrings +mergedeep==1.3.4 + # via + # mkdocs + # mkdocs-get-deps +mkdocs==1.6.1 + # via + # mkdocs-autorefs + # mkdocstrings +mkdocs-autorefs==1.4.4 + # via + # mkdocstrings + # mkdocstrings-python +mkdocs-get-deps==0.2.2 + # via mkdocs +mkdocstrings==1.0.4 + # via + # -r requirements-docs.in + # mkdocstrings-python +mkdocstrings-python==2.0.5 + # via mkdocstrings +packaging==26.2 + # via mkdocs +pathspec==1.1.1 + # via mkdocs +platformdirs==4.10.0 + # via mkdocs-get-deps +pygments==2.20.0 + # via zensical +pymdown-extensions==11.0 + # via + # mkdocstrings + # zensical +python-dateutil==2.9.0.post0 + # via ghp-import +pyyaml==6.0.3 + # via + # mkdocs + # mkdocs-get-deps + # pymdown-extensions + # pyyaml-env-tag + # zensical +pyyaml-env-tag==1.1 + # via mkdocs +six==1.17.0 + # via python-dateutil +tomli==2.4.1 + # via zensical +watchdog==6.0.0 + # via mkdocs +zensical==0.0.46 + # via -r requirements-docs.in diff --git a/scripts/docs/generate_nav.py b/scripts/docs/generate_nav.py index 6bca2c0..e764644 100644 --- a/scripts/docs/generate_nav.py +++ b/scripts/docs/generate_nav.py @@ -2,8 +2,9 @@ """ Generate client documentation navigation dynamically. -Run this script before mkdocs build to generate: -docs/SUMMARY.md - Navigation file for literate-nav plugin +Run this script before `zensical build` to regenerate the `nav` block in +zensical.toml (delimited by AUTO-NAV markers) so newly added clients show up +in the sidebar without manual edits. """ import logging @@ -11,6 +12,8 @@ from pathlib import Path +from generate_all_doc_stubs import discover_clients + logging.basicConfig( level=logging.INFO, @@ -19,10 +22,54 @@ ) logger = logging.getLogger("generate_nav") +# Markers in zensical.toml that bound the generated `nav` block. +NAV_START_MARKER = "# >>> AUTO-NAV >>>" +NAV_END_MARKER = "# <<< AUTO-NAV <<<" + + +def _toml_key(value: str) -> str: + """Quote a string for use as a TOML key, escaping as a basic string.""" + escaped = value.replace("\\", "\\\\").replace('"', '\\"') + return f'"{escaped}"' + + +def build_nav_block(clients_dir: Path) -> str: + """ + Build the TOML `nav` block mirroring the curated documentation structure. + + Args: + clients_dir: Path to the clients directory. + + Returns: + The `nav = [...]` TOML snippet (without the surrounding markers). + """ + lines = [ + "nav = [", + ' { Overview = "index.md" },', + ' { Contributing = "contributing.md" },', + ' { "Available Clients" = [', + ' "clients/index.md",', + ] + + # Discover clients and add each as a nested item under Available Clients + clients = discover_clients(clients_dir) + for client in clients: + lines.append( + f" {{ {_toml_key(client.service_name)} = " + f'"clients/{client.path_name}/index.md" }},' + ) + logger.info(f"Discovered client: {client.service_name}") + + lines.append(" ] },") + lines.append("]") + + logger.info(f"Found {len(clients)} total clients") + return "\n".join(lines) + def generate_nav(repo_root: Path) -> bool: """ - Generate navigation structure for clients using literate-nav SUMMARY.md format. + Regenerate the AUTO-NAV block in zensical.toml for all clients. Args: repo_root: Path to the repository root. @@ -37,39 +84,33 @@ def generate_nav(repo_root: Path) -> bool: logger.error(f"Clients directory not found: {clients_dir}") return False - # Build the SUMMARY.md content for literate-nav - lines = [ - "* [Overview](index.md)", - "* [Contributing](contributing.md)", - "* [Available Clients](clients/index.md)", - ] - - # Discover clients and add each as a nested item under Available Clients - client_count = 0 - for client_path in sorted(clients_dir.iterdir()): - if not (client_path / "scripts" / "docs" / "generate_doc_stubs.py").exists(): - continue + config_path = repo_root / "zensical.toml" + try: + config = config_path.read_text() + except OSError as e: + logger.error(f"Failed to read {config_path.name}: {e}") + return False - # Extract service name and path from package name - # (e.g., "aws-sdk-bedrock-runtime" -> "Bedrock Runtime" / "bedrock-runtime") - path_name = client_path.name.replace("aws-sdk-", "") - display_name = path_name.replace("-", " ").title() + if NAV_START_MARKER not in config or NAV_END_MARKER not in config: + logger.error( + f"AUTO-NAV markers not found in {config_path.name}. " + f"Expected '{NAV_START_MARKER}' and '{NAV_END_MARKER}'." + ) + return False - lines.append(f" * [{display_name}](clients/{path_name}/index.md)") - logger.info(f"Discovered client: {display_name}") - client_count += 1 + nav_block = build_nav_block(clients_dir) - logger.info(f"Found {client_count} total clients") + before, _, rest = config.partition(NAV_START_MARKER) + _, _, after = rest.partition(NAV_END_MARKER) + updated = f"{before}{NAV_START_MARKER}\n{nav_block}\n{NAV_END_MARKER}{after}" - # Write the SUMMARY.md file to the docs directory - summary_path = repo_root / "docs" / "SUMMARY.md" try: - summary_path.write_text("\n".join(lines) + "\n") + config_path.write_text(updated) except OSError as e: - logger.error(f"Failed to write SUMMARY.md: {e}") + logger.error(f"Failed to write {config_path.name}: {e}") return False - logger.info(f"✅ Generated SUMMARY.md navigation for {client_count} clients") + logger.info(f"✅ Regenerated navigation in {config_path.name}") return True diff --git a/zensical.toml b/zensical.toml new file mode 100644 index 0000000..c9846ce --- /dev/null +++ b/zensical.toml @@ -0,0 +1,229 @@ +[project] + +# General +site_name = "AWS SDK for Python" +site_description = "Documentation for AWS SDK for Python Clients" +site_author = "Amazon Web Services" +site_url = "https://docs.aws.amazon.com/sdk-for-python/v1/reference/" +copyright = """ +Copyright © 2026, Amazon Web Services, Inc +""" + +# Repository +repo_name = "awslabs/aws-sdk-python" +repo_url = "https://github.com/awslabs/aws-sdk-python" + +# Navigation. The block below is regenerated by scripts/docs/generate_nav.py +# (run via `make docs-generate`) to auto-discover clients. Do not edit by hand. +# >>> AUTO-NAV >>> +nav = [ + { Overview = "index.md" }, + { Contributing = "contributing.md" }, + { "Available Clients" = [ + "clients/index.md", + { "Bedrock Runtime" = "clients/bedrock-runtime/index.md" }, + { "Connecthealth" = "clients/connecthealth/index.md" }, + { "Lex Runtime V2" = "clients/lex-runtime-v2/index.md" }, + { "Polly" = "clients/polly/index.md" }, + { "Qbusiness" = "clients/qbusiness/index.md" }, + { "Sagemaker Runtime Http2" = "clients/sagemaker-runtime-http2/index.md" }, + { "Transcribe Streaming" = "clients/transcribe-streaming/index.md" }, + ] }, +] +# <<< AUTO-NAV <<< + + +# Extras +extra_css = ["stylesheets/extra.css"] +extra_javascript = [{ path = "javascript/nav-expand.js", defer = true }] + +# ---------------------------------------------------------------------------- +# Section for configuring theme options +# ---------------------------------------------------------------------------- +[project.theme] +language = "en" + +features = [ + # Zensical includes an announcement bar. This feature allows users to + # dismiss it when they have read the announcement. + # https://zensical.org/docs/setup/header/#announcement-bar + "announce.dismiss", + + # Code annotations allow you to add an icon with a tooltip to your + # code blocks to provide explanations at crucial points. + # https://zensical.org/docs/authoring/code-blocks/#code-annotations + "content.code.annotate", + + # This feature turns on a button in code blocks that allow users to + # copy the content to their clipboard without first selecting it. + # https://zensical.org/docs/authoring/code-blocks/#code-copy-button + "content.code.copy", + + # Code blocks can include a button to allow for the selection of line + # ranges by the user. + # https://zensical.org/docs/authoring/code-blocks/#code-selection-button + "content.code.select", + + # Zensical can render footnotes as inline tooltips, so the user can read + # the footnote without leaving the context of the document. + # https://zensical.org/docs/authoring/footnotes/#footnote-tooltips + "content.footnote.tooltips", + + # If you have many content tabs that have the same titles (e.g., "Python", + # "JavaScript", "Cobol"), this feature causes all of them to switch to + # at the same time when the user chooses their language in one. + # https://zensical.org/docs/authoring/content-tabs/#linked-content-tabs + "content.tabs.link", + + # With this feature enabled users can add tooltips to links that will be + # displayed when the mouse pointer hovers the link. + # https://zensical.org/docs/authoring/tooltips/#improved-tooltips + "content.tooltips", + + # This feature turns on navigation elements in the footer that allow the + # user to navigate to a next or previous page. + # https://zensical.org/docs/setup/footer/#navigation + "navigation.footer", + + # When section index pages are enabled, documents can be directly attached + # to sections, which is particularly useful for providing overview pages. + # https://zensical.org/docs/setup/navigation/#section-index-pages + "navigation.indexes", + + # When instant navigation is enabled, clicks on all internal links will be + # intercepted and dispatched via XHR without fully reloading the page. + # https://zensical.org/docs/setup/navigation/#instant-navigation + "navigation.instant", + + # With instant prefetching, your site will start to fetch a page once the + # user hovers over a link. This will reduce the perceived loading time + # for the user. + # https://zensical.org/docs/setup/navigation/#instant-prefetching + "navigation.instant.prefetch", + + # When navigation paths are activated, a breadcrumb navigation is rendered + # above the title of each page + # https://zensical.org/docs/setup/navigation/#navigation-path + "navigation.path", + + # A back-to-top button can be shown when the user, after scrolling down, + # starts to scroll up again. + # https://zensical.org/docs/setup/navigation/#back-to-top-button + "navigation.top", + + # When anchor tracking is enabled, the URL in the address bar is + # automatically updated with the active anchor as highlighted in the table + # of contents. + # https://zensical.org/docs/setup/navigation/#anchor-tracking + "navigation.tracking", + + # When search highlighting is enabled and a user clicks on a search result, + # Zensical will highlight all occurrences after following the link. + # https://zensical.org/docs/setup/search/#search-highlighting + "search.highlight", + + # When anchor following for the table of contents is enabled, the sidebar + # is automatically scrolled so that the active anchor is always visible. + # https://zensical.org/docs/setup/navigation/#anchor-following + "toc.follow", +] + +logo = "assets/aws-logo-white.svg" + +# Palette toggle for automatic mode +[[project.theme.palette]] +media = "(prefers-color-scheme)" +toggle.icon = "lucide/sun-moon" +toggle.name = "Switch to light mode" + +# Palette toggle for light mode +[[project.theme.palette]] +media = "(prefers-color-scheme: light)" +scheme = "default" +toggle.icon = "lucide/sun" +toggle.name = "Switch to dark mode" + +# Palette toggle for dark mode +[[project.theme.palette]] +media = "(prefers-color-scheme: dark)" +scheme = "slate" +toggle.icon = "lucide/moon" +toggle.name = "Switch to system preference" + +[[project.extra.social]] +icon = "fontawesome/brands/github" +link = "https://github.com/awslabs/aws-sdk-python" + +# ---------------------------------------------------------------------------- +# In this section you can configure the Markdown extensions that are used when +# rendering your documentation. We enable the most useful extensions by default, +# but you can customize this list to your needs. +# +# Read more: +# - https://zensical.org/docs/setup/extensions/ +# ---------------------------------------------------------------------------- +[project.markdown_extensions.abbr] +[project.markdown_extensions.admonition] +[project.markdown_extensions.attr_list] +[project.markdown_extensions.def_list] +[project.markdown_extensions.footnotes] +[project.markdown_extensions.md_in_html] +[project.markdown_extensions.toc] +permalink = true +[project.markdown_extensions.pymdownx.arithmatex] +generic = true +[project.markdown_extensions.pymdownx.betterem] +[project.markdown_extensions.pymdownx.caret] +[project.markdown_extensions.pymdownx.details] +[project.markdown_extensions.pymdownx.emoji] +emoji_generator = "zensical.extensions.emoji.to_svg" +emoji_index = "zensical.extensions.emoji.twemoji" +[project.markdown_extensions.pymdownx.highlight] +anchor_linenums = true +line_spans = "__span" +pygments_lang_class = true +[project.markdown_extensions.pymdownx.inlinehilite] +[project.markdown_extensions.pymdownx.keys] +[project.markdown_extensions.pymdownx.magiclink] +[project.markdown_extensions.pymdownx.mark] +[project.markdown_extensions.pymdownx.smartsymbols] +[project.markdown_extensions.pymdownx.superfences] +custom_fences = [ + { name = "mermaid", class = "mermaid", format = "pymdownx.superfences.fence_code_format" } +] +[project.markdown_extensions.pymdownx.tabbed] +alternate_style = true +combine_header_slug = true +[project.markdown_extensions.pymdownx.tasklist] +custom_checkbox = true +[project.markdown_extensions.pymdownx.tilde] + + +[project.plugins.mkdocstrings.handlers.python] +inventories = ["https://docs.python.org/3/objects.inv"] +paths = ["src"] + +[project.plugins.mkdocstrings.handlers.python.options] +docstring_style = "google" +inherited_members = true +show_source = false +show_signature = true +show_signature_annotations = true +show_root_heading = true +show_root_full_path = false +show_object_full_path = false +show_symbol_type_heading = true +show_symbol_type_toc = true +show_if_no_docstring = true +show_category_heading = true +group_by_category = true +separate_signature = true +signature_crossrefs = true +filters = [ + "!^_", + "!^deserialize", + "!^serialize" +] + +[project.markdown_extensions.pymdownx.snippets] +check_paths = true From ac5f40ba093fd5824916ef9b9e711298587b46e6 Mon Sep 17 00:00:00 2001 From: jonathan343 Date: Fri, 26 Jun 2026 15:57:52 -0400 Subject: [PATCH 2/2] address PR comments --- scripts/docs/generate_nav.py | 38 ++++++++++++++++++++++++++---------- zensical.toml | 13 ++++++------ 2 files changed, 35 insertions(+), 16 deletions(-) diff --git a/scripts/docs/generate_nav.py b/scripts/docs/generate_nav.py index e764644..71c77d0 100644 --- a/scripts/docs/generate_nav.py +++ b/scripts/docs/generate_nav.py @@ -10,6 +10,7 @@ import logging import sys +from datetime import datetime, timezone from pathlib import Path from generate_all_doc_stubs import discover_clients @@ -26,6 +27,19 @@ NAV_START_MARKER = "# >>> AUTO-NAV >>>" NAV_END_MARKER = "# <<< AUTO-NAV <<<" +# Markers in zensical.toml that bound the generated `copyright` line. +COPYRIGHT_START_MARKER = "# >>> AUTO-COPYRIGHT >>>" +COPYRIGHT_END_MARKER = "# <<< AUTO-COPYRIGHT <<<" + + +def _replace_block(config: str, start: str, end: str, body: str) -> str: + """Replace the content between two markers (inclusive of newlines).""" + if start not in config or end not in config: + raise ValueError(f"Markers not found. Expected '{start}' and '{end}'.") + before, _, rest = config.partition(start) + _, _, after = rest.partition(end) + return f"{before}{start}\n{body}\n{end}{after}" + def _toml_key(value: str) -> str: """Quote a string for use as a TOML key, escaping as a basic string.""" @@ -91,18 +105,22 @@ def generate_nav(repo_root: Path) -> bool: logger.error(f"Failed to read {config_path.name}: {e}") return False - if NAV_START_MARKER not in config or NAV_END_MARKER not in config: - logger.error( - f"AUTO-NAV markers not found in {config_path.name}. " - f"Expected '{NAV_START_MARKER}' and '{NAV_END_MARKER}'." + year = datetime.now(timezone.utc).year + copyright_line = ( + f'copyright = "© {year}, Amazon Web Services, Inc. ' + f'or its affiliates. All rights reserved."' + ) + try: + updated = _replace_block( + config, NAV_START_MARKER, NAV_END_MARKER, build_nav_block(clients_dir) ) + updated = _replace_block( + updated, COPYRIGHT_START_MARKER, COPYRIGHT_END_MARKER, copyright_line + ) + except ValueError as e: + logger.error(f"Failed to update {config_path.name}: {e}") return False - - nav_block = build_nav_block(clients_dir) - - before, _, rest = config.partition(NAV_START_MARKER) - _, _, after = rest.partition(NAV_END_MARKER) - updated = f"{before}{NAV_START_MARKER}\n{nav_block}\n{NAV_END_MARKER}{after}" + logger.info(f"Set copyright year to {year}") try: config_path.write_text(updated) diff --git a/zensical.toml b/zensical.toml index c9846ce..5811e42 100644 --- a/zensical.toml +++ b/zensical.toml @@ -5,9 +5,11 @@ site_name = "AWS SDK for Python" site_description = "Documentation for AWS SDK for Python Clients" site_author = "Amazon Web Services" site_url = "https://docs.aws.amazon.com/sdk-for-python/v1/reference/" -copyright = """ -Copyright © 2026, Amazon Web Services, Inc -""" +# Copyright. The year below is regenerated by scripts/docs/generate_nav.py +# (run via `make docs-generate`). Do not edit the year by hand. +# >>> AUTO-COPYRIGHT >>> +copyright = "© 2026, Amazon Web Services, Inc. or its affiliates. All rights reserved." +# <<< AUTO-COPYRIGHT <<< # Repository repo_name = "awslabs/aws-sdk-python" @@ -197,6 +199,8 @@ combine_header_slug = true [project.markdown_extensions.pymdownx.tasklist] custom_checkbox = true [project.markdown_extensions.pymdownx.tilde] +[project.markdown_extensions.pymdownx.snippets] +check_paths = true [project.plugins.mkdocstrings.handlers.python] @@ -224,6 +228,3 @@ filters = [ "!^deserialize", "!^serialize" ] - -[project.markdown_extensions.pymdownx.snippets] -check_paths = true