integrating pygments with Django
In the python world pygments is the library of choice when it comes to syntax highlighting. This is an example of integration with Django by creating a custom template filter. First, let's make a generic function in the file misc/pygmentify.py ('misc' is a Django app):
- import warnings
- warnings.simplefilter('ignore')
- from pygments.lexers import LEXERS, get_lexer_by_name
- warnings.resetwarnings()
- from pygments import highlight
- from pygments.formatters import HtmlFormatter
- from lxml import html
- def pygmentify_text(text):
- default_lang = 'text'
- # a tuple of known lexer names
- lexer_names = reduce(lambda a,b: a + b[2], LEXERS.itervalues(), ())
- # default formatter
- formatter = HtmlFormatter(encoding='utf-8', linenos='inline')
- html_node = html.fragment_fromstring(text, create_parent='div')
- new_html_node = html_node
- for code_node in html_node.findall('pre'):
- if not code_node.text:
- continue
- lang = code_node.attrib.get('lang', default_lang)
- if lang not in lexer_names:
- lang = default_lang
- lexer = get_lexer_by_name(lang, stripall=True)
- new_code_node = html.fragment_fromstring(highlight(code_node.text, lexer, formatter))
- new_html_node.replace(code_node, new_code_node)
- return html.tostring(new_html_node, encoding=unicode, method='xml')[5:-6] # need to strip the enclosing div
The whole warning disabling at the top is to work around an annoying bug in the pkg_resources module used by pygmentify's plugin system. We are using<pre> tags for our code with the custom attribute "lang=something_known_to_pygments" to specify the programming language of the snippet.
Notice the use of lxml for parsing and generating (X)HTML. The fast C module comes in handy for the type of on-the-fly highlighting that we do.
Now the custom template filter in misc/templatetags/pygmentify.py:
- from django import template
- from django.template.defaultfilters import stringfilter
- from misc.pygmentify import pygmentify_text
- register = template.Library()
- # truncate after a certain number of characters
- @register.filter
- @stringfilter
- def pygmentify(value):
- try:
- res = pygmentify_text(value)
- except Exception, e:
- print e
- print 'value="%s"' % value
- res = value
- return res
The exception handling comes in handy when debugging because Django's trace-back for template exceptions is of no use.
Our filter would be used like this in a template:
- {% load pygmentify %}
- {% autoescape off %}
- {{ entry.item.post|pygmentify|linebreaks }}
- {% endautoescape %}
The only thing missing is the CSS. We can generate the default theme from the python console:
- from pygments.formatters import HtmlFormatter
- print HtmlFormatter().get_style_defs('.highlight')
You might want to control the overflow and impose a color for the line numbers so add this:
- .highlight { background: #f8f8f8; overflow: auto; }
- .highlight .lineno { color: #000000 }
Some useful strings recognized by pygments:
- "python"
- "pycon" - python console
- "pytb" - python traceback
- "html+django" - Django template



Leave a Comment :