From 85e1a0c29c3f659fa1f36e94788a00b3e864b9d3 Mon Sep 17 00:00:00 2001 From: Dmitry Shachnev Date: Wed, 25 Sep 2013 11:39:16 +0400 Subject: Add new utility function parseBoolValue() and use it in all extension that need parsing bool config values. --- markdown/extensions/headerid.py | 15 +++------------ markdown/extensions/smarty.py | 11 ++--------- markdown/extensions/toc.py | 4 ++-- markdown/util.py | 13 +++++++++++++ 4 files changed, 20 insertions(+), 23 deletions(-) diff --git a/markdown/extensions/headerid.py b/markdown/extensions/headerid.py index 0476f9e..8221fe1 100644 --- a/markdown/extensions/headerid.py +++ b/markdown/extensions/headerid.py @@ -78,7 +78,7 @@ from __future__ import absolute_import from __future__ import unicode_literals from . import Extension from ..treeprocessors import Treeprocessor -from ..util import HTML_PLACEHOLDER_RE +from ..util import HTML_PLACEHOLDER_RE, parseBoolValue import re import logging import unicodedata @@ -166,23 +166,14 @@ class HeaderIdTreeprocessor(Treeprocessor): def _get_meta(self): """ Return meta data suported by this ext as a tuple """ level = int(self.config['level']) - 1 - force = self._str2bool(self.config['forceid']) + force = parseBoolValue(self.config['forceid']) if hasattr(self.md, 'Meta'): if 'header_level' in self.md.Meta: level = int(self.md.Meta['header_level'][0]) - 1 if 'header_forceid' in self.md.Meta: - force = self._str2bool(self.md.Meta['header_forceid'][0]) + force = parseBoolValue(self.md.Meta['header_forceid'][0]) return level, force - def _str2bool(self, s, default=False): - """ Convert a string to a booleen value. """ - s = str(s) - if s.lower() in ['0', 'f', 'false', 'off', 'no', 'n']: - return False - elif s.lower() in ['1', 't', 'true', 'on', 'yes', 'y']: - return True - return default - class HeaderIdExtension(Extension): def __init__(self, configs): diff --git a/markdown/extensions/smarty.py b/markdown/extensions/smarty.py index 18f9217..a0737b7 100644 --- a/markdown/extensions/smarty.py +++ b/markdown/extensions/smarty.py @@ -68,6 +68,7 @@ from __future__ import unicode_literals from . import Extension from ..inlinepatterns import HtmlPattern +from ..util import parseBoolValue def canonicalize(regex): """ @@ -173,15 +174,7 @@ class SmartyExtension(Extension): 'smart_ellipses': [True, 'Educate ellipses'] } for key, value in configs: - if not isinstance(value, str): - value = bool(value) - elif value.lower() in ('true', 't', 'yes', 'y', '1'): - value = True - elif value.lower() in ('false', 'f', 'no', 'n', '0'): - value = False - else: - raise ValueError('Cannot parse bool value: %s' % value) - self.setConfig(key, value) + self.setConfig(key, parseBoolValue(value)) def _addPatterns(self, md, patterns, serie): for ind, pattern in enumerate(patterns): diff --git a/markdown/extensions/toc.py b/markdown/extensions/toc.py index 99afba0..516f2c5 100644 --- a/markdown/extensions/toc.py +++ b/markdown/extensions/toc.py @@ -13,7 +13,7 @@ from __future__ import absolute_import from __future__ import unicode_literals from . import Extension from ..treeprocessors import Treeprocessor -from ..util import etree +from ..util import etree, parseBoolValue from .headerid import slugify, unique, itertext, stashedHTML2text import re @@ -127,7 +127,7 @@ class TocTreeprocessor(Treeprocessor): div.attrib["class"] = "toc" header_rgx = re.compile("[Hh][123456]") - self.use_anchors = self.config["anchorlink"] in [1, '1', True, 'True', 'true'] + self.use_anchors = parseBoolValue(self.config["anchorlink"]) # Get a list of id attributes used_ids = set() diff --git a/markdown/util.py b/markdown/util.py index d292aad..0027176 100644 --- a/markdown/util.py +++ b/markdown/util.py @@ -84,6 +84,19 @@ def isBlockLevel(tag): # Some ElementTree tags are not strings, so return False. return False +def parseBoolValue(value, fail_on_errors=True): + """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.""" + if not isinstance(value, str): + 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 fail_on_errors: + raise ValueError('Cannot parse bool value: %r' % value) + """ MISC AUXILIARY CLASSES ============================================================================= -- cgit v1.2.3 From ed399ac3da4dddfd676bedada42a3aa4d4d80e1e Mon Sep 17 00:00:00 2001 From: Dmitry Shachnev Date: Wed, 25 Sep 2013 11:56:40 +0400 Subject: Add new "permalink" option to toc extension and use it in our docs --- markdown/extensions/toc.py | 44 +++++++++++++++++++++++++++++++------------- markdown/util.py | 2 +- setup.py | 3 ++- 3 files changed, 34 insertions(+), 15 deletions(-) diff --git a/markdown/extensions/toc.py b/markdown/extensions/toc.py index 516f2c5..eba0e69 100644 --- a/markdown/extensions/toc.py +++ b/markdown/extensions/toc.py @@ -89,16 +89,24 @@ class TocTreeprocessor(Treeprocessor): yield parent, child def add_anchor(self, c, elem_id): #@ReservedAssignment - if self.use_anchors: - anchor = etree.Element("a") - anchor.text = c.text - anchor.attrib["href"] = "#" + elem_id - anchor.attrib["class"] = "toclink" - c.text = "" - for elem in c.getchildren(): - anchor.append(elem) - c.remove(elem) - c.append(anchor) + anchor = etree.Element("a") + anchor.text = c.text + anchor.attrib["href"] = "#" + elem_id + anchor.attrib["class"] = "toclink" + c.text = "" + for elem in c.getchildren(): + anchor.append(elem) + c.remove(elem) + c.append(anchor) + + def add_permalink(self, c, elem_id): + permalink = etree.Element("a") + permalink.text = ("\xb6" if self.use_permalinks is True + else self.use_permalinks) + permalink.attrib["href"] = "#" + elem_id + permalink.attrib["class"] = "headerlink" + permalink.attrib["title"] = "Permanent link" + c.append(permalink) def build_toc_etree(self, div, toc_list): # Add title to the div @@ -128,6 +136,9 @@ class TocTreeprocessor(Treeprocessor): header_rgx = re.compile("[Hh][123456]") self.use_anchors = parseBoolValue(self.config["anchorlink"]) + self.use_permalinks = parseBoolValue(self.config["permalink"], False) + if self.use_permalinks is None: + self.use_permalinks = self.config["permalink"] # Get a list of id attributes used_ids = set() @@ -171,8 +182,11 @@ class TocTreeprocessor(Treeprocessor): toc_list.append({'level': tag_level, 'id': elem_id, 'name': text}) - - self.add_anchor(c, elem_id) + + if self.use_anchors: + self.add_anchor(c, elem_id) + if self.use_permalinks: + self.add_permalink(c, elem_id) toc_list_nested = order_toc_list(toc_list) self.build_toc_etree(div, toc_list_nested) @@ -202,7 +216,11 @@ class TocExtension(Extension): "Defaults to None"], "anchorlink" : [0, "1 if header should be a self link" - "Defaults to 0"]} + "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) diff --git a/markdown/util.py b/markdown/util.py index 0027176..d0ef8a3 100644 --- a/markdown/util.py +++ b/markdown/util.py @@ -88,7 +88,7 @@ def parseBoolValue(value, fail_on_errors=True): """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.""" - if not isinstance(value, str): + if not isinstance(value, string_type): return bool(value) elif value.lower() in ('true', 'yes', 'y', 'on', '1'): return True diff --git a/setup.py b/setup.py index 826de9d..0702f6b 100755 --- a/setup.py +++ b/setup.py @@ -144,7 +144,8 @@ class build_docs(Command): else: with codecs.open('docs/_template.html', encoding='utf-8') as f: template = f.read() - self.md = markdown.Markdown(extensions=['extra', 'toc', 'meta', 'admonition', 'smarty']) + self.md = markdown.Markdown( + extensions=['extra', 'toc(permalink=true)', 'meta', 'admonition', 'smarty']) for infile in self.docs: outfile, ext = os.path.splitext(infile) if ext == '.txt': -- cgit v1.2.3 From 7d10f204203397c3ab34c20014fef55e3d8ba16d Mon Sep 17 00:00:00 2001 From: Dmitry Shachnev Date: Wed, 25 Sep 2013 14:43:18 +0400 Subject: Add tests for the previous two commits --- tests/extensions/test.cfg | 4 ++-- tests/extensions/toc_nested.html | 8 ++++---- tests/extensions/toc_nested2.html | 8 ++++---- tests/test_apis.py | 13 +++++++++++++ 4 files changed, 23 insertions(+), 10 deletions(-) diff --git a/tests/extensions/test.cfg b/tests/extensions/test.cfg index 1a13b1c..494d79b 100644 --- a/tests/extensions/test.cfg +++ b/tests/extensions/test.cfg @@ -16,10 +16,10 @@ extensions=toc extensions=toc [toc_nested] -extensions=toc +extensions=toc(permalink=1) [toc_nested2] -extensions=toc +extensions=toc(permalink=[link]) [wikilinks] extensions=wikilinks diff --git a/tests/extensions/toc_nested.html b/tests/extensions/toc_nested.html index 3703e51..b913640 100644 --- a/tests/extensions/toc_nested.html +++ b/tests/extensions/toc_nested.html @@ -1,7 +1,7 @@ -

Header A

-

Header 1

-

Header i

-

Header B

+

Header A

+

Header 1

+

Header i

+

Header B

  • Header A
      diff --git a/tests/extensions/toc_nested2.html b/tests/extensions/toc_nested2.html index bf87716..2d8fa2d 100644 --- a/tests/extensions/toc_nested2.html +++ b/tests/extensions/toc_nested2.html @@ -8,7 +8,7 @@
    • Header 3
-

Start with header other than one.

-

Header 3

-

Header 4

-

Header 3

\ No newline at end of file +

Start with header other than one.[link]

+

Header 3[link]

+

Header 4[link]

+

Header 3[link]

\ No newline at end of file diff --git a/tests/test_apis.py b/tests/test_apis.py index bbe165d..a7d6685 100644 --- a/tests/test_apis.py +++ b/tests/test_apis.py @@ -420,3 +420,16 @@ class testAtomicString(unittest.TestCase): '

*some* *more* *text* *here* ' '*to* *test* *with*

') +class TestConfigParsing(unittest.TestCase): + def assertParses(self, value, result): + self.assertIs(markdown.util.parseBoolValue(value, False), result) + + def testBooleansParsing(self): + self.assertParses(True, True) + self.assertParses('novalue', None) + self.assertParses('yES', True) + self.assertParses('FALSE', False) + self.assertParses(0., False) + + def testInvalidBooleansParsing(self): + self.assertRaises(ValueError, markdown.util.parseBoolValue, 'novalue') -- cgit v1.2.3 From 6efe75869ac7ab60ccadc0a9c4ac64f9a7722379 Mon Sep 17 00:00:00 2001 From: Dmitry Shachnev Date: Sun, 29 Sep 2013 19:06:28 +0400 Subject: Mention new option in toc.txt --- docs/extensions/toc.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/extensions/toc.txt b/docs/extensions/toc.txt index 2a91bb6..260129c 100644 --- a/docs/extensions/toc.txt +++ b/docs/extensions/toc.txt @@ -58,6 +58,8 @@ The following options are provided to configure the output: * **title**: Title to insert in TOC ``
``. Defaults to ``None``. * **anchorlink**: Set to ``True`` to have the headers link to themselves. Default is ``False``. +* **permalink**: Set to ``True`` to have this extension generate Sphinx-style + permanent links near the headers (for use with Sphinx stylesheets). If a 'marker' is not found in the document, then the toc is available as an attribute of the Markdown class. This allows one to insert the toc elsewhere -- cgit v1.2.3 From 635d2f71db191145d30cba4934ab7fa8f4d20509 Mon Sep 17 00:00:00 2001 From: Dmitry Shachnev Date: Sun, 29 Sep 2013 19:20:59 +0400 Subject: toc: insert `¶` instead of raw unicode character --- markdown/extensions/toc.py | 6 +++--- tests/extensions/toc_nested.html | 8 ++++---- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/markdown/extensions/toc.py b/markdown/extensions/toc.py index eba0e69..89468d6 100644 --- a/markdown/extensions/toc.py +++ b/markdown/extensions/toc.py @@ -13,7 +13,7 @@ from __future__ import absolute_import from __future__ import unicode_literals from . import Extension from ..treeprocessors import Treeprocessor -from ..util import etree, parseBoolValue +from ..util import etree, parseBoolValue, AMP_SUBSTITUTE from .headerid import slugify, unique, itertext, stashedHTML2text import re @@ -101,8 +101,8 @@ class TocTreeprocessor(Treeprocessor): def add_permalink(self, c, elem_id): permalink = etree.Element("a") - permalink.text = ("\xb6" if self.use_permalinks is True - else self.use_permalinks) + permalink.text = ("%spara;" % AMP_SUBSTITUTE + if self.use_permalinks is True else self.use_permalinks) permalink.attrib["href"] = "#" + elem_id permalink.attrib["class"] = "headerlink" permalink.attrib["title"] = "Permanent link" diff --git a/tests/extensions/toc_nested.html b/tests/extensions/toc_nested.html index b913640..27af9df 100644 --- a/tests/extensions/toc_nested.html +++ b/tests/extensions/toc_nested.html @@ -1,7 +1,7 @@ -

Header A

-

Header 1

-

Header i

-

Header B

+

Header A

+

Header 1

+

Header i

+

Header B