From 33c53ffe73c1c8d1b771804514c8a3a157434c76 Mon Sep 17 00:00:00 2001 From: facelessuser Date: Thu, 16 Nov 2017 20:55:13 -0700 Subject: Initial work on ancestry exclusion Intial work and tests for excluding a pattern due parental tags. --- markdown/inlinepatterns.py | 4 ++++ markdown/treeprocessors.py | 51 ++++++++++++++++++++++++++++++++++--------- tests/test_apis.py | 54 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 99 insertions(+), 10 deletions(-) diff --git a/markdown/inlinepatterns.py b/markdown/inlinepatterns.py index 3658ebd..f483f99 100644 --- a/markdown/inlinepatterns.py +++ b/markdown/inlinepatterns.py @@ -207,6 +207,10 @@ class Pattern(object): if markdown_instance: self.markdown = markdown_instance + def getExcludes(self): + """Get tag to exclude.""" + return [] + def getCompiledRegExp(self): """ Return a compiled regular expression. """ return self.compiled_re diff --git a/markdown/treeprocessors.py b/markdown/treeprocessors.py index bb76572..ae411e2 100644 --- a/markdown/treeprocessors.py +++ b/markdown/treeprocessors.py @@ -54,6 +54,7 @@ class InlineProcessor(Treeprocessor): self.__placeholder_re = util.INLINE_PLACEHOLDER_RE self.markdown = md self.inlinePatterns = md.inlinePatterns + self.ancestors = [] def __makePlaceholder(self, type): """ Generate a placeholder """ @@ -138,7 +139,7 @@ class InlineProcessor(Treeprocessor): childResult.reverse() for newChild in childResult: - node.insert(pos, newChild) + node.insert(pos, newChild[0]) def __processPlaceholders(self, data, parent, isText=True): """ @@ -155,10 +156,10 @@ class InlineProcessor(Treeprocessor): def linkText(text): if text: if result: - if result[-1].tail: - result[-1].tail += text + if result[-1][0].tail: + result[-1][0].tail += text else: - result[-1].tail = text + result[-1][0].tail = text elif not isText: if parent.tail: parent.tail += text @@ -199,7 +200,7 @@ class InlineProcessor(Treeprocessor): continue strartIndex = phEndIndex - result.append(node) + result.append((node, self.ancestors[:])) else: # wrong placeholder end = index + len(self.__placeholder_prefix) @@ -230,6 +231,12 @@ class InlineProcessor(Treeprocessor): Returns: String with placeholders instead of ElementTree elements. """ + # print(self.ancestors) + # print(data[startIndex:]) + for exclude in pattern.getExcludes(): + if exclude.lower() in self.ancestors: + return data, False, 0 + match = pattern.getCompiledRegExp().match(data[startIndex:]) leftData = data[:startIndex] @@ -245,15 +252,19 @@ class InlineProcessor(Treeprocessor): if not isinstance(node.text, util.AtomicString): # We need to process current node too for child in [node] + list(node): + self.ancestors.append(child.tag.lower()) if not isString(node): if child.text: child.text = self.__handleInline( child.text, patternIndex + 1 ) + self.ancestors.pop() if child.tail: child.tail = self.__handleInline( child.tail, patternIndex ) + else: + self.ancestors.pop() placeholder = self.__stashNode(node, pattern.type()) @@ -261,6 +272,19 @@ class InlineProcessor(Treeprocessor): match.group(1), placeholder, match.groups()[-1]), True, 0 + def __build_ancestors(self, parent, parents): + """Build the ancestor list.""" + + ancestors = [] + parent_map = dict((c, p) for p in parent.getiterator() for c in p) + ancestors.append(parent.tag.lower()) + while parent: + parent = parent_map.get(parent) + if parent: + ancestors.append(parent.tag.lower()) + ancestors.reverse() + parents.extend(ancestors) + def run(self, tree): """Apply inline patterns to a parsed Markdown tree. @@ -280,12 +304,17 @@ class InlineProcessor(Treeprocessor): """ self.stashed_nodes = {} - stack = [tree] + stack = [(tree, [])] while stack: - currElement = stack.pop() + currElement, parents = stack.pop() + + self.ancestors = parents + self.__build_ancestors(currElement, self.ancestors) + insertQueue = [] for child in currElement: + self.ancestors.append(child.tag.lower()) if child.text and not isinstance( child.text, util.AtomicString ): @@ -296,6 +325,7 @@ class InlineProcessor(Treeprocessor): ) stack += lst insertQueue.append((child, lst)) + self.ancestors.pop() if child.tail: tail = self.__handleInline(child.tail) dumby = util.etree.Element('d') @@ -306,9 +336,9 @@ class InlineProcessor(Treeprocessor): pos = list(currElement).index(child) + 1 tailResult.reverse() for newChild in tailResult: - currElement.insert(pos, newChild) + currElement.insert(pos, newChild[0]) if len(child): - stack.append(child) + stack.append((child, self.ancestors[:])) for element, lst in insertQueue: if self.markdown.enable_attributes: @@ -317,7 +347,8 @@ class InlineProcessor(Treeprocessor): element.text, element ) i = 0 - for newChild in lst: + for obj in lst: + newChild = obj[0] if self.markdown.enable_attributes: # Processing attributes if newChild.tail and isString(newChild.tail): diff --git a/tests/test_apis.py b/tests/test_apis.py index 7b1214f..cbcb989 100644 --- a/tests/test_apis.py +++ b/tests/test_apis.py @@ -770,3 +770,57 @@ class TestEscapeAppend(unittest.TestCase): self.assertEqual('|' in md.ESCAPED_CHARS, True) md2 = markdown.Markdown() self.assertEqual('|' not in md2.ESCAPED_CHARS, True) + + +class TestAncestorExclusion(unittest.TestCase): + """ Tests exclusion of tags in ancestor list. """ + + class AncestorExample(markdown.inlinepatterns.SimpleTagPattern): + """ Ancestor Test. """ + + def getExcludes(self): + """ Tags to exclude. """ + return ['a'] + + def handleMatch(self, m): + """ Handle match. """ + el = markdown.util.etree.Element(self.tag) + el.text = m.group(3) + return el + + class AncestorExtension(markdown.Extension): + + def __init__(self, *args, **kwargs): + """Initialize.""" + + self.config = {} + + def extendMarkdown(self, md, md_globals): + """Modify inline patterns.""" + + pattern = r'(\+)([^\+]+)\2' + md.inlinePatterns["ancestor-test"] = TestAncestorExclusion.AncestorExample(pattern, 'strong') + + def setUp(self): + """Setup markdown object.""" + self.md = markdown.Markdown(extensions=[TestAncestorExclusion.AncestorExtension()]) + + def test_ancestors(self): + """ Test that an extension can exclude parent tags. """ + test = """ +Some +test+ and a [+link+](http://test.com) +""" + result = """

Some test and a +link+

""" + + self.md.reset() + self.assertEqual(self.md.convert(test), result) + + def test_ancestors_tail(self): + """ Test that an extension can exclude parent tags when dealing with a tail. """ + test = """ +[***+em+*+strong+**](http://test.com) +""" + result = """

+em++strong+

""" + + self.md.reset() + self.assertEqual(self.md.convert(test), result) -- cgit v1.2.1