The functionality of the script can be extended without changing the code, by inserting additional pre-processors, post-processors or inline patterns into Markdown's pipeline.
Pre-processors operate on lines of source text and are run in the
beginning. It is sufficient to write a class with a run() method that
takes a list of text lines as a parameter and returns the same or a
new list. An instance of the preprocessor can then be inserted into
the markdown pipeline. Preprocessor classes must extend
markdown.Preprocessor.
class SamplePreprocessor (markdown.Preprocessor) :
def run(self, lines) :
for i in range(len(lines)) :
if lines[i].startswith(SOMETHING) :
lines[i] = do_something(lines[i])
return lines
md_instance.preprocessors.insert(SOME_INDEX, SamplePreprocessor())
Post-processors operate on a NanoDom tree and run at the very end.
They need to implement a "run" method that takes a pointer to a NanoDom
document. Postprocessor classes must extend markdown.Postprocessor.
class SamplePostprocessor (markdown.Postprocessor):
def run(self, doc) :
doc.documentElement.appendChild(doc.createElement("new"))
md_instance.postprocessors.insert(SOME_INDEX, SamplePostprocessor())
Finally, inline patterns can be added. Each inline pattern includes a regular expression pattern and a method for turning a match object into a DOM fragment
class MyCustomPattern (markdown.Pattern) :
def handleMatch(self, m, doc) :
el = doc.createElement('custom')
el.setAttribute('stuff', m.group(3))
return el
new_pattern = MyCustomPattern(r'your regular expression here')
md_instance.inlinePatterns.insert(SOME_INDEX, new_pattern)
Preprocessors, patterns and postprocessors need to then be wrapped
together into an extension, which should be implemented as a class
that extends markdown.Extension and should over-ride the
extendMarkdown() method. extendMarkdown() is called during
markdown construction, giving the extension a pointer to the markdown
object (the md parameters) and markdown's global variables
(md_globals), which in theory gives the extension an option of
changing anything it cares to change. However, what it really should
be doing is inserting preprocessors, postprocessors and patterns into
the markdown pipeline:
def extendMarkdown(self, md, md_globals) :
self.md = md
# Insert a preprocessor before ReferencePreprocessor
index = md.preprocessors.index(md_globals['REFERENCE_PREPROCESSOR'])
preprocessor = FootnotePreprocessor(self)
preprocessor.md = md
md.preprocessors.insert(index, preprocessor)
If the extension uses any parameters that the use may want to change, they should be stored in self.config in the following format:
self.config = {parameter_1_name : [value1, description1],
parameter_2_name : [value2, description2] }
When stored this way the config parameters can be over-riden from the command line or at the time Markdown is instantiated:
markdown.py -x myextension(SOME_PARAM=2) inputfile.txt > output.txt
Note that parameters should always be assumed to be set to string values, and should be converted at the run time, e.g.
i += int(self.getConfig("SOME_PARAM"))
Each extension should ideally be placed in its own module starting
with mdx_ prefix (e.g. mdx_footnotes.py). The module must
provide a module-level function called makeExtensions that takes
as an optional parameter a list of configuration over-rides and
returns an instance of the extension. E.g.:
def makeExtension(configs=None) :
return FootnoteExtension(configs=configs)
See the implementation of footnote support distributed with the markdown script (mdxfootnotes.py) for an example of using all three in combination to provide reasonably complex additional functionality. (I use a pre-processor to collect footnote definitions, an inline pattern to handle uses of footnotes inside the text and a post-processor to attach the HTML of the actual footnotes at the end of the document) The RSS extension (mdxrss.py, also included), which generates an RSS file from markdown source is a simpler extension but provides an example of extending markdown to generate arbitrary XML rather than HTML.
See Extensions for the list of known extensions.