fix: support pymdownx blocks in markdown-exec (#871)

Signed-off-by: Timothée Mazzucotelli <dev@pawamoy.fr>
This commit is contained in:
Timothée Mazzucotelli
2026-08-19 16:11:56 +02:00
parent aece054f2c
commit 68e0c9845b
4 changed files with 30 additions and 18 deletions
+18 -7
View File
@@ -842,10 +842,18 @@ def _shim_autorefs(config: dict[str, Any]) -> None:
def _shim_markdown_exec(config: dict[str, Any]) -> None:
# Map markdown-exec plugin configuration to superfences configuration
if "markdown-exec" in config["plugins"]:
try:
import markdown_exec # noqa: PLC0415 # ty:ignore[unresolved-import]
except ImportError as error:
raise ConfigurationError(
"markdown-exec plugin is enabled, but markdown-exec is not "
"installed. Please install markdown-exec or disable the plugin."
) from error
markdown_exec_config = config["plugins"]["markdown-exec"]["config"]
enabled = markdown_exec_config.pop("enabled", True)
languages = markdown_exec_config.get("languages", None)
if enabled and (languages or languages is None):
# Check ANSI colors requirement and pygments-ansi-color presence.
trueish = ("auto", "required", True)
ansi = markdown_exec_config.get("ansi", False) in trueish
if ansi and not find_spec("pygments_ansi_color"):
@@ -854,6 +862,7 @@ def _shim_markdown_exec(config: dict[str, Any]) -> None:
"installed. Please install pygments-ansi-color or turn off "
"ANSI requirement in markdown-exec configuration."
)
# Update superfences configuration.
if "pymdownx.superfences" not in config["markdown_extensions"]:
config["markdown_extensions"].append("pymdownx.superfences")
if "pymdownx.superfences" not in config["mdx_configs"]:
@@ -864,6 +873,13 @@ def _shim_markdown_exec(config: dict[str, Any]) -> None:
superfences["custom_fences"].extend(
_get_markdown_exec_superfences(languages)
)
# Save configuration in markdown-exec.
# We must pass the original, mutable references,
# since other shims and our `render` function might modify them.
markdown_exec.markdown_config.save(
config["markdown_extensions"],
config["mdx_configs"],
)
def _shim_mkdocstrings(config: dict[str, Any]) -> None:
@@ -920,13 +936,8 @@ def _get_markdown_exec_superfences(
languages: list[str] | None = None,
) -> list[dict[str, Any]]:
"""Get superfences configuration for markdown-exec."""
try:
import markdown_exec # noqa: PLC0415 # ty:ignore[unresolved-import]
except ImportError as error:
raise ConfigurationError(
"markdown-exec plugin is enabled, but markdown-exec is not "
"installed. Please install markdown-exec or disable the plugin."
) from error
import markdown_exec # noqa: PLC0415 # ty:ignore[unresolved-import]
return [
{
"name": language,
-3
View File
@@ -94,9 +94,6 @@ class ContextExtension(Extension):
def extendMarkdown(self, md: Markdown) -> None:
"""Register rendering context preprocessor."""
# We must register the extension to ensure markdown-exec
# is able to forward it to its inner Markdown instances
md.registerExtension(self)
preprocessor = ContextPreprocessor(md=md, **self._kwargs)
md.preprocessors.register(preprocessor, preprocessor.name, 0)
-5
View File
@@ -101,11 +101,6 @@ class SearchExtension(Extension):
def extendMarkdown(self, md: Markdown) -> None:
"""Register the PostProcessor with Markdown."""
# We explicitly do not register the extension,
# because it only needs to run in the top conversion layer
# (and not in inner layers like mkdocstrings' or markdown-exec's).
# Indeed it runs last as a postprocessor,
# so is able to see everything generated by inner layers.
config = SearchConfig(**self._kwargs)
processor = SearchProcessor(md, config)
md.postprocessors.register(processor, processor.name, 0)
+12 -3
View File
@@ -86,10 +86,10 @@ def render(content: str, path: str, url: str) -> dict:
page = Page(url=url, path=path, meta=meta)
set_autorefs_page(page)
# Update configuration to include context extension
# Update configuration to include context extension.
# It's important we mutate the global configuration here,
# to allow mkdocstrings to forward the extension
# to its inner Markdown instances
# to allow mkdocstrings and markdown-exec to forward
# the extension to their inner Markdown instances.
config = get_config()
for extension in config["markdown_extensions"]:
if isinstance(extension, ContextExtension):
@@ -110,6 +110,15 @@ def render(content: str, path: str, url: str) -> dict:
extension_configs=config["mdx_configs"],
)
# Note: mkdocstrings and markdown-exec do not need to propagate
# the links and search extensions to their inner Markdown instances:
# their postprocessors run last and can see inner layer contents.
# More importantly, inner layers *must not* run the links and search
# extensions: the inner links treeprocessor would transform links once,
# and the outer links postprocessor would transform them again.
# The search postprocessor would run twice for generated content,
# incurring a performance cost.
# Register links extension, which is equivalent to MkDocs' path resolution
# Markdown extension. This is a bandaid, until we move this to Rust
links = LinksExtension(