mirror of
https://github.com/zensical/zensical.git
synced 2026-09-24 07:15:34 +00:00
refactor: finalize scheduler and plugin integration
Signed-off-by: squidfunk <martin.donath@squidfunk.com>
This commit is contained in:
+1
-1
@@ -15,7 +15,7 @@
|
||||
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
|
||||
Vendored
+31
-1
@@ -171,6 +171,18 @@ class TestPluginShimming:
|
||||
config = self._parse_yaml(tmp_path, plugins={"glightbox": {}})
|
||||
assert GlightboxExtension.name in config["markdown_extensions"]
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"entry", ["material/meta", {"material/meta": None}]
|
||||
)
|
||||
def test_material_meta_presence_enables_defaults(
|
||||
self, tmp_path: Path, entry: object
|
||||
) -> None:
|
||||
config = self._parse_yaml(tmp_path, plugins=[entry])
|
||||
assert config["plugins"]["meta"]["config"] == {
|
||||
"enabled": True,
|
||||
"meta_file": ".meta.yml",
|
||||
}
|
||||
|
||||
def test_material_meta_plugin_is_normalized(self, tmp_path: Path) -> None:
|
||||
config = self._parse_yaml(
|
||||
tmp_path,
|
||||
@@ -183,6 +195,25 @@ class TestPluginShimming:
|
||||
}
|
||||
assert config["plugins_hash"] == cfg_module._hash(config["plugins"])
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"name",
|
||||
[
|
||||
"material/meta",
|
||||
"redirects",
|
||||
"minify",
|
||||
"literate-nav",
|
||||
"awesome-nav",
|
||||
],
|
||||
)
|
||||
def test_native_plugin_configuration_must_be_a_mapping(
|
||||
self, tmp_path: Path, name: str
|
||||
) -> None:
|
||||
with pytest.raises(
|
||||
cfg_module.ConfigurationError,
|
||||
match=rf"{name} configuration must be a mapping",
|
||||
):
|
||||
self._parse_yaml(tmp_path, plugins={name: []})
|
||||
|
||||
def test_redirects_plugin_is_normalized(self, tmp_path: Path) -> None:
|
||||
config = self._parse_yaml(
|
||||
tmp_path,
|
||||
@@ -292,7 +323,6 @@ class TestPluginShimming:
|
||||
@pytest.mark.parametrize(
|
||||
("plugin", "message"),
|
||||
[
|
||||
([], "configuration must be a mapping"),
|
||||
({"unknown": True}, "unknown awesome-nav option"),
|
||||
({"filename": 42}, "filename must be a string"),
|
||||
({"filename": ""}, "filename must not be empty"),
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
|
||||
+28
-42
@@ -1292,8 +1292,8 @@ def _convert_plugin_markdown_extensions(
|
||||
|
||||
def _convert_plugins(value: Any, config: dict) -> dict:
|
||||
"""Convert plugins configuration to something we can work with."""
|
||||
plugins = {}
|
||||
tags = []
|
||||
plugins: dict[str, Any] = {}
|
||||
tags: list[dict[str, Any]] = []
|
||||
|
||||
def add(name: str, data: Any) -> None:
|
||||
"""Preserve tags instances while retaining legacy map semantics."""
|
||||
@@ -1334,35 +1334,23 @@ def _convert_plugins(value: Any, config: dict) -> dict:
|
||||
|
||||
# Consume Material's public plugin name and normalize it to the internal
|
||||
# identifier extracted into typed Rust configuration.
|
||||
material_meta = plugins.pop("material/meta", None)
|
||||
if material_meta is None:
|
||||
meta = {"enabled": False, "meta_file": ".meta.yml"}
|
||||
else:
|
||||
meta = dict(material_meta or {})
|
||||
set_default(meta, "enabled", True, bool)
|
||||
set_default(meta, "meta_file", ".meta.yml", str)
|
||||
present, meta = _pop_plugin_config(plugins, "material/meta")
|
||||
set_default(meta, "enabled", present, bool)
|
||||
set_default(meta, "meta_file", ".meta.yml", str)
|
||||
plugins["meta"] = meta
|
||||
|
||||
# Normalize redirects into typed native configuration. The enabled flag is
|
||||
# internal; plugin presence retains MkDocs' activation semantics.
|
||||
if "redirects" not in plugins:
|
||||
redirects = {"enabled": False, "redirect_maps": {}}
|
||||
else:
|
||||
redirects = dict(plugins["redirects"] or {})
|
||||
set_default(redirects, "enabled", True, bool)
|
||||
set_default(redirects, "redirect_maps", {}, dict)
|
||||
present, redirects = _pop_plugin_config(plugins, "redirects")
|
||||
set_default(redirects, "enabled", present, bool)
|
||||
set_default(redirects, "redirect_maps", {}, dict)
|
||||
plugins["redirects"] = redirects
|
||||
|
||||
# Normalize the complete mkdocs-minify-plugin configuration surface. Asset
|
||||
# settings are retained for the dedicated copy/output stage; the inline
|
||||
# switches are Zensical extensions handled by the final HTML pass.
|
||||
minify: dict[str, Any]
|
||||
if "minify" not in plugins:
|
||||
minify = {"enabled": False}
|
||||
else:
|
||||
minify = dict(plugins["minify"] or {})
|
||||
set_default(minify, "enabled", True, bool)
|
||||
|
||||
present, minify = _pop_plugin_config(plugins, "minify")
|
||||
set_default(minify, "enabled", present, bool)
|
||||
set_default(minify, "minify_html", False, bool)
|
||||
set_default(minify, "minify_js", False, bool)
|
||||
set_default(minify, "minify_css", False, bool)
|
||||
@@ -1401,13 +1389,8 @@ def _convert_plugins(value: Any, config: dict) -> dict:
|
||||
# Normalize mkdocs-literate-nav without importing or executing the plugin.
|
||||
# Python retains extension objects and callables for the narrow Markdown
|
||||
# rendering boundary; Rust owns discovery and navigation resolution.
|
||||
literate_nav: dict[str, Any]
|
||||
if "literate-nav" not in plugins:
|
||||
literate_nav = {"enabled": False}
|
||||
else:
|
||||
literate_nav_config = plugins.pop("literate-nav")
|
||||
literate_nav = dict(literate_nav_config or {})
|
||||
set_default(literate_nav, "enabled", True, bool)
|
||||
present, literate_nav = _pop_plugin_config(plugins, "literate-nav")
|
||||
set_default(literate_nav, "enabled", present, bool)
|
||||
set_default(literate_nav, "nav_file", "SUMMARY.md", str)
|
||||
set_default(literate_nav, "implicit_index", False, bool)
|
||||
set_default(literate_nav, "tab_length", 4, int)
|
||||
@@ -1420,19 +1403,8 @@ def _convert_plugins(value: Any, config: dict) -> dict:
|
||||
|
||||
# Normalize mkdocs-awesome-nav without importing or executing the plugin.
|
||||
# Rust owns discovery, YAML parsing, matching and navigation resolution.
|
||||
awesome_nav: dict[str, Any]
|
||||
if "awesome-nav" not in plugins:
|
||||
awesome_nav = {"enabled": False}
|
||||
else:
|
||||
awesome_nav_config = plugins.pop("awesome-nav")
|
||||
if awesome_nav_config is not None and not isinstance(
|
||||
awesome_nav_config, dict
|
||||
):
|
||||
raise ConfigurationError(
|
||||
"awesome-nav configuration must be a mapping"
|
||||
)
|
||||
awesome_nav = dict(awesome_nav_config or {})
|
||||
set_default(awesome_nav, "enabled", True, bool)
|
||||
present, awesome_nav = _pop_plugin_config(plugins, "awesome-nav")
|
||||
set_default(awesome_nav, "enabled", present, bool)
|
||||
unknown = set(awesome_nav) - {"enabled", "filename", "logs"}
|
||||
if unknown:
|
||||
option = sorted(unknown)[0]
|
||||
@@ -1516,3 +1488,17 @@ def _convert_plugins(value: Any, config: dict) -> dict:
|
||||
|
||||
# Return plugins
|
||||
return plugins
|
||||
|
||||
|
||||
def _pop_plugin_config(
|
||||
plugins: dict[str, Any], name: str
|
||||
) -> tuple[bool, dict[str, Any]]:
|
||||
"""Consume one optional mapping while preserving presence semantics."""
|
||||
if name not in plugins:
|
||||
return False, {}
|
||||
value = plugins.pop(name)
|
||||
if value is None:
|
||||
return True, {}
|
||||
if not isinstance(value, dict):
|
||||
raise ConfigurationError(f"{name} configuration must be a mapping")
|
||||
return True, dict(value)
|
||||
|
||||
Reference in New Issue
Block a user