From 1a1aad9cc9ad5e155d7565db7d83aa4c1eb92454 Mon Sep 17 00:00:00 2001 From: Sebastian Wagner Date: Fri, 25 Jul 2025 18:31:24 +0200 Subject: [PATCH] Configuration option for markdown extensions Introduces a new optional configuration parameter to specify any instance-specific extensions for the markdown parser based on https://github.com/moinwiki/moin/pull/1964 implements https://github.com/moinwiki/moin/issues/1957 --- docs/user/markdown.rst | 12 ++++++++++++ src/moin/config/default.py | 1 + src/moin/converters/_tests/test_markdown_in.py | 15 ++++++++++++++- .../converters/_tests/test_markdown_in_out.py | 15 ++++++++++++++- src/moin/converters/markdown_in.py | 7 ++++++- 5 files changed, 47 insertions(+), 3 deletions(-) diff --git a/docs/user/markdown.rst b/docs/user/markdown.rst index 48703426b..038e1a9d8 100644 --- a/docs/user/markdown.rst +++ b/docs/user/markdown.rst @@ -753,3 +753,15 @@ The following types are supported: .. note:: You should note that the title will be automatically capitalized. + +Instance-specific extensions +---------------------------- + +You can specify a list of additional extensions to the markdown parser per instance. +A list of potentially interesting extensions can be access in `Python-markdown's wiki `_. + +For example, to automatically link URLs: :: + + class Config(DefaultConfig): + ... + markdown_extensions = ['pymdownx.magiclink'] diff --git a/src/moin/config/default.py b/src/moin/config/default.py index 1c469c5a8..79f88b8fb 100644 --- a/src/moin/config/default.py +++ b/src/moin/config/default.py @@ -54,6 +54,7 @@ class for the benefit of the WikiConfig macro. auth_have_login = None auth_login_inputs = None _site_plugin_lists = None + markdown_extensions = [] def __init__(self): """Init Config instance""" diff --git a/src/moin/converters/_tests/test_markdown_in.py b/src/moin/converters/_tests/test_markdown_in.py index 033b20d87..af7d5166c 100644 --- a/src/moin/converters/_tests/test_markdown_in.py +++ b/src/moin/converters/_tests/test_markdown_in.py @@ -6,7 +6,9 @@ MoinMoin - Tests for moin.converters.markdown_in """ +from collections import namedtuple import pytest +from flask import Flask from . import serialize, XMLNS_RE @@ -15,13 +17,24 @@ from ..markdown_in import Converter +DefaultConfig = namedtuple("DefaultConfig", ("markdown_extensions",)) +config = DefaultConfig(markdown_extensions=[]) + + class TestConverter: namespaces = {moin_page: "", xlink: "xlink", xinclude: "xinclude", html: "html"} output_re = XMLNS_RE def setup_class(self): - self.conv = Converter() + # mock patching flask.current_app.cfg does not work here as for speccing the original object is called and that causes a "RuntimeError: working outside of application context" + app = Flask(__name__) + # DefaultConfig doesn't work here as it does not provide all the defaults required to be initialized + app.cfg = config + ctx = app.app_context() + ctx.push() + with ctx: + self.conv = Converter() data = [ ("Text", "

Text

"), diff --git a/src/moin/converters/_tests/test_markdown_in_out.py b/src/moin/converters/_tests/test_markdown_in_out.py index 36ac80b48..e46f35d75 100644 --- a/src/moin/converters/_tests/test_markdown_in_out.py +++ b/src/moin/converters/_tests/test_markdown_in_out.py @@ -7,7 +7,9 @@ MoinMoin - Tests for markdown->DOM->markdown using markdown_in and markdown_out converters """ +from collections import namedtuple import pytest +from flask import Flask from emeraldtree import ElementTree as ET @@ -18,6 +20,10 @@ from moin.converters.markdown_out import Converter as conv_out +DefaultConfig = namedtuple("DefaultConfig", ("markdown_extensions",)) +config = DefaultConfig(markdown_extensions=[]) + + class TestConverter: input_namespaces = 'xmlns="{}" xmlns:page="{}" xmlns:xlink="{}" xmlns:xinclude="{}" xmlns:html="{}"'.format( @@ -36,7 +42,14 @@ class TestConverter: output_re = XMLNS_RE def setup_class(self): - self.conv_in = conv_in() + # mock patching flask.current_app.cfg does not work here as for speccing the original object is called and that causes a "RuntimeError: working outside of application context" + app = Flask(__name__) + # DefaultConfig doesn't work here as it does not provide all the defaults required to be initialized + app.cfg = config + ctx = app.app_context() + ctx.push() + with ctx: + self.conv_in = conv_in() self.conv_out = conv_out() data = [ diff --git a/src/moin/converters/markdown_in.py b/src/moin/converters/markdown_in.py index 2d83dcef3..b2f6753f1 100644 --- a/src/moin/converters/markdown_in.py +++ b/src/moin/converters/markdown_in.py @@ -17,6 +17,7 @@ from ._util import decode_data from moin.utils.iri import Iri from moin.converters.html_in import Converter as HTML_IN_Converter +from flask import current_app from emeraldtree import ElementTree as ET @@ -558,8 +559,12 @@ def convert_invalid_p_nodes(self, node): self.convert_invalid_p_nodes(child) def __init__(self): + # The Moin configuration + self.app_configuration = current_app.cfg + self.markdown = Markdown( - extensions=[ExtraExtension(), CodeHiliteExtension(guess_lang=False), "mdx_wikilink_plus", "admonition"], + extensions=[ExtraExtension(), CodeHiliteExtension(guess_lang=False), "mdx_wikilink_plus", "admonition"] + + self.app_configuration.markdown_extensions, extension_configs={ "mdx_wikilink_plus": { "html_class": None,