Skip to content

Instantly share code, notes, and snippets.

@jakevdp
Created April 29, 2013 13:54
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jakevdp/5481697 to your computer and use it in GitHub Desktop.
Save jakevdp/5481697 to your computer and use it in GitHub Desktop.
An experimental set of small Pelican plugins to add Jekyl-style enhancements to markdown
import warnings
import re
import itertools
from pelican.readers import MarkdownReader, EXTENSIONS
from pelican.utils import pelican_open
from pelican import signals
def split_args(argstring):
"""Split on spaces, correctly handling single and double quotes"""
args = re.split(''' (?=(?:[^'"]|'[^']*'|"[^"]*")*$)''', argstring)
R = re.compile("""(^".*"$)|(^'.*'$)""")
def strip_quotes(arg):
return arg[1:-1] if R.match(arg) else arg
return filter(strip_quotes, args)
class EnhancedMarkdownReader(MarkdownReader):
include_tags = {}
@classmethod
def register_tag(cls, tag, tag_func):
"""Register a new enhanced markdown tag."""
if tag in cls.include_tags:
warnings.warn("Enhanced Markdown: overriding tag '%s'" % tag)
cls.include_tags[tag] = tag_func
def read(self, source_path):
"""Parse content and metadata of markdown files"""
R = re.compile('\{%.*%\}')
with pelican_open(source_path) as text:
includes = [pattern.group() for pattern in R.finditer(text)]
# process all includes
for i in range(len(includes)):
args = split_args(includes[i][2:-2])
func = self.include_tags.get(args[0])
if func is not None:
includes[i] = func(*args[1:])
# add an empty string to includes so that chaining works
includes.append('')
# reconstruct string
text = ''.join(itertools.chain(*zip(R.split(text), includes)))
content = self._md.convert(text)
metadata = self._parse_metadata(self._md.Meta)
return content, metadata
def replace_reader(gen):
for ext in EnhancedMarkdownReader.file_extensions:
EXTENSIONS[ext] = EnhancedMarkdownReader
def register():
signals.initialized.connect(replace_reader)
"""
Pelican plugin which allows inclusion of images.
Example:
{% img filename.png [width height] %}
"""
import os
from enhanced_markdown import EnhancedMarkdownReader, register
def image_tag(*args):
filename = args[0]
image_url = os.path.join('/static/images/', filename)
ret = '<img src="%s"' % image_url
try:
width = args[1]
ret += ' width="%s"' % width
except IndexError:
pass
try:
height = args[2]
ret += ' height="%s"' % height
except IndexError:
pass
ret += ">\n"
return ret
EnhancedMarkdownReader.register_tag('img', image_tag)
"""
Pelican plugin which allows include of code blocks.
Example:
{% include_code my_script.py %}
"""
import os
from enhanced_markdown import EnhancedMarkdownReader, register
def include_code(*args):
filename = args[0]
# TODO: allow code directory to be specified in configuration
code_url = os.path.join('/static/code/', filename)
code_path = os.path.join('content', 'code', filename)
if not os.path.exists(code_path):
raise ValueError("path %s does not exist" % code_path)
# TODO: allow more than just python code
s = '\n``` python\n'
with open(code_path) as code:
s += ''.join(code)
s += '\n```\n[(download code)](%s)\n' % code_url
return s
EnhancedMarkdownReader.register_tag('include_code', include_code)
"""
Pelican plugin which allows inclusion of videos.
Example:
{% video url/to/video [width height] [url/to/poster] %}
"""
import os
from enhanced_markdown import EnhancedMarkdownReader, register
def video_tag(*args):
video_url = args[0]
ret = '<video'
try:
width = args[1]
ret += ' width="%s"' % width
except IndexError:
pass
try:
height = args[2]
ret += ' height="%s"' % height
except IndexError:
pass
ret += ' preload="none" controls'
try:
poster_url = args[3]
ret += ' poster="%s"' % poster_url
except IndexError:
pass
ret += ">\n"
ret += ' <source src="%s" type="video/mp4; codecs=\'avc1.42E01E, mp4a.40.2\'">\n</video>\n' % video_url
return ret
EnhancedMarkdownReader.register_tag('video', video_tag)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment