From 3a390c764750d06202af778185e597df30b28a55 Mon Sep 17 00:00:00 2001 From: squidfunk Date: Tue, 1 Sep 2026 18:19:41 +0200 Subject: [PATCH] refactor: migrate `mkdocstrings` MkDocs plugin replacement Signed-off-by: squidfunk --- crates/zensical/src/compat/mkdocs.rs | 1 + .../src/compat/mkdocs/mkdocstrings.rs | 45 +++++++++++++++++++ crates/zensical/src/workflow.rs | 38 +--------------- python/tests/integration/test_mkdocstrings.py | 37 +++++++++++++++ 4 files changed, 85 insertions(+), 36 deletions(-) create mode 100644 crates/zensical/src/compat/mkdocs/mkdocstrings.rs create mode 100644 python/tests/integration/test_mkdocstrings.py diff --git a/crates/zensical/src/compat/mkdocs.rs b/crates/zensical/src/compat/mkdocs.rs index 1520a1d..491df5e 100644 --- a/crates/zensical/src/compat/mkdocs.rs +++ b/crates/zensical/src/compat/mkdocs.rs @@ -5,4 +5,5 @@ //! MkDocs compatibility modules. +pub mod mkdocstrings; pub mod search; diff --git a/crates/zensical/src/compat/mkdocs/mkdocstrings.rs b/crates/zensical/src/compat/mkdocs/mkdocstrings.rs new file mode 100644 index 0000000..ecc5ba3 --- /dev/null +++ b/crates/zensical/src/compat/mkdocs/mkdocstrings.rs @@ -0,0 +1,45 @@ +// Copyright (c) 2025-2026 Zensical and contributors + +// SPDX-License-Identifier: MIT +// All contributions are certified under the DCO + +//! Mkdocstrings compatibility artifacts. + +use pyo3::types::PyAnyMethods; +use pyo3::Python; +use std::fs; +use zrx::id::Id; +use zrx::stream::Signal; + +use crate::config::Config; +use crate::structure::nav::Navigation; + +// ---------------------------------------------------------------------------- +// Functions +// ---------------------------------------------------------------------------- + +/// Attach object inventory generation to the settled navigation stream. +pub(crate) fn attach(config: &Config, nav: &Signal) { + let config = config.clone(); + let _ = nav.map(move |_: &Navigation| { + let cache_dir = config.get_cache_dir(); + let cache_path = cache_dir.join("objects.inv"); + let cached = fs::read(&cache_path).ok(); + + let data = Python::attach(|py| { + let module = py.import("zensical.compat.mkdocstrings")?; + module + .call_method1("get_inventory", (cached,))? + .extract::>() + }); + + if let Ok(data) = data { + let site_dir = config.get_site_dir(); + let path = site_dir.join("objects.inv"); + let _ = fs::create_dir_all(path.parent().expect("invariant")); + let _ = fs::write(path, &data); + let _ = fs::create_dir_all(&cache_dir); + let _ = fs::write(&cache_path, &data); + } + }); +} diff --git a/crates/zensical/src/workflow.rs b/crates/zensical/src/workflow.rs index ed3cbd6..082ba7a 100644 --- a/crates/zensical/src/workflow.rs +++ b/crates/zensical/src/workflow.rs @@ -25,8 +25,6 @@ //! Workflow definitions -use pyo3::types::PyAnyMethods; -use pyo3::Python; use regex::Regex; use std::hash::{DefaultHasher, Hash, Hasher}; use std::path::{Path, PathBuf}; @@ -41,7 +39,7 @@ use zrx::stream::{ concurrent, Key, Signal, Stream, StreamTupleExt, Value, Workflow, }; -use super::compat::mkdocs::search; +use super::compat::mkdocs::{mkdocstrings, search}; use super::config::Config; use super::structure::markdown::Markdown; use super::structure::nav::Navigation; @@ -120,7 +118,7 @@ impl Main { let site = generate_site(&self.config, &page); let nav = generate_nav(&site); search::attach(&self.config, &page, &nav); - generate_object_inventory(&self.config, &nav); + mkdocstrings::attach(&self.config, &nav); let _ = render_templates(&self.config, &files, &nav); let unresolved = render_pages(&self.config, &site); validate(&self.config, self.strict, &files, &page, &unresolved); @@ -409,38 +407,6 @@ fn generate_nav(site: &Signal) -> Signal { site.map(|site: &Site| site.nav.clone()) } -/// Generate object inventory -pub fn generate_object_inventory( - config: &Config, nav: &Stream, -) { - // Retrieve inventory from Python interpreter using pyo3 - let config = config.clone(); - let _ = nav.map(move |_: &Navigation| { - let cache_dir = config.get_cache_dir(); - let cache_path = cache_dir.join("objects.inv"); - - // Load previously cached inventory, if any - let cached = fs::read(&cache_path).ok(); - - let data = Python::attach(|py| { - let module = py.import("zensical.compat.mkdocstrings")?; - module - .call_method1("get_inventory", (cached,))? - .extract::>() - }); - - // Write object inventory to disk and update cache - if let Ok(data) = data { - let site_dir = config.get_site_dir(); - let path = site_dir.join("objects.inv"); - let _ = fs::create_dir_all(path.parent().expect("invariant")); - let _ = fs::write(path, &data); - let _ = fs::create_dir_all(&cache_dir); - let _ = fs::write(&cache_path, &data); - } - }); -} - /// Render static and extra templates. pub fn render_templates( config: &Config, files: &Stream, nav: &Signal, diff --git a/python/tests/integration/test_mkdocstrings.py b/python/tests/integration/test_mkdocstrings.py new file mode 100644 index 0000000..a867e4d --- /dev/null +++ b/python/tests/integration/test_mkdocstrings.py @@ -0,0 +1,37 @@ +# Copyright (c) 2025-2026 Zensical and contributors + +# SPDX-License-Identifier: MIT +# All contributions are certified under the DCO + +"""Integration tests for MkDocs-compatible mkdocstrings artifacts.""" + +from __future__ import annotations + +from typing import TYPE_CHECKING, Any + +import zensical + +if TYPE_CHECKING: + from pathlib import Path + + +_BUILD_OPTIONS: dict[str, Any] = {"clean": False, "strict": False} + + +def test_object_inventory_is_restored_from_cache(tmp_path: Path) -> None: + """The cached inventory is published when no handler updates it.""" + docs = tmp_path / "docs" + docs.mkdir() + (docs / "index.md").write_text("# Home\n", encoding="utf-8") + config = tmp_path / "zensical.toml" + config.write_text('[project]\nsite_name = "Inventory"\n', encoding="utf-8") + + cache = tmp_path / ".cache" + cache.mkdir() + inventory = b"cached object inventory" + (cache / "objects.inv").write_bytes(inventory) + + zensical.build(str(config), _BUILD_OPTIONS) + + assert (tmp_path / "site" / "objects.inv").read_bytes() == inventory + assert (cache / "objects.inv").read_bytes() == inventory