From 2e171f527a06877dede7cdab31641fa5e7819a9f Mon Sep 17 00:00:00 2001 From: squidfunk Date: Fri, 7 Nov 2025 13:46:14 +0100 Subject: [PATCH] workspace:fix - offline plugin breaks config parser --- python/zensical/config.py | 43 +++++++++++++++++++++------------------ 1 file changed, 23 insertions(+), 20 deletions(-) diff --git a/python/zensical/config.py b/python/zensical/config.py index e87bf05..65b2113 100644 --- a/python/zensical/config.py +++ b/python/zensical/config.py @@ -131,12 +131,12 @@ def _apply_defaults(config: dict, path: str) -> dict: raise ConfigurationError("Missing required setting: site_name") # Set site directory - config.setdefault("site_dir", "site") + set_default(config, "site_dir", "site", str) if ".." in config.get("site_dir"): raise ConfigurationError("site_dir must not contain '..'") # Set docs directory - config.setdefault("docs_dir", "docs") + set_default(config, "docs_dir", "docs", str) if ".." in config.get("docs_dir"): raise ConfigurationError("docs_dir must not contain '..'") @@ -176,7 +176,7 @@ def _apply_defaults(config: dict, path: str) -> dict: config["edit_uri"] = edit_uri.rstrip("/") # Set defaults for theme font settings - theme = config.setdefault("theme", {}) + theme = set_default(config, "theme", {}, dict) if isinstance(theme, str): theme = {"name": theme} config["theme"] = theme @@ -212,7 +212,7 @@ def _apply_defaults(config: dict, path: str) -> dict: set_default(theme["font"], "code", font["code"], str) # Set defaults for theme icons - icon = theme.setdefault("icon", {}) + icon = set_default(theme, "icon", {}, dict) set_default(icon, "repo", None, str) set_default(icon, "annotation", None, str) set_default(icon, "tag", {}, dict) @@ -242,7 +242,7 @@ def _apply_defaults(config: dict, path: str) -> dict: set_default(icon, "next", None, str) # Set defaults for theme admonition icons - admonition = icon.setdefault("admonition", {}) + admonition = set_default(icon, "admonition", {}, dict) set_default(admonition, "note", None, str) set_default(admonition, "abstract", None, str) set_default(admonition, "info", None, str) @@ -277,7 +277,7 @@ def _apply_defaults(config: dict, path: str) -> dict: set_default(toggle, "name", None, str) # Set defaults for extra settings - extra = config.setdefault("extra", {}) + extra = set_default(config, "extra", {}, dict) set_default(extra, "homepage", None, str) set_default(extra, "scope", None, str) set_default(extra, "annotate", {}, dict) @@ -436,10 +436,13 @@ def _apply_defaults(config: dict, path: str) -> dict: def set_default( entry: dict, key: str, default: Any, data_type: type = None -) -> None: +) -> any: """ Set a key to a default value if it isn't set, and optionally cast it to the specified data type. """ + if key in entry and entry[key] is None: + del entry[key] + # Set the default value if the key is not present entry.setdefault(key, default) @@ -450,6 +453,9 @@ def set_default( except (ValueError, TypeError) as e: raise ValueError(f"Failed to cast key '{key}' to {data_type}: {e}") + # Return the resulting value + return entry[key] + def _convert_extra(data: dict | list) -> dict | list: """ @@ -640,13 +646,15 @@ def _convert_plugins(value: any, config: dict) -> list: plugins[item] = {} # Define defaults for search plugin - search = plugins.setdefault("search", {}) - search.setdefault("enabled", True) - search.setdefault("separator", '[\\s\\-_,:!=\\[\\]()\\\\"`/]+|\\.(?!\\d)') + search = set_default(plugins, "search", {}, dict) + set_default(search, "enabled", True, bool) + set_default( + search, "separator", '[\\s\\-_,:!=\\[\\]()\\\\"`/]+|\\.(?!\\d)', str + ) # Define defaults for offline plugin - offline = plugins.setdefault("offline", {"enabled": False}) - offline.setdefault("enabled", True) + offline = set_default(plugins, "offline", {"enabled": False}, dict) + set_default(offline, "enabled", True, bool) # Ensure correct resolution of links when viewing the site from the # file system by disabling directory URLs @@ -660,16 +668,11 @@ def _convert_plugins(value: any, config: dict) -> list: script = "https://unpkg.com/iframe-worker/shim" config["extra"]["polyfills"].append(script) - # Ensure extra polyfills/shims use structured format - config["extra"]["polyfills"] = _convert_extra_javascript( - config["extra"]["polyfills"] - ) - # Now, add another level of indirection, by moving all plugin configuration # into a `config` property, making it compatible with Material for MkDocs. - for name, config in plugins.items(): - if not isinstance(config, dict) or "config" not in config: - plugins[name] = {"config": config} + for name, data in plugins.items(): + if not isinstance(data, dict) or "config" not in data: + plugins[name] = {"config": data} # Return plugins return plugins