Python-Markdown 可以支持图像板样式的链接吗?

Can Python-Markdown support imageboard-style links?

我想为 Python-Markdown 添加一个额外的语法:如果 n 是一个正整数,>>n 应该扩展为 <a href="#post-n">n</a>。 (双尖括号 (>>) 是在 imageboard forums 中创建 link 的常规语法。)

默认情况下,Python-Markdown 将 >>n 扩展为嵌套的块引用:<blockquote><blockquote>n</blockquote></blockquote>。有没有办法从 >>n 创建 links,同时保留 blockquote 的其余默认行为?也就是说,如果x是一个正整数,>>x应该展开成一个link,但是如果x不是一个正整数,>>x应该还是展开成嵌套的块引用。

我已阅读相关维基文章:Tutorial 1 Writing Extensions for Python Markdown。根据我在 wiki 中学到的知识,我写了一个自定义扩展:

import markdown
import xml.etree.ElementTree as ET
from markdown.extensions import Extension
from markdown.inlinepatterns import Pattern


class ImageboardLinkPattern(Pattern):
    def handleMatch(self, match):
        number = match.group('number')
        # Create link.
        element = ET.Element('a', attrib={'href': f'#post-{number}'})
        element.text = f'>>{number}'
        return element


class ImageboardLinkExtension(Extension):
    def extendMarkdown(self, md):
        IMAGEBOARD_LINK_RE = '>>(?P<number>[1-9][0-9]*)'
        imageboard_link = ImageboardLinkPattern(IMAGEBOARD_LINK_RE)
        md.inlinePatterns['imageboard_link'] = imageboard_link


html = markdown.markdown('>>123',
                         extensions=[ImageboardLinkExtension()])
print(html)

然而,>>123 仍然产生 <blockquote><blockquote>123</blockquote></blockquote>。上面的实现有什么问题?

问题是您的新语法与先前存在的块引用语法冲突。如果您的扩展程序被调用,它可能会起作用。然而,由于冲突,这从未发生过。请注意,它们有五种类型的处理器。作为 documented:

  • Preprocessors alter the source before it is passed to the parser.
  • Block Processors work with blocks of text separated by blank lines.
  • Tree Processors modify the constructed ElementTree
  • Inline Processors are common tree processors for inline elements, such as *strong*.
  • Postprocessors munge of the output of the parser just before it is returned.

这里重要的是处理器的顺序是 运行。换句话说,在任何内联处理器 运行 之前,所有块处理器都是 运行。因此,blockquote 块处理器 运行 首先在您的输入上删除双尖括号,将行的其余部分包装在双 blockquote 标记中。当您的内联处理器看到文档时,您的正则表达式将不再匹配,因此永远不会被调用。

也就是说,内联处理器是实现 link 语法的正确方法。但是,您需要执行以下两项操作之一才能使其正常工作。

  1. 更改语法,使其不与任何先前存在的语法冲突;或
  2. 更改块引用行为以避免冲突。

就我个人而言,我会推荐选项 1,但我知道您正在尝试从另一个环境中实施预先存在的语法。所以,如果你想探索选项 2,那么我建议也许使 blockquote 语法更严格一些。例如,虽然这不是必需的,但推荐的语法是始终在块引用中的尖括号后插入 space。将 BlockquoteProcessor 更改为需要 space 应该相对简单,这将导致您的语法不再冲突。

这其实很简单。您可能会注意到,整个语法是通过一个相当简单的正则表达式定义的:

RE = re.compile(r'(^|\n)[ ]{0,3}>[ ]?(.*)')

您只需要重写它,以便不再接受 0 whitespace(> 而不是 >[ ]?)。首先导入和 subclass 现有处理器,然后覆盖正则表达式:

from markdown.blockprocessors import BlockquoteProcessor

class CustomBlockquoteProcessor(BlockquoteProcessor):
    RE = re.compile(r'(^|\n)[ ]{0,3}> (.*)')

最后,您只需要告诉 Markdown 使用您的自定义 class 而不是默认值。将以下内容添加到 ImageboardLinkExtension class 的 extendMarkdown 方法中:

md.parser.blockprocessors.register(CustomBlockQuoteProcessor(md.parser), 'quote', 20)

现在 blockquote 语法将不再与您的 link 语法冲突,您将有机会在文本中使用您的代码 运行。只是要小心记住,对于任何实际的块引用,始终包含现在需要的 space。