From 47372051cf9724f1355b1c07c63c4beff9a5f626 Mon Sep 17 00:00:00 2001 From: Waylan Limberg Date: Thu, 31 Jul 2014 22:40:33 -0400 Subject: Update extensions for Extension.__init__ refactor Fixes #325. All extensions can now accept a dict of configs or **kwargs, not just a list of tuples. Third party extensions may want to follow suite. Extensions may only accept keyword arguments in the future. These changes still need to be documented. A couple things of note: The CodeHilite extension previously issued a DeprecationWarning if the old config key `force_linenos` was used. With thins change, a KeyError will now be raised. The `markdown.util.parseBoolValue` function gained a new argument: `preserve_none` (defaults to False), which when set to True, will pass None through unaltered (will not convert it to False). --- markdown/extensions/__init__.py | 5 +++++ markdown/extensions/abbr.py | 4 ++-- markdown/extensions/admonition.py | 5 +++-- markdown/extensions/attr_list.py | 4 ++-- markdown/extensions/codehilite.py | 23 ++++--------------- markdown/extensions/def_list.py | 4 ++-- markdown/extensions/extra.py | 4 ++-- markdown/extensions/fenced_code.py | 5 +++-- markdown/extensions/footnotes.py | 34 ++++++++++++++-------------- markdown/extensions/headerid.py | 5 +++-- markdown/extensions/meta.py | 5 +++-- markdown/extensions/nl2br.py | 5 +++-- markdown/extensions/sane_lists.py | 4 ++-- markdown/extensions/smart_strong.py | 4 ++-- markdown/extensions/smarty.py | 9 ++++---- markdown/extensions/tables.py | 5 +++-- markdown/extensions/toc.py | 44 ++++++++++++++++++------------------- markdown/extensions/wikilinks.py | 22 +++++++++---------- markdown/util.py | 11 +++++++--- tests/test_extensions.py | 37 +++++++++++++++++-------------- 20 files changed, 121 insertions(+), 118 deletions(-) diff --git a/markdown/extensions/__init__.py b/markdown/extensions/__init__.py index 296ce0c..2751eef 100644 --- a/markdown/extensions/__init__.py +++ b/markdown/extensions/__init__.py @@ -4,6 +4,7 @@ Extensions """ from __future__ import unicode_literals +from ..util import parseBoolValue class Extension(object): """ Base class for extensions to subclass. """ @@ -47,6 +48,10 @@ class Extension(object): def setConfig(self, key, value): """ Set a config setting for `key` with the given `value`. """ + if isinstance(self.config[key][0], bool): + value = parseBoolValue(value) + if self.config[key][0] is None: + value = parseBoolValue(value, preserve_none=True) self.config[key][0] = value def setConfigs(self, items): diff --git a/markdown/extensions/abbr.py b/markdown/extensions/abbr.py index bed3bb4..7f2344a 100644 --- a/markdown/extensions/abbr.py +++ b/markdown/extensions/abbr.py @@ -92,5 +92,5 @@ class AbbrPattern(Pattern): abbr.set('title', self.title) return abbr -def makeExtension(configs=None): - return AbbrExtension(configs=configs) +def makeExtension(*args, **kwargs): + return AbbrExtension(*args, **kwargs) diff --git a/markdown/extensions/admonition.py b/markdown/extensions/admonition.py index 9a45b92..7888d5e 100644 --- a/markdown/extensions/admonition.py +++ b/markdown/extensions/admonition.py @@ -114,5 +114,6 @@ class AdmonitionProcessor(BlockProcessor): return klass, title -def makeExtension(configs={}): - return AdmonitionExtension(configs=configs) +def makeExtension(*args, **kwargs): + return AdmonitionExtension(*args, **kwargs) + diff --git a/markdown/extensions/attr_list.py b/markdown/extensions/attr_list.py index f0508cd..f725a17 100644 --- a/markdown/extensions/attr_list.py +++ b/markdown/extensions/attr_list.py @@ -164,5 +164,5 @@ class AttrListExtension(Extension): md.treeprocessors.add('attr_list', AttrListTreeprocessor(md), '>prettify') -def makeExtension(configs={}): - return AttrListExtension(configs=configs) +def makeExtension(*args, **kwargs): + return AttrListExtension(*args, **kwargs) diff --git a/markdown/extensions/codehilite.py b/markdown/extensions/codehilite.py index f379da7..e3c5b3d 100644 --- a/markdown/extensions/codehilite.py +++ b/markdown/extensions/codehilite.py @@ -225,7 +225,7 @@ class HiliteTreeprocessor(Treeprocessor): class CodeHiliteExtension(Extension): """ Add source code hilighting to markdown codeblocks. """ - def __init__(self, configs): + def __init__(self, *args, **kwargs): # define default configs self.config = { 'linenums': [None, "Use lines numbers. True=yes, False=no, None=auto"], @@ -237,22 +237,7 @@ class CodeHiliteExtension(Extension): 'noclasses': [False, 'Use inline styles instead of CSS classes - Default false'] } - # Override defaults with user settings - for key, value in configs: - # convert strings to booleans - if value == 'True': value = True - if value == 'False': value = False - if value == 'None': value = None - - if key == 'force_linenos': #pragma: no cover - warnings.warn('The "force_linenos" config setting' - ' to the CodeHilite extension is deprecrecated.' - ' Use "linenums" instead.', DeprecationWarning) - if value: - # Carry 'force_linenos' over to new 'linenos'. - self.setConfig('linenums', True) - - self.setConfig(key, value) + super(CodeHiliteExtension, self).__init__(*args, **kwargs) def extendMarkdown(self, md, md_globals): """ Add HilitePostprocessor to Markdown instance. """ @@ -263,6 +248,6 @@ class CodeHiliteExtension(Extension): md.registerExtension(self) -def makeExtension(configs={}): - return CodeHiliteExtension(configs=configs) +def makeExtension(*args, **kwargs): + return CodeHiliteExtension(*args, **kwargs) diff --git a/markdown/extensions/def_list.py b/markdown/extensions/def_list.py index df639df..3511651 100644 --- a/markdown/extensions/def_list.py +++ b/markdown/extensions/def_list.py @@ -113,6 +113,6 @@ class DefListExtension(Extension): '>ulist') -def makeExtension(configs={}): - return DefListExtension(configs=configs) +def makeExtension(*args, **kwargs): + return DefListExtension(*args, **kwargs) diff --git a/markdown/extensions/extra.py b/markdown/extensions/extra.py index 8986ba6..6fba8db 100644 --- a/markdown/extensions/extra.py +++ b/markdown/extensions/extra.py @@ -60,8 +60,8 @@ class ExtraExtension(Extension): r'^(p|h[1-6]|li|dd|dt|td|th|legend|address)$', re.IGNORECASE) -def makeExtension(configs={}): - return ExtraExtension(configs=dict(configs)) +def makeExtension(*args, **kwargs): + return ExtraExtension(*args, **kwargs) class MarkdownInHtmlProcessor(BlockProcessor): diff --git a/markdown/extensions/fenced_code.py b/markdown/extensions/fenced_code.py index fe1559c..25163cf 100644 --- a/markdown/extensions/fenced_code.py +++ b/markdown/extensions/fenced_code.py @@ -175,5 +175,6 @@ class FencedBlockPreprocessor(Preprocessor): return txt -def makeExtension(configs=None): - return FencedCodeExtension(configs=configs) +def makeExtension(*args, **kwargs): + return FencedCodeExtension(*args, **kwargs) + diff --git a/markdown/extensions/footnotes.py b/markdown/extensions/footnotes.py index 9f93ad1..cec0d71 100644 --- a/markdown/extensions/footnotes.py +++ b/markdown/extensions/footnotes.py @@ -42,23 +42,23 @@ TABBED_RE = re.compile(r'((\t)|( ))(.*)') class FootnoteExtension(Extension): """ Footnote Extension. """ - def __init__ (self, configs): + def __init__ (self, *args, **kwargs): """ Setup configs. """ - self.config = {'PLACE_MARKER': - ["///Footnotes Go Here///", - "The text string that marks where the footnotes go"], - 'UNIQUE_IDS': - [False, - "Avoid name collisions across " - "multiple calls to reset()."], - "BACKLINK_TEXT": - ["↩", - "The text string that links from the footnote to the reader's place."] - } - - for key, value in configs: - self.config[key][0] = value + self.config = { + 'PLACE_MARKER': + ["///Footnotes Go Here///", + "The text string that marks where the footnotes go"], + 'UNIQUE_IDS': + [False, + "Avoid name collisions across " + "multiple calls to reset()."], + "BACKLINK_TEXT": + ["↩", + "The text string that links from the footnote to the reader's place."] + } + super(FootnoteExtension, self).__init__(*args, **kwargs) + # In multiple invocations, emit links that don't get tangled. self.unique_prefix = 0 @@ -309,7 +309,7 @@ class FootnotePostprocessor(Postprocessor): text = text.replace(FN_BACKLINK_TEXT, self.footnotes.getConfig("BACKLINK_TEXT")) return text.replace(NBSP_PLACEHOLDER, " ") -def makeExtension(configs=[]): +def makeExtension(*args, **kwargs): """ Return an instance of the FootnoteExtension """ - return FootnoteExtension(configs=configs) + return FootnoteExtension(*args, **kwargs) diff --git a/markdown/extensions/headerid.py b/markdown/extensions/headerid.py index 07351cc..55e09ce 100644 --- a/markdown/extensions/headerid.py +++ b/markdown/extensions/headerid.py @@ -204,5 +204,6 @@ class HeaderIdExtension(Extension): self.processor.IDs = set() -def makeExtension(configs=None): - return HeaderIdExtension(configs=configs) +def makeExtension(*args, **kwargs): + return HeaderIdExtension(*args, **kwargs) + diff --git a/markdown/extensions/meta.py b/markdown/extensions/meta.py index 9063188..8de2646 100644 --- a/markdown/extensions/meta.py +++ b/markdown/extensions/meta.py @@ -89,5 +89,6 @@ class MetaPreprocessor(Preprocessor): return lines -def makeExtension(configs={}): - return MetaExtension(configs=configs) +def makeExtension(*args, **kwargs): + return MetaExtension(*args, **kwargs) + diff --git a/markdown/extensions/nl2br.py b/markdown/extensions/nl2br.py index 028561c..c355c13 100644 --- a/markdown/extensions/nl2br.py +++ b/markdown/extensions/nl2br.py @@ -34,5 +34,6 @@ class Nl2BrExtension(Extension): md.inlinePatterns.add('nl', br_tag, '_end') -def makeExtension(configs=None): - return Nl2BrExtension(configs) +def makeExtension(*args, **kwargs): + return Nl2BrExtension(*args, **kwargs) + diff --git a/markdown/extensions/sane_lists.py b/markdown/extensions/sane_lists.py index fda6638..22a4ff3 100644 --- a/markdown/extensions/sane_lists.py +++ b/markdown/extensions/sane_lists.py @@ -46,6 +46,6 @@ class SaneListExtension(Extension): md.parser.blockprocessors['ulist'] = SaneUListProcessor(md.parser) -def makeExtension(configs={}): - return SaneListExtension(configs=configs) +def makeExtension(*args, **kwargs): + return SaneListExtension(*args, **kwargs) diff --git a/markdown/extensions/smart_strong.py b/markdown/extensions/smart_strong.py index c7ac9f1..877b5a7 100644 --- a/markdown/extensions/smart_strong.py +++ b/markdown/extensions/smart_strong.py @@ -38,5 +38,5 @@ class SmartEmphasisExtension(Extension): md.inlinePatterns['strong'] = SimpleTagPattern(STRONG_RE, 'strong') md.inlinePatterns.add('strong2', SimpleTagPattern(SMART_STRONG_RE, 'strong'), '>emphasis2') -def makeExtension(configs={}): - return SmartEmphasisExtension(configs=dict(configs)) +def makeExtension(*args, **kwargs): + return SmartEmphasisExtension(*args, **kwargs) diff --git a/markdown/extensions/smarty.py b/markdown/extensions/smarty.py index 8131591..5a82dfd 100644 --- a/markdown/extensions/smarty.py +++ b/markdown/extensions/smarty.py @@ -134,15 +134,14 @@ class SubstituteTextPattern(HtmlPattern): return result class SmartyExtension(Extension): - def __init__(self, configs): + def __init__(self, *args, **kwargs): self.config = { 'smart_quotes': [True, 'Educate quotes'], 'smart_angled_quotes': [False, 'Educate angled quotes'], 'smart_dashes': [True, 'Educate dashes'], 'smart_ellipses': [True, 'Educate ellipses'] } - for key, value in configs: - self.setConfig(key, parseBoolValue(value)) + super(SmartyExtension, self).__init__(*args, **kwargs) def _addPatterns(self, md, patterns, serie): for ind, pattern in enumerate(patterns): @@ -204,5 +203,5 @@ class SmartyExtension(Extension): md.treeprocessors.add('smarty', inlineProcessor, '_end') md.ESCAPED_CHARS.extend(['"', "'"]) -def makeExtension(configs=None): - return SmartyExtension(configs) +def makeExtension(*args, **kwargs): + return SmartyExtension(*args, **kwargs) diff --git a/markdown/extensions/tables.py b/markdown/extensions/tables.py index cfac0bc..2977bd1 100644 --- a/markdown/extensions/tables.py +++ b/markdown/extensions/tables.py @@ -96,5 +96,6 @@ class TableExtension(Extension): ' - " - "Defaults to None"], - "anchorlink" : [0, - "1 if header should be a self link" - "Defaults to 0"], - "permalink" : [0, - "1 or link text if a Sphinx-style permalink should be added", - "Defaults to 0"] - } - - for key, value in configs: - self.setConfig(key, value) + def __init__(self, *args, **kwargs): + self.config = { + "marker" : ["[TOC]", + "Text to find and replace with Table of Contents - " + "Defaults to \"[TOC]\""], + "slugify" : [slugify, + "Function to generate anchors based on header text - " + "Defaults to the headerid ext's slugify function."], + "title" : [None, + "Title to insert into TOC
- " + "Defaults to None"], + "anchorlink" : [0, + "1 if header should be a self link - " + "Defaults to 0"], + "permalink" : [0, + "1 or link text if a Sphinx-style permalink should be added - " + "Defaults to 0"] + } + + super(TocExtension, self).__init__(*args, **kwargs) def extendMarkdown(self, md, md_globals): tocext = self.TreeProcessorClass(md) @@ -236,5 +236,5 @@ class TocExtension(Extension): md.treeprocessors.add("toc", tocext, "_end") -def makeExtension(configs={}): - return TocExtension(configs=configs) +def makeExtension(*args, **kwargs): + return TocExtension(*args, **kwargs) diff --git a/markdown/extensions/wikilinks.py b/markdown/extensions/wikilinks.py index 6d34173..c2aab8f 100644 --- a/markdown/extensions/wikilinks.py +++ b/markdown/extensions/wikilinks.py @@ -90,19 +90,17 @@ def build_url(label, base, end): class WikiLinkExtension(Extension): - def __init__(self, configs): - # set extension defaults + + def __init__ (self, *args, **kwargs): self.config = { - 'base_url' : ['/', 'String to append to beginning or URL.'], - 'end_url' : ['/', 'String to append to end of URL.'], - 'html_class' : ['wikilink', 'CSS hook. Leave blank for none.'], - 'build_url' : [build_url, 'Callable formats URL from label.'], + 'base_url' : ['/', 'String to append to beginning or URL.'], + 'end_url' : ['/', 'String to append to end of URL.'], + 'html_class' : ['wikilink', 'CSS hook. Leave blank for none.'], + 'build_url' : [build_url, 'Callable formats URL from label.'], } - configs = dict(configs) or {} - # Override defaults with user settings - for key, value in configs.items(): - self.setConfig(key, value) + super(WikiLinkExtension, self).__init__(*args, **kwargs) + def extendMarkdown(self, md, md_globals): self.md = md @@ -147,5 +145,5 @@ class WikiLinks(Pattern): return base_url, end_url, html_class -def makeExtension(configs=None) : - return WikiLinkExtension(configs=configs) +def makeExtension(*args, **kwargs) : + return WikiLinkExtension(*args, **kwargs) diff --git a/markdown/util.py b/markdown/util.py index cdad775..d5deaba 100644 --- a/markdown/util.py +++ b/markdown/util.py @@ -86,16 +86,21 @@ def isBlockLevel(tag): # Some ElementTree tags are not strings, so return False. return False -def parseBoolValue(value, fail_on_errors=True): +def parseBoolValue(value, fail_on_errors=True, preserve_none=False): """Parses a string representing bool value. If parsing was successful, - returns True or False. If parsing was not successful, raises - ValueError, or, if fail_on_errors=False, returns None.""" + returns True or False. If preserve_none=True, returns True, False, + or None. If parsing was not successful, raises ValueError, or, if + fail_on_errors=False, returns None.""" if not isinstance(value, string_type): + if preserve_none and value is None: + return value return bool(value) elif value.lower() in ('true', 'yes', 'y', 'on', '1'): return True elif value.lower() in ('false', 'no', 'n', 'off', '0'): return False + elif preserve_none and value.lower() == 'none': + return None elif fail_on_errors: raise ValueError('Cannot parse bool value: %r' % value) diff --git a/tests/test_extensions.py b/tests/test_extensions.py index e763576..b98295c 100644 --- a/tests/test_extensions.py +++ b/tests/test_extensions.py @@ -16,7 +16,10 @@ class TestExtensionClass(unittest.TestCase): def setUp(self): class TestExtension(markdown.extensions.Extension): - config = {'foo': ['bar', 'Description of foo']} + config = { + 'foo': ['bar', 'Description of foo'], + 'bar': ['baz', 'Description of bar'] + } self.ext = TestExtension() self.ExtKlass = TestExtension @@ -29,38 +32,39 @@ class TestExtensionClass(unittest.TestCase): self.assertEqual(self.ext.getConfig('baz', default='missing'), 'missing') def testGetConfigs(self): - self.assertEqual(self.ext.getConfigs(), {'foo': 'bar'}) + self.assertEqual(self.ext.getConfigs(), {'foo': 'bar', 'bar': 'baz'}) def testGetConfigInfo(self): - self.assertEqual(self.ext.getConfigInfo(), [('foo', 'Description of foo')]) + self.assertEqual(self.ext.getConfigInfo(), [('foo', 'Description of foo'), + ('bar', 'Description of bar')]) def testSetConfig(self): self.ext.setConfig('foo', 'baz') - self.assertEqual(self.ext.getConfigs(), {'foo': 'baz'}) + self.assertEqual(self.ext.getConfigs(), {'foo': 'baz', 'bar': 'baz'}) def testSetConfigWithBadKey(self): # self.ext.setConfig('bad', 'baz) ==> KeyError self.assertRaises(KeyError, self.ext.setConfig, 'bad', 'baz') def testConfigAsArgListOnInit(self): - ext = self.ExtKlass([('foo', 'baz')]) - self.assertEqual(ext.getConfigs(), {'foo': 'baz'}) + ext = self.ExtKlass([('foo', 'baz'), ('bar', 'blah')]) + self.assertEqual(ext.getConfigs(), {'foo': 'baz', 'bar': 'blah'}) def testConfigAsArgDictOnInit(self): - ext = self.ExtKlass({'foo': 'baz'}) - self.assertEqual(ext.getConfigs(), {'foo': 'baz'}) + ext = self.ExtKlass({'foo': 'baz', 'bar': 'blah', 'bar': 'blah'}) + self.assertEqual(ext.getConfigs(), {'foo': 'baz', 'bar': 'blah'}) def testConfigAsKwargListOnInit(self): - ext = self.ExtKlass(configs=[('foo', 'baz')]) - self.assertEqual(ext.getConfigs(), {'foo': 'baz'}) + ext = self.ExtKlass(configs=[('foo', 'baz'), ('bar', 'blah')]) + self.assertEqual(ext.getConfigs(), {'foo': 'baz', 'bar': 'blah'}) def testConfigAsKwargDictOnInit(self): - ext = self.ExtKlass(configs={'foo': 'baz'}) - self.assertEqual(ext.getConfigs(), {'foo': 'baz'}) + ext = self.ExtKlass(configs={'foo': 'baz', 'bar': 'blah'}) + self.assertEqual(ext.getConfigs(), {'foo': 'baz', 'bar': 'blah'}) def testConfigAsKwargsOnInit(self): - ext = self.ExtKlass(foo='baz') - self.assertEqual(ext.getConfigs(), {'foo': 'baz'}) + ext = self.ExtKlass(foo='baz', bar='blah') + self.assertEqual(ext.getConfigs(), {'foo': 'baz', 'bar': 'blah'}) class TestAbbr(unittest.TestCase): @@ -515,11 +519,12 @@ Some text with a [[WikiLink]].""" def testURLCallback(self): """ Test used of a custom URL builder. """ + + from markdown.extensions.wikilinks import WikiLinkExtension def my_url_builder(label, base, end): return '/bar/' - md = markdown.Markdown(extensions=['wikilinks'], - extension_configs={'wikilinks' : [('build_url', my_url_builder)]}) + md = markdown.Markdown(extensions=[WikiLinkExtension(build_url=my_url_builder)]) self.assertEqual(md.convert('[[foo]]'), '

foo

') -- cgit v1.2.3