Markdown w/ Pygments: Round 2

Dig back into my vast blog archive and you will find this post: I said it, I did it. Therein you will find my bumbling, ham-fisted solution for integrating pygment code highlighting into markdown.

I just wandered back past this code today and then stumbled across the actual right way to do this.

Quoth me:

Pygments as [sic] a markdown pre-processor. Ideally one would be able to just pass this through as an extension to markdown using the functionality in the existing template tag. Unfortunately, pre-processors seem to be a different sort of animal. In the end I couldn't keep things very DRY.

Well, I was half right. Introducing this extra functionality via an extension is the ideal solution. But pre-processors are only different from extensions in that they're just one of kinds of things you can enable via extensions.

I've righted past wrongs. Here is the proof, in diff form:

--- /dev/null
+++ b/build/site/lib/
@@ -0,0 +1,9 @@
+import markdown
+from pg_md_processor import CodeBlockPreprocessor
+class PygmentsExtension(markdown.Extension):
+ def extendMarkdown(self, md, md_globals):
+ md.textPreprocessors.insert(0, CodeBlockPreprocessor())
+def makeExtension(configs=None):
+ return PygmentsExtension(configs=configs)
--- a/build/site/pocketuniverse/pygmented_markdown/templatetags/
+++ /dev/null
@@ -1,64 +0,0 @@
-from django import template
-from django.conf import settings
-from django.utils.encoding import smart_str, force_unicode
-from django.utils.safestring import mark_safe
-from markdown import Markdown
-from pg_md_processor import CodeBlockPreprocessor
-register = template.Library()
-def markdown(value, arg=''):
- """
- Runs Markdown over a given value, optionally using various
- extensions python-markdown supports.
- Syntax::
- {{ value|markdown:"extension1_name,extension2_name..." }}
- To enable safe mode, which strips raw HTML and only returns HTML
- generated by actual Markdown syntax, pass "safe" as the first
- extension in the list.
- If the version of Markdown in use does not support extensions,
- they will be silently ignored.
- """
- extensions = [e for e in arg.split(",") if e]
- if len(extensions) > 0 and extensions[0] == "safe":
- extensions = extensions[1:]
- safe_mode = True
- else:
- safe_mode = False
- return mark_safe(_pygmented_markdown(force_unicode(value), extensions, safe_mode=safe_mode))
-markdown.is_safe = True
-def _pygmented_markdown(text,
- extensions = [],
- safe_mode = False):
- extension_names = []
- extension_configs = {}
- for ext in extensions:
- pos = ext.find("(")
- if pos == -1:
- extension_names.append(ext)
- else:
- name = ext[:pos]
- extension_names.append(name)
- pairs = [x.split("=") for x in ext[pos+1:-1].split(",")]
- configs = [(x.strip(), y.strip()) for (x, y) in pairs]
- extension_configs[name] = configs
- md = Markdown(extensions=extension_names,
- extension_configs=extension_configs,
- safe_mode = safe_mode)
- md.textPreprocessors.insert(0, CodeBlockPreprocessor())
- return md.convert(text)

With that change in place I can access the pygments extension in the template like so:

{% load markup %}
{{ blogpost.body|markdown:"pygments" }}

It is a good day when a commit results in better functionality and a net drop in lines of code.

I got rid of an entire Django app and replaced it with a nine line python module. I guess it's true what they say about reading the manual (they say that you should).

I owe my enlightenment to this post on writing markdown extensions. Seek within for wisdom.