Custom Link
extension for marko
markdown Python library
🏠 Go home
I generate this site using a simple Python script (currently 108 LOC) from markdown files created using obsidian. I use python-frontmatter Python library to parse the markdown with yaml header and then marko (there is a javascript implementation with the same name) to parse the markdown and generate the HTML.
In obsidian, I link the files using the usual markdown syntax and during the HTML generation I need to convert these links to HTML hyperlinks. That works out-of-box in marko but links contain addresses to markdown files (without the .md
extension) and I need append there the .html
extension.
Marko extensions
Marko allows to extend its behaviour using extensions. Extensions can be provided in the constructor of the Markdown
class. Let's call our extension LinkExtension
.
class LinkExtension:
"""TODO"""
markdown = Markdown(extensions=[LinkExtension])
The extension can specify list of Element
classes that implement element parsing. Luckily, we're not introducing a new syntax and there is already existing marko.inline.Link
class for that.
from marko.inline import Link
class LinkExtension:
elements = [Link]
Actually, we can completely ignore it and not specify it at all because the only thing we're going to modify is the rendering. We implement a render mixin class. We need to implement render_link
method which is going to be used instead of the one from marko.html_renderer.HTMLRenderer
. It accepts a single element
object that contains title
and dest
properties parsed using the Link
parser.
I simply called the parent's render_link
with an element modified so that the dest
has .html
extension. I decided to make a shallow copy of the element
object, but it works correctly also when the element is mutated. But mutation is dangerous in general, so just in case...
from copy import copy
class LinkRendererMixin:
def render_link(self, element):
new_element = copy(element)
new_element.dest = f"{new_element.dest}.html"
return super().render_link(element)
The final extension is then a simple class specifying the renderer.
class LinkExtension:
renderer_mixins = [LinkRendererMixin]
Final result
from copy import copy
from marko import Markdown
class LinkRendererMixin:
def render_link(self, element):
new_element = copy(element)
new_element.dest = f"{new_element.dest}.html"
return super().render_link(new_element)
class LinkExtension:
renderer_mixins = [LinkRendererMixin]
def main():
markdown = Markdown(extensions=[LinkExtension])
example_text = "Hello world with [link](files/another_one)"
print(markdown.convert(example_text))
if __name__ == "__main__":
main()
which output is
<p>Hello world with <a href="files/another_one.html">link</a>.</p>