From 25812f7b1b5e8b3227cacb91654ab1535767333a Mon Sep 17 00:00:00 2001 From: Dan Date: Sat, 14 Aug 2010 12:18:08 -0700 Subject: [PATCH] added tests for registering extensions, updated docs, fixed some pep8 stuff --- docs/index.rst | 6 +++- flaskext/markdown.py | 80 ++++++++++++++++++++++++++++++++++-------- tests/mdx_simple.py | 47 +++++++++++++++++++++++++ tests/test_markdown.py | 47 ++++++++++++++++++++++--- 4 files changed, 160 insertions(+), 20 deletions(-) create mode 100644 tests/mdx_simple.py diff --git a/docs/index.rst b/docs/index.rst index 53523de..7b7b54f 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -45,13 +45,17 @@ You can also do {{ mkd|markdown }} + +Optionally, you can keep a reference to the Markdown instance and use that +to register custom extensions by calling :func:`Markdown.register_extension` or +decorating the extension class with :func:`Markdown.extend` + API Reference ------------- .. autoclass:: Markdown :members: - .. _`Flask`: http://flask.pocoo.org/ .. _`Markdown`: http://www.freewisdom.org/projects/python-markdown/ .. _`Github`: http://www.github.com/dcolish/flask-markdown diff --git a/flaskext/markdown.py b/flaskext/markdown.py index 827e27f..d334b4c 100644 --- a/flaskext/markdown.py +++ b/flaskext/markdown.py @@ -18,15 +18,27 @@ You can also do:: - {{ mymarkdown | markdown}} + {{ mymarkdown|markdown}} + + +Optionally, you can keep a reference to the Markdown instance and use that +to register custom extensions by calling :func:`register_extension` or +decorating the extension class with :func:`extend` :copyright: (c) 2010 by Dan Colish. :license: BSD, MIT see LICENSE for more details. """ from __future__ import absolute_import from flask import Markup -from inspect import getmodule import markdown as md +from markdown import ( + blockprocessors, + Extension, + preprocessors, +) + + +__all__ = ['blockprocessors', 'Extension', 'Markdown', 'preprocessors'] class Markdown(object): @@ -34,12 +46,18 @@ class Markdown(object): Simple wrapper class for Markdown objects, any options that are available for markdown may be passed as keyword arguments like so:: - Markdown(app, - extensions=['footnotes'], - extension_configs={'footnotes': ('PLACE_MARKER','~~~~~~~~')}, - safe_mode=True, - output_format='html4', - ) + md = Markdown(app, + extensions=['footnotes'], + extension_configs={'footnotes': ('PLACE_MARKER','~~~~~~~~')}, + safe_mode=True, + output_format='html4', + ) + + You can then call :func:`register_extension` to load custom extensions into + the Markdown instance or use the :func:`extend` decorator + + :param app: Your Flask app instance + :param markdown_options: Keyword args for the Markdown instance """ def __init__(self, app, **markdown_options): @@ -50,19 +68,51 @@ def __init__(self, app, **markdown_options): def __call__(self, stream): return Markup(self._instance.convert(stream)) - def makeExtension(self, configs={}): + def extend(self, configs=None): """ + Decorator for registering macros + You must either force the decorated class to be imported - or define it in the same file you instanciate Markdown + or define it in the same file you instantiate Markdown. + To register a simple extension you could do:: + + from flaskext.markdown import Extension, Markdown + from preprocessors import SimplePreprocessor + markdown_instance = Markdown(app) + + @markdown_instance.make_extension() + class SimpleExtension(Extension): + def extendMarkdown(self, md, md_globals): + md.preprocessors.add('prover_block', + SimplePreprocessor(md), + '_begin') + md.registerExtension(self) + + :param configs: A dictionary of options for the extension being registered """ + def decorator(ext_cls): - return self.registerExtension(ext_cls, configs) + return self.register_extension(ext_cls, configs) return decorator - def registerExtension(self, ext_cls, configs=None): - """This will register an extension class with self._instance""" + def register_extension(self, ext_cls, configs=None): + """ + This will register an extension class with self._instance. You may pass + any additional configs required for your extension + + It is best to call this when starting your Flask app, ie.:: + + from .mdx_simpl import SimpleExtension + + md = Markdown(app) + md.register_extension(SimpleExtension) + + Any additional configuration arguments can be added to configs and will + be passed through to the extension you are registering + + :param configs: A dictionary of options for the extension being regsitered + :param ext_cls: The class name of your extension + """ instance = ext_cls() self._instance.registerExtensions([instance], configs) - module = getmodule(ext_cls) - module.makeExtension = ext_cls return ext_cls diff --git a/tests/mdx_simple.py b/tests/mdx_simple.py new file mode 100644 index 0000000..21a38a4 --- /dev/null +++ b/tests/mdx_simple.py @@ -0,0 +1,47 @@ +""" +Simple Extension for Python-Markdown +============================================= + +A simple example: + + [[[ + This is now a paragraph div + ]]] +""" +from cgi import escape +import re +import markdown + + +class SimpleExtension(markdown.Extension): + def extendMarkdown(self, md, md_globals): + md.preprocessors.add('prover_block', + SimplePreprocessor(md), + '_begin') + md.registerExtension(self) + + +class SimplePreprocessor(markdown.preprocessors.Preprocessor): + + RE = re.compile(r'(?P^\[{3,})[ ]*\n(?P.*?)' + '(?P^\]{3,})[ ]*$', + re.MULTILINE | re.DOTALL) + WRAP = """

{0}

""" + + def __init__(self, md): + markdown.preprocessors.Preprocessor.__init__(self, md) + + def run(self, lines): + text = "\n".join(lines) + while 1: + m = self.RE.search(text) + if m: + content = m.group('content').strip() + output = self.WRAP.format(escape(content, quote=True)) + placeholder = self.markdown.htmlStash.store(output, + safe=True) + text = '%s\n%s\n%s' % (text[:m.start()], placeholder, + text[m.end():]) + else: + break + return text.split("\n") diff --git a/tests/test_markdown.py b/tests/test_markdown.py index bdc8397..7e43ea3 100644 --- a/tests/test_markdown.py +++ b/tests/test_markdown.py @@ -1,12 +1,15 @@ from flask import Flask, render_template_string -from flaskext.markdown import Markdown + +from flaskext.markdown import Extension, Markdown +from mdx_simple import SimpleExtension, SimplePreprocessor + +app = Flask(__name__) +app.debug = True +md = Markdown(app) def run_client(): - app = Flask(__name__) - app.debug = True - md = Markdown(app) @app.route('/test_inline') def view_render_inline(): @@ -24,6 +27,30 @@ def view_render_in_block(): tmp = u'{% filter markdown %}This is a *markdown* block{% endfilter %}' return render_template_string(tmp) + @app.route('/test_register') + def view_render_registered_extension(): + md.register_extension(SimpleExtension) + + text = """[[[ +This is now a paragraph div +]]]""" + return render_template_string(u"{{ text|markdown }}", text=text) + + @app.route('/test_extend') + def view_render_decorated_extension(): + @md.extend() + class SimpleExtension(Extension): + def extendMarkdown(self, md, md_globals): + md.preprocessors.add('prover_block', + SimplePreprocessor(md), + '_begin') + md.registerExtension(self) + + text = """[[[ +This is now a paragraph div +]]]""" + return render_template_string(u"{{ text|markdown }}", text=text) + return app.test_client() @@ -43,3 +70,15 @@ def test_render_in_block(): client = run_client() resp = client.open('/test_in_block') assert resp.data == '

This is a markdown block

' + + +def test_render_register_extension(): + client = run_client() + resp = client.open('/test_register') + assert resp.data == "

This is now a paragraph div

" + + +def test_render_extend_decorator(): + client = run_client() + resp = client.open('/test_extend') + assert resp.data == "

This is now a paragraph div

"