From ae30d3a3070c3e3bdf24595bf28bd7c150bc7abf Mon Sep 17 00:00:00 2001 From: Gerry LaMontagne Date: Sun, 29 Aug 2010 22:31:04 -0400 Subject: Replaced block.rfind in _get_right_tag with custom search function. --- markdown/preprocessors.py | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/markdown/preprocessors.py b/markdown/preprocessors.py index ab44f96..c23d728 100644 --- a/markdown/preprocessors.py +++ b/markdown/preprocessors.py @@ -82,9 +82,23 @@ class HtmlBlockPreprocessor(Preprocessor): return tag, len(tag)+2, {} def _get_right_tag(self, left_tag, left_index, block): + def recursive_tagfind(start_index, ltag, rtag): + while 1: + i = block.find(rtag, start_index) + if i == -1: + return -1 + j = block.find(ltag, start_index) + # if no ltag, or rtag found before another ltag, return index + if (j > i or j == -1): + return i + len(rtag) + start_index = recursive_tagfind(j + len(ltag) + 1, ltag, rtag) + for p in self.right_tag_patterns: tag = p % left_tag - i = block.rfind(tag) + i = recursive_tagfind(left_index, "<%s" % left_tag, tag) +# i = block.rfind(tag) + if i != -1: + i = i - len(tag) if i > 2: return tag.lstrip("<").rstrip(">"), i + len(p)-2 + left_index-2 return block.rstrip()[-left_index:-1].lower(), len(block) -- cgit v1.2.3 From 6ef287f5bafa167c01c74b84778b6ed531988bef Mon Sep 17 00:00:00 2001 From: Gerry LaMontagne Date: Mon, 30 Aug 2010 12:36:41 -0400 Subject: Further improvements to closing tag search in HtmlBlockProcessors --- markdown/preprocessors.py | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/markdown/preprocessors.py b/markdown/preprocessors.py index c23d728..f2dcd10 100644 --- a/markdown/preprocessors.py +++ b/markdown/preprocessors.py @@ -82,7 +82,7 @@ class HtmlBlockPreprocessor(Preprocessor): return tag, len(tag)+2, {} def _get_right_tag(self, left_tag, left_index, block): - def recursive_tagfind(start_index, ltag, rtag): + def recursive_tagfind(ltag, rtag, start_index): while 1: i = block.find(rtag, start_index) if i == -1: @@ -91,16 +91,20 @@ class HtmlBlockPreprocessor(Preprocessor): # if no ltag, or rtag found before another ltag, return index if (j > i or j == -1): return i + len(rtag) - start_index = recursive_tagfind(j + len(ltag) + 1, ltag, rtag) + # another ltag found before rtag, use end of ltag as starting + # point and search again + j = block.find('>', j) + start_index = recursive_tagfind(ltag, rtag, j + 1) + if start_index == -1: + # HTML potentially malformed- ltag has no corresponding + # rtag + return -1 for p in self.right_tag_patterns: tag = p % left_tag - i = recursive_tagfind(left_index, "<%s" % left_tag, tag) -# i = block.rfind(tag) - if i != -1: - i = i - len(tag) + i = recursive_tagfind("<%s" % left_tag, tag, left_index) if i > 2: - return tag.lstrip("<").rstrip(">"), i + len(p)-2 + left_index-2 + return tag.lstrip("<").rstrip(">"), i return block.rstrip()[-left_index:-1].lower(), len(block) def _equal_tags(self, left_tag, right_tag): -- cgit v1.2.3 From 0887ea58b0c2221c1328ad087b3bf5ee292fc402 Mon Sep 17 00:00:00 2001 From: Gerry LaMontagne Date: Mon, 30 Aug 2010 21:56:27 -0400 Subject: Refactored fix- created method from local function for search. Fixes ticket 62. --- markdown/preprocessors.py | 40 ++++++++++++++++++++-------------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/markdown/preprocessors.py b/markdown/preprocessors.py index f2dcd10..2bd85b0 100644 --- a/markdown/preprocessors.py +++ b/markdown/preprocessors.py @@ -81,32 +81,32 @@ class HtmlBlockPreprocessor(Preprocessor): tag = block[1:].replace(">", " ", 1).split()[0].lower() return tag, len(tag)+2, {} - def _get_right_tag(self, left_tag, left_index, block): - def recursive_tagfind(ltag, rtag, start_index): - while 1: - i = block.find(rtag, start_index) - if i == -1: - return -1 - j = block.find(ltag, start_index) - # if no ltag, or rtag found before another ltag, return index - if (j > i or j == -1): - return i + len(rtag) - # another ltag found before rtag, use end of ltag as starting - # point and search again - j = block.find('>', j) - start_index = recursive_tagfind(ltag, rtag, j + 1) - if start_index == -1: - # HTML potentially malformed- ltag has no corresponding - # rtag - return -1 + def _recursive_tagfind(self, ltag, rtag, start_index, block): + while 1: + i = block.find(rtag, start_index) + if i == -1: + return -1 + j = block.find(ltag, start_index) + # if no ltag, or rtag found before another ltag, return index + if (j > i or j == -1): + return i + len(rtag) + # another ltag found before rtag, use end of ltag as starting + # point and search again + j = block.find('>', j) + start_index = self._recursive_tagfind(ltag, rtag, j + 1, block) + if start_index == -1: + # HTML potentially malformed- ltag has no corresponding + # rtag + return -1 + def _get_right_tag(self, left_tag, left_index, block): for p in self.right_tag_patterns: tag = p % left_tag - i = recursive_tagfind("<%s" % left_tag, tag, left_index) + i = self._recursive_tagfind("<%s" % left_tag, tag, left_index, block) if i > 2: return tag.lstrip("<").rstrip(">"), i return block.rstrip()[-left_index:-1].lower(), len(block) - + def _equal_tags(self, left_tag, right_tag): if left_tag[0] in ['?', '@', '%']: # handle PHP, etc. return True -- cgit v1.2.3 From b15aeef1c4dbfb2f6bda88eb3c94b730074e1903 Mon Sep 17 00:00:00 2001 From: Gerry LaMontagne Date: Thu, 2 Sep 2010 20:12:07 -0400 Subject: Added test files for bug fix to ticket 62 --- tests/misc/block_html_attr.html | 27 +++++++++++++++++++++++++++ tests/misc/block_html_attr.txt | 24 ++++++++++++++++++++++++ tests/misc/block_html_simple.html | 10 ++++++++++ tests/misc/block_html_simple.txt | 9 +++++++++ 4 files changed, 70 insertions(+) create mode 100644 tests/misc/block_html_attr.html create mode 100644 tests/misc/block_html_attr.txt create mode 100644 tests/misc/block_html_simple.html create mode 100644 tests/misc/block_html_simple.txt diff --git a/tests/misc/block_html_attr.html b/tests/misc/block_html_attr.html new file mode 100644 index 0000000..d1c9efc --- /dev/null +++ b/tests/misc/block_html_attr.html @@ -0,0 +1,27 @@ +
+Raw HTML processing should not confuse this with the blockquote below +
+ +
+
+
+
+

Header2

+
+
+

Header3

+

Paragraph

+

Header3

+

Paragraph

+
+

Paragraph

+
+

Paragraph

+

linktext

+
+
+ +
+
+ + \ No newline at end of file diff --git a/tests/misc/block_html_attr.txt b/tests/misc/block_html_attr.txt new file mode 100644 index 0000000..b2603cc --- /dev/null +++ b/tests/misc/block_html_attr.txt @@ -0,0 +1,24 @@ +
+Raw HTML processing should not confuse this with the blockquote below +
+
+
+
+
+

Header2

+
+
+

Header3

+

Paragraph

+

Header3

+

Paragraph

+
+

Paragraph

+
+

Paragraph

+

linktext

+
+
+ +
+
diff --git a/tests/misc/block_html_simple.html b/tests/misc/block_html_simple.html new file mode 100644 index 0000000..dce68bc --- /dev/null +++ b/tests/misc/block_html_simple.html @@ -0,0 +1,10 @@ +

foo

+ +
    +
  • +

    bar

    +
  • +
  • +

    baz

    +
  • +
\ No newline at end of file diff --git a/tests/misc/block_html_simple.txt b/tests/misc/block_html_simple.txt new file mode 100644 index 0000000..d108c50 --- /dev/null +++ b/tests/misc/block_html_simple.txt @@ -0,0 +1,9 @@ +

foo

+
    +
  • +

    bar

    +
  • +
  • +

    baz

    +
  • +
-- cgit v1.2.3 From fb262f7793efd6eda61e9571cc68cfbd5bb1bf9e Mon Sep 17 00:00:00 2001 From: Waylan Limberg Date: Sun, 29 Aug 2010 00:10:29 -0400 Subject: Fixed Ticket 71. Wrapper functions no longer do there own thing with extensions. All behavior is now within the class. --- markdown/__init__.py | 49 +++++++++++++++++++++++++++++++++++---- markdown/extensions/__init__.py | 51 ----------------------------------------- tests/test_apis.py | 4 ++-- 3 files changed, 47 insertions(+), 57 deletions(-) diff --git a/markdown/__init__.py b/markdown/__init__.py index 5375b7d..57708fe 100644 --- a/markdown/__init__.py +++ b/markdown/__init__.py @@ -43,7 +43,7 @@ from blockprocessors import build_block_parser from treeprocessors import build_treeprocessors from inlinepatterns import build_inlinepatterns from postprocessors import build_postprocessors -from extensions import Extension, load_extension, load_extensions +from extensions import Extension import html4 # For backwards compatibility in the 2.0.x series @@ -136,7 +136,7 @@ class Markdown: """ for ext in extensions: if isinstance(ext, basestring): - ext = load_extension(ext, configs.get(ext, [])) + ext = self.build_extension(ext, configs.get(ext, [])) if isinstance(ext, Extension): try: ext.extendMarkdown(self, globals()) @@ -147,6 +147,47 @@ class Markdown: 'Extension "%s.%s" must be of type: "markdown.Extension".' \ % (ext.__class__.__module__, ext.__class__.__name__)) + def build_extension(self, ext_name, configs = []): + """Build extension by name, then return the module. + + The extension name may contain arguments as part of the string in the + following format: "extname(key1=value1,key2=value2)" + + """ + + # Parse extensions config params (ignore the order) + configs = dict(configs) + pos = ext_name.find("(") # find the first "(" + if pos > 0: + ext_args = ext_name[pos+1:-1] + ext_name = ext_name[:pos] + pairs = [x.split("=") for x in ext_args.split(",")] + configs.update([(x.strip(), y.strip()) for (x, y) in pairs]) + + # Setup the module names + ext_module = 'markdown.extensions' + module_name_new_style = '.'.join([ext_module, ext_name]) + module_name_old_style = '_'.join(['mdx', ext_name]) + + # Try loading the extention first from one place, then another + try: # New style (markdown.extensons.) + module = __import__(module_name_new_style, {}, {}, [ext_module]) + except ImportError: + try: # Old style (mdx_) + module = __import__(module_name_old_style) + except ImportError: + message(WARN, "Failed loading extension '%s' from '%s' or '%s'" + % (ext_name, module_name_new_style, module_name_old_style)) + # Return None so we don't try to initiate none-existant extension + return None + + # If the module is loaded successfully, we expect it to define a + # function called makeExtension() + try: + return module.makeExtension(configs.items()) + except AttributeError, e: + message(CRITICAL, "Failed to initiate extension '%s': %s" % (ext_name, e)) + def registerExtension(self, extension): """ This gets called by the extension """ self.registeredExtensions.append(extension) @@ -321,7 +362,7 @@ def markdown(text, Returns: An HTML document as a string. """ - md = Markdown(extensions=load_extensions(extensions), + md = Markdown(extensions=extensions, safe_mode=safe_mode, output_format=output_format) return md.convert(text) @@ -334,7 +375,7 @@ def markdownFromFile(input = None, safe_mode = False, output_format = 'xhtml1'): """Read markdown code from a file and write it to a file or a stream.""" - md = Markdown(extensions=load_extensions(extensions), + md = Markdown(extensions=extensions, safe_mode=safe_mode, output_format=output_format) md.convertFile(input, output, encoding) diff --git a/markdown/extensions/__init__.py b/markdown/extensions/__init__.py index 14e0911..1cc4762 100644 --- a/markdown/extensions/__init__.py +++ b/markdown/extensions/__init__.py @@ -48,54 +48,3 @@ class Extension: raise NotImplementedError, 'Extension "%s.%s" must define an "extendMarkdown"' \ 'method.' % (self.__class__.__module__, self.__class__.__name__) - -def load_extension(ext_name, configs = []): - """Load extension by name, then return the module. - - The extension name may contain arguments as part of the string in the - following format: "extname(key1=value1,key2=value2)" - - """ - - # Parse extensions config params (ignore the order) - configs = dict(configs) - pos = ext_name.find("(") # find the first "(" - if pos > 0: - ext_args = ext_name[pos+1:-1] - ext_name = ext_name[:pos] - pairs = [x.split("=") for x in ext_args.split(",")] - configs.update([(x.strip(), y.strip()) for (x, y) in pairs]) - - # Setup the module names - ext_module = 'markdown.extensions' - module_name_new_style = '.'.join([ext_module, ext_name]) - module_name_old_style = '_'.join(['mdx', ext_name]) - - # Try loading the extention first from one place, then another - try: # New style (markdown.extensons.) - module = __import__(module_name_new_style, {}, {}, [ext_module]) - except ImportError: - try: # Old style (mdx.) - module = __import__(module_name_old_style) - except ImportError: - message(WARN, "Failed loading extension '%s' from '%s' or '%s'" - % (ext_name, module_name_new_style, module_name_old_style)) - # Return None so we don't try to initiate none-existant extension - return None - - # If the module is loaded successfully, we expect it to define a - # function called makeExtension() - try: - return module.makeExtension(configs.items()) - except AttributeError, e: - message(CRITICAL, "Failed to initiate extension '%s': %s" % (ext_name, e)) - - -def load_extensions(ext_names): - """Loads multiple extensions""" - extensions = [] - for ext_name in ext_names: - extension = load_extension(ext_name) - if extension: - extensions.append(extension) - return extensions diff --git a/tests/test_apis.py b/tests/test_apis.py index e420c2a..51f4e73 100644 --- a/tests/test_apis.py +++ b/tests/test_apis.py @@ -249,13 +249,13 @@ class TestErrors(unittest.TestCase): def testLoadExtensionFailure(self): """ Test failure of an extension to load. """ self.assertRaises(MarkdownWarning, - markdown.extensions.load_extension, 'non_existant_ext') + markdown.Markdown, extensions=['non_existant_ext']) def testLoadBadExtension(self): """ Test loading of an Extension with no makeExtension function. """ _create_fake_extension(name='fake', has_factory_func=False) self.assertRaises(MarkdownException, - markdown.extensions.load_extension, 'fake') + markdown.Markdown, extensions=['fake']) def testNonExtension(self): """ Test loading a non Extension object as an extension. """ -- cgit v1.2.3 From 5366b6908de0daa389ac514eb92c8db3e04148db Mon Sep 17 00:00:00 2001 From: Waylan Limberg Date: Sun, 29 Aug 2010 00:22:28 -0400 Subject: Wrapper functions now accept any arguments also accepted by the class, rather than a limited subset. They are also more future proof. By using *args and **kwargs, we should not need to update them if we add/change any arguments or keywords on the class. --- markdown/__init__.py | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/markdown/__init__.py b/markdown/__init__.py index 57708fe..1375523 100644 --- a/markdown/__init__.py +++ b/markdown/__init__.py @@ -335,10 +335,7 @@ Those are the two functions we really mean to export: markdown() and markdownFromFile(). """ -def markdown(text, - extensions = [], - safe_mode = False, - output_format = 'xhtml1'): +def markdown(text, *args, **kwargs): """Convert a markdown string to HTML and return HTML as a unicode string. This is a shortcut function for `Markdown` class to cover the most @@ -362,9 +359,7 @@ def markdown(text, Returns: An HTML document as a string. """ - md = Markdown(extensions=extensions, - safe_mode=safe_mode, - output_format=output_format) + md = Markdown(*args, **kwargs) return md.convert(text) @@ -372,12 +367,9 @@ def markdownFromFile(input = None, output = None, extensions = [], encoding = None, - safe_mode = False, - output_format = 'xhtml1'): + *args, **kwargs): """Read markdown code from a file and write it to a file or a stream.""" - md = Markdown(extensions=extensions, - safe_mode=safe_mode, - output_format=output_format) + md = Markdown(extensions=extensions, *args, **kwargs) md.convertFile(input, output, encoding) -- cgit v1.2.3