Skip to content

Commit 9082ed4

Browse files
facelessuserwaylan
authored andcommitted
Fix the lost tail issue in inlineprocessors.
See #253. Prior to this patch, if any inline processors returned an element with a tail, the tail would end up empty. This resolves that issue and will allow for #253 to be fixed. Thanks to @facelessuser for the work on this.
1 parent 8f878c3 commit 9082ed4

File tree

3 files changed

+40
-23
lines changed

3 files changed

+40
-23
lines changed

markdown/inlinepatterns.py

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -156,7 +156,7 @@ def __init__(self, pattern, markdown_instance=None):
156156
157157
"""
158158
self.pattern = pattern
159-
self.compiled_re = re.compile("^(.*?)%s(.*?)$" % pattern,
159+
self.compiled_re = re.compile("^(.*?)%s(.*?)$" % pattern,
160160
re.DOTALL | re.UNICODE)
161161

162162
# Api for Markdown to pass safe_mode into instance
@@ -210,7 +210,7 @@ def get_stash(m):
210210
return value
211211
else:
212212
# An etree Element - return text content only
213-
return ''.join(itertext(value))
213+
return ''.join(itertext(value))
214214
return util.INLINE_PLACEHOLDER_RE.sub(get_stash, text)
215215

216216

@@ -228,7 +228,7 @@ def handleMatch(self, m):
228228
if char in self.markdown.ESCAPED_CHARS:
229229
return '%s%s%s' % (util.STX, ord(char), util.ETX)
230230
else:
231-
return None
231+
return None
232232

233233

234234
class SimpleTagPattern(Pattern):
@@ -300,7 +300,7 @@ def get_stash(m):
300300
return self.markdown.serializer(value)
301301
except:
302302
return '\%s' % value
303-
303+
304304
return util.INLINE_PLACEHOLDER_RE.sub(get_stash, text)
305305

306306

@@ -320,7 +320,7 @@ def handleMatch(self, m):
320320
el.set("href", "")
321321

322322
if title:
323-
title = dequote(self.unescape(title))
323+
title = dequote(self.unescape(title))
324324
el.set("title", title)
325325
return el
326326

@@ -344,19 +344,19 @@ def sanitize_url(self, url):
344344
if not self.markdown.safeMode:
345345
# Return immediately bipassing parsing.
346346
return url
347-
347+
348348
try:
349349
scheme, netloc, path, params, query, fragment = url = urlparse(url)
350350
except ValueError: #pragma: no cover
351351
# Bad url - so bad it couldn't be parsed.
352352
return ''
353-
353+
354354
locless_schemes = ['', 'mailto', 'news']
355355
allowed_schemes = locless_schemes + ['http', 'https', 'ftp', 'ftps']
356356
if scheme not in allowed_schemes:
357357
# Not a known (allowed) scheme. Not safe.
358358
return ''
359-
359+
360360
if netloc == '' and scheme not in locless_schemes: #pragma: no cover
361361
# This should not happen. Treat as suspect.
362362
return ''

markdown/treeprocessors.py

Lines changed: 18 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,8 @@ class Treeprocessor(util.Processor):
3434
def run(self, root):
3535
"""
3636
Subclasses of Treeprocessor should implement a `run` method, which
37-
takes a root ElementTree. This method can return another ElementTree
38-
object, and the existing root ElementTree will be replaced, or it can
37+
takes a root ElementTree. This method can return another ElementTree
38+
object, and the existing root ElementTree will be replaced, or it can
3939
modify the current tree and return None.
4040
"""
4141
pass #pragma: no cover
@@ -71,7 +71,7 @@ def __findPlaceholder(self, data, index):
7171
* index: index, from which we start search
7272
7373
Returns: placeholder id and string index, after the found placeholder.
74-
74+
7575
"""
7676
m = self.__placeholder_re.search(data, index)
7777
if m:
@@ -129,19 +129,18 @@ def __processElementText(self, node, subnode, isText=True):
129129
text = subnode.tail
130130
subnode.tail = None
131131

132-
childResult = self.__processPlaceholders(text, subnode)
132+
childResult = self.__processPlaceholders(text, subnode, isText)
133133

134134
if not isText and node is not subnode:
135135
pos = list(node).index(subnode)
136-
node.remove(subnode)
137136
else:
138137
pos = 0
139138

140139
childResult.reverse()
141140
for newChild in childResult:
142141
node.insert(pos, newChild)
143142

144-
def __processPlaceholders(self, data, parent):
143+
def __processPlaceholders(self, data, parent, isText=True):
145144
"""
146145
Process string with placeholders and generate ElementTree tree.
147146
@@ -151,7 +150,7 @@ def __processPlaceholders(self, data, parent):
151150
* parent: Element, which contains processing inline data
152151
153152
Returns: list with ElementTree elements with applied inline patterns.
154-
153+
155154
"""
156155
def linkText(text):
157156
if text:
@@ -160,6 +159,11 @@ def linkText(text):
160159
result[-1].tail += text
161160
else:
162161
result[-1].tail = text
162+
elif not isText:
163+
if parent.tail:
164+
parent.tail += text
165+
else:
166+
parent.tail = text
163167
else:
164168
if parent.text:
165169
parent.text += text
@@ -183,7 +187,7 @@ def linkText(text):
183187
for child in [node] + list(node):
184188
if child.tail:
185189
if child.tail.strip():
186-
self.__processElementText(node, child,False)
190+
self.__processElementText(node, child, False)
187191
if child.text:
188192
if child.text.strip():
189193
self.__processElementText(child, child)
@@ -240,7 +244,7 @@ def __applyPattern(self, pattern, data, patternIndex, startIndex=0):
240244
# We need to process current node too
241245
for child in [node] + list(node):
242246
if not isString(node):
243-
if child.text:
247+
if child.text:
244248
child.text = self.__handleInline(child.text,
245249
patternIndex + 1)
246250
if child.tail:
@@ -288,11 +292,10 @@ def run(self, tree):
288292
if child.tail:
289293
tail = self.__handleInline(child.tail)
290294
dumby = util.etree.Element('d')
291-
tailResult = self.__processPlaceholders(tail, dumby)
292-
if dumby.text:
293-
child.tail = dumby.text
294-
else:
295-
child.tail = None
295+
child.tail = None
296+
tailResult = self.__processPlaceholders(tail, dumby, False)
297+
if dumby.tail:
298+
child.tail = dumby.tail
296299
pos = list(currElement).index(child) + 1
297300
tailResult.reverse()
298301
for newChild in tailResult:
@@ -304,7 +307,7 @@ def run(self, tree):
304307
if self.markdown.enable_attributes:
305308
if element.text and isString(element.text):
306309
element.text = \
307-
inlinepatterns.handleAttributes(element.text,
310+
inlinepatterns.handleAttributes(element.text,
308311
element)
309312
i = 0
310313
for newChild in lst:

tests/test_apis.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -380,6 +380,20 @@ def testCommentPrettify(self):
380380
'<!--foo-->\n')
381381

382382

383+
class testElementTailTests(unittest.TestCase):
384+
""" Element Tail Tests """
385+
def setUp(self):
386+
self.pretty = markdown.treeprocessors.PrettifyTreeprocessor()
387+
388+
def testBrTailNoNewline(self):
389+
""" Test that last <br> in tree has a new line tail """
390+
root = markdown.util.etree.Element('root')
391+
br = markdown.util.etree.SubElement(root, 'br')
392+
self.assertEqual(br.tail, None)
393+
self.pretty.run(root)
394+
self.assertEqual(br.tail, "\n")
395+
396+
383397
class testSerializers(unittest.TestCase):
384398
""" Test the html and xhtml serializers. """
385399

0 commit comments

Comments
 (0)