1""" 2Python Markdown 3 4A Python implementation of John Gruber's Markdown. 5 6Documentation: https://python-markdown.github.io/ 7GitHub: https://github.com/Python-Markdown/markdown/ 8PyPI: https://pypi.org/project/Markdown/ 9 10Started by Manfred Stienstra (http://www.dwerg.net/). 11Maintained for a few years by Yuri Takhteyev (http://www.freewisdom.org). 12Currently maintained by Waylan Limberg (https://github.com/waylan), 13Dmitry Shachnev (https://github.com/mitya57) and Isaac Muse (https://github.com/facelessuser). 14 15Copyright 2007-2018 The Python Markdown Project (v. 1.7 and later) 16Copyright 2004, 2005, 2006 Yuri Takhteyev (v. 0.2-1.6b) 17Copyright 2004 Manfred Stienstra (the original version) 18 19License: BSD (see LICENSE.md for details). 20 21POST-PROCESSORS 22============================================================================= 23 24Markdown also allows post-processors, which are similar to preprocessors in 25that they need to implement a "run" method. However, they are run after core 26processing. 27 28""" 29 30from collections import OrderedDict 31from . import util 32import re 33 34 35def build_postprocessors(md, **kwargs): 36 """ Build the default postprocessors for Markdown. """ 37 postprocessors = util.Registry() 38 postprocessors.register(RawHtmlPostprocessor(md), 'raw_html', 30) 39 postprocessors.register(AndSubstitutePostprocessor(), 'amp_substitute', 20) 40 return postprocessors 41 42 43class Postprocessor(util.Processor): 44 """ 45 Postprocessors are run after the ElementTree it converted back into text. 46 47 Each Postprocessor implements a "run" method that takes a pointer to a 48 text string, modifies it as necessary and returns a text string. 49 50 Postprocessors must extend markdown.Postprocessor. 51 52 """ 53 54 def run(self, text): 55 """ 56 Subclasses of Postprocessor should implement a `run` method, which 57 takes the html document as a single text string and returns a 58 (possibly modified) string. 59 60 """ 61 pass # pragma: no cover 62 63 64class RawHtmlPostprocessor(Postprocessor): 65 """ Restore raw html to the document. """ 66 67 BLOCK_LEVEL_REGEX = re.compile(r'^\<\/?([^ >]+)') 68 69 def run(self, text): 70 """ Iterate over html stash and restore html. """ 71 replacements = OrderedDict() 72 for i in range(self.md.htmlStash.html_counter): 73 html = self.stash_to_string(self.md.htmlStash.rawHtmlBlocks[i]) 74 if self.isblocklevel(html): 75 replacements["<p>{}</p>".format( 76 self.md.htmlStash.get_placeholder(i))] = html 77 replacements[self.md.htmlStash.get_placeholder(i)] = html 78 79 def substitute_match(m): 80 key = m.group(0) 81 82 if key not in replacements: 83 if key[3:-4] in replacements: 84 return f'<p>{ replacements[key[3:-4]] }</p>' 85 else: 86 return key 87 88 return replacements[key] 89 90 if replacements: 91 base_placeholder = util.HTML_PLACEHOLDER % r'([0-9]+)' 92 pattern = re.compile(f'<p>{ base_placeholder }</p>|{ base_placeholder }') 93 processed_text = pattern.sub(substitute_match, text) 94 else: 95 return text 96 97 if processed_text == text: 98 return processed_text 99 else: 100 return self.run(processed_text) 101 102 def isblocklevel(self, html): 103 m = self.BLOCK_LEVEL_REGEX.match(html) 104 if m: 105 if m.group(1)[0] in ('!', '?', '@', '%'): 106 # Comment, php etc... 107 return True 108 return self.md.is_block_level(m.group(1)) 109 return False 110 111 def stash_to_string(self, text): 112 """ Convert a stashed object to a string. """ 113 return str(text) 114 115 116class AndSubstitutePostprocessor(Postprocessor): 117 """ Restore valid entities """ 118 119 def run(self, text): 120 text = text.replace(util.AMP_SUBSTITUTE, "&") 121 return text 122 123 124@util.deprecated( 125 "This class will be removed in the future; " 126 "use 'treeprocessors.UnescapeTreeprocessor' instead." 127) 128class UnescapePostprocessor(Postprocessor): 129 """ Restore escaped chars """ 130 131 RE = re.compile(r'{}(\d+){}'.format(util.STX, util.ETX)) 132 133 def unescape(self, m): 134 return chr(int(m.group(1))) 135 136 def run(self, text): 137 return self.RE.sub(self.unescape, text) 138