Markdown is awesome. It’s a pleasure to use it and it’s a pleasure to extend the Python Markdown module to fit your needs. Along with a bunch of official extensions, the module contains a nice API for writing your own extensions.
The API is clean and comprehensible. The reason of writing this article is that maybe somebody else will bump into the same problems like me and might find some of the solutions good enough to be used.
Problem statement
Right now I am writing in Markdown. When I want to insert an image I am doing the following ![My Image](/static/img/my_image.png)
. I specify the relative path, because when I switch to different domain names, I want to have the same picture and no broken links. So I figured it out there can be 2 ways to solve that:
- render the template before markdown does. Use your templating language for inserting a variable that will be your
MEDIA_URL
. - write a markdown extension that will fix your links
At this moment I would choose the first solution. But I implemented the second one, so I want to share some basic knowledge.
My setup
I use Flask-FlatPages for rendering articles. Internally Python markdown
module is used, and that means that you build your own extension easily.
Consists of
The extension must have:
- a class that will represent the extension and needs to inherit from
markdown.extensions.Extension
and implementextendMarkdown
method - a function called
makeExtension()
, since you will be having per module one single extension. When you will specify the name of the extension (i.e. to the configuration variables of Flask FlatPages or directly when creating the Markdown instance), it will import the module and call that function. Here I am just quoting the documentation. - some kind of processor that will perform the manipulations with your markdown text
Let’s start coding
This function also accepts a list of configs. It is useful for us, since we want to get a base URL or a media URL which we will prepend to the image source links.
There are different flavors of processors: preprocessor (before code is sent to Markdown core), treeprocessor (being an ElementTree object), postprocessor (you got your output string already). For my needs I have decided that an ElementTree is a good way to easily extract <img>
tags and insert the changed src
.
Method run()
is mandatory. The syntax is straight forward and the implementation is pretty naive. Also pay attention that we have the access to the config.
And finally the extension class:
self.config
should be a dictionary and store values of the form param_name: [param_value, description]
. In the extendMarkdown()
we are adding our treeprocessor class to Markdown TreeProcessors by specifying it’s name, an instance and where to insert it - before the end.
The extension lives here - https://gist.github.com/ana-balica/5944798.
Enabling the extension
For Flask do something like that - FLATPAGES_MARKDOWN_EXTENSIONS = ['absolute_images(base_url=http://this-important-url)']
in the config. First Markdown will search for the extension in the module directory - markdown.extensions
. If you don’t want to keep it there for some reasons, you can make the extension name contain dots (then Markdown will import it as-is) or prepend to the extension filename mdx-
(mdx-absolute_images
). But be careful that the mdx-
notation is discouraged from being used.