Skip to content

Commit

Permalink
Added files.
Browse files Browse the repository at this point in the history
  • Loading branch information
wnielson committed Apr 13, 2012
0 parents commit 10ebac5
Show file tree
Hide file tree
Showing 3 changed files with 158 additions and 0 deletions.
8 changes: 8 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# markdown-macros

This is a simple extension for [python-markdown][markdown] that makes it easy to create
custom macros using a [trac-like][trac] syntax. Check out `example.py` to see how to
use this.

[markdown]: http://freewisdom.org/projects/python-markdown
[trac]: http://trac.edgewall.org/demo-0.13/wiki/WikiFormatting#Macros
101 changes: 101 additions & 0 deletions __init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
import markdown
import re

MACRO_RE = r'\[\[(?P<macro>(?P<name>[A-Za-z0-9_^(]+)\((?P<args>.*)\))\]\]'

class BaseMacro(object):
"""
A base class for creating new macros. Extend this class, changing the
``name`` and ``key`` attributes and define your own ``handler`` function.
"""
name = 'Base macro'
key = 'Macro'

def __init__(self, inline):
self.inline = inline

def handler(self, *args, **kwargs):
pass

def render_macro(name, arguments, inline, config):
"""
Converts a macro found within a Markdown document into HTML. If the macro
fails for whatever reason, nothing is returned.
"""
for MacroKlass in config.get('macros', []):
if MacroKlass.key == name:
macro = MacroKlass(inline)
if macro.handler:
args, kwargs = [], {}
for arg in arguments.split(','):
if '=' in arg:
k, v = arg.strip().split('=', 1)
kwargs[k] = v
else:
args.append(arg.strip())
return macro.handler(*args, **kwargs)

class MacroExtension(markdown.Extension):
"""
Macro Extension for Python-Markdown.
"""
def __init__(self, config):
# set extension defaults
self.config = {'macros': []}

for conf in config:
if conf[0] == "macros":
self.config['macros'].extend(conf[1])

def extendMarkdown(self, md, md_globals):
macroPattern = MacroPattern(MACRO_RE, self.config)
md.inlinePatterns.add('macro', macroPattern, "<not_strong")
md.parser.blockprocessors.add('macro', MacroBlockParser(self.config, md.parser), '<empty')

class MacroPattern(markdown.inlinepatterns.Pattern):
"""
Matches inline macros.
"""
def __init__(self, pattern, config):
markdown.inlinepatterns.Pattern.__init__(self, pattern)
self.config = config

def handleMatch(self, m):
macro_name = m.groupdict()['name']
rendered = render_macro(macro_name, m.groupdict()['args'], True, self.config)
if rendered:
return markdown.util.etree.fromstring(rendered)
else:
logger.error("Invalid macro: %s" % macro_name)
return m.group(0)

class MacroBlockParser(markdown.blockprocessors.BlockProcessor):
"""
Matches block-level macros.
"""
def __init__(self, config, *args, **kwargs):
markdown.blockprocessors.BlockProcessor.__init__(self, *args, **kwargs)
self.config = config
self.pattern = re.compile(MACRO_RE)

def test(self, parent, block):
return (self.pattern.match(block.strip()) is not None)

def run(self, parent, blocks):
block = blocks.pop(0).strip()
m = self.pattern.match(block)
if m:
macro_name = m.groupdict()['name']
rendered = render_macro(macro_name, m.groupdict()['args'], False, self.config)
if rendered:
elem = markdown.util.etree.fromstring(rendered)
parent.append(elem)
else:
# The macro doesn't exist, so just place the macro in the
# output wrapped in a <p> tag
elem = markdown.util.etree.SubElement(parent, 'p')
elem.text = block
logger.error("Invalid macro: %s" % macro_name)

def makeExtension(config=[]):
return MacroExtension(config)
49 changes: 49 additions & 0 deletions example.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import markdown
import sys

sys.path.append('../')

import mdx_macros

class SummationMacro(mdx_macros.BaseMacro):
"""
A trivial macro that attempts to sum up a series of numbers. Example
usage::
[[Sum(4,6,2,9)]]
"""
name = 'Summation macro'
key = 'Sum'

def handler(self, *args, **kwargs):
total = 0
for arg in args:
try:
total += eval(arg)
except:
pass

if self.inline:
return "<span class='sum'>%s</span>" % total
return "<div class='sum'>%s</div>" % total

md = markdown.Markdown(
extensions=['macros'],
extension_configs={
'macros': {
'macros': [SummationMacro]
}
})

text = """
This is an example of the `macros` extension for `python-markdown`.
The sum 1+3+5+7 = [[Sum(1,3,5,7)]]. The macro was called like so: `[[Sum(1,3,5,7)]]`.
Macros can be either inline or block-level elements. For example, here
is the sum of 19923+19875 as a block-level element:
[[Sum(19923, 19875)]]
"""

print md.convert(text)

0 comments on commit 10ebac5

Please sign in to comment.