From: DongHun Kwak Date: Fri, 15 Jul 2022 00:02:47 +0000 (+0900) Subject: Imported Upstream version 3.3.4 X-Git-Tag: upstream/3.3.4^0 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=393a80ae6bd64c051dc98d6a2ce7e07f4aaa059f;p=platform%2Fupstream%2Fpython3-markdown.git Imported Upstream version 3.3.4 --- diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md index 30c3745..a30e62f 100644 --- a/CODE_OF_CONDUCT.md +++ b/CODE_OF_CONDUCT.md @@ -35,7 +35,7 @@ This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Instances of abusive, harassing, or otherwise unacceptable behavior may be -reported by contacting a project maintainer at markdown@freewisdom.org. All +reported by contacting a project maintainer at python.markdown@gmail.com. All complaints will be reviewed and investigated and will result in a response that is deemed necessary and appropriate to the circumstances. Maintainers are obligated to maintain confidentiality with regard to the reporter of an diff --git a/README.md b/README.md index c5652e7..7b979b0 100644 --- a/README.md +++ b/README.md @@ -34,9 +34,19 @@ supported by the [Available Extensions][]. Documentation ------------- -Installation and usage documentation is available in the `docs/` directory -of the distribution and on the project website at -. +```bash +pip install markdown +``` +```python +import markdown +html = markdown.markdown(your_text_string) +``` + +For more advanced [installation] and [usage] documentation, see the `docs/` directory +of the distribution or the project website at . + +[installation]: https://python-markdown.github.io/install/ +[usage]: https://python-markdown.github.io/reference/ See the change log at . diff --git a/docs/authors.md b/docs/authors.md index acf78b0..114227d 100644 --- a/docs/authors.md +++ b/docs/authors.md @@ -22,7 +22,7 @@ Primary Authors and has been assisting with maintenance, reviewing pull requests and ticket triage. -* __[Yuri Takteyev](http://freewisdom.org/)__ +* __[Yuri Takteyev](https://github.com/yuri)__ Yuri wrote most of the code found in version 1.x while procrastinating his Ph.D. Various pieces of his code still exist, most notably the basic diff --git a/docs/change_log/index.md b/docs/change_log/index.md index 632449a..d7487a3 100644 --- a/docs/change_log/index.md +++ b/docs/change_log/index.md @@ -3,6 +3,15 @@ title: Change Log Python-Markdown Change Log ========================= +Feb 24, 2021: version 3.3.4 (a bug-fix release). + +* Properly parse unclosed tags in code spans (#1066). +* Properly parse processing instructions in md_in_html (#1070). +* Properly parse code spans in md_in_html (#1069). +* Preserve text immediately before an admonition (#1092). +* Simplified regex for HTML placeholders (#928) addressing (#932). +* Ensure `permalinks` and `ankorlinks` are not restricted by `toc_depth` (#1107). + Oct 25, 2020: version 3.3.3 (a bug-fix release). * Unify all block-level tags (#1047). diff --git a/docs/change_log/release-3.3.md b/docs/change_log/release-3.3.md index 79e22b2..12273bc 100644 --- a/docs/change_log/release-3.3.md +++ b/docs/change_log/release-3.3.md @@ -101,6 +101,7 @@ The following bug fixes are included in the 3.3 release: * Fix unescaping of HTML characters `<>` in CodeHilite (#990). * Fix complex scenarios involving lists and admonitions (#1004). * Fix complex scenarios with nested ordered and unordered lists in a definition list (#918). +* Fix corner cases with lists under admonitions. [spec]: https://www.w3.org/TR/html5/text-level-semantics.html#the-code-element [fenced_code]: ../extensions/fenced_code_blocks.md diff --git a/docs/contributing.md b/docs/contributing.md index 20f21c2..5022e45 100644 --- a/docs/contributing.md +++ b/docs/contributing.md @@ -9,7 +9,7 @@ propose changes to this document in a pull request. This project and everyone participating in it is governed by the [Python-Markdown Code of Conduct]. By participating, you are expected to uphold -this code. Please report unacceptable behavior to [markdown@freewisdom.org][email]. +this code. Please report unacceptable behavior to . ## Project Organization @@ -478,7 +478,6 @@ label from the same group. [Python-Markdown Organization]: https://github.com/Python-Markdown [Python-Markdown Code of Conduct]: https://github.com/Python-Markdown/markdown/blob/master/CODE_OF_CONDUCT.md -[email]: mailto:markdown@freewisdom.org [Python-Markdown/markdown]: https://github.com/Python-Markdown/markdown [issue tracker]: https://github.com/Python-Markdown/markdown/issues [syntax rules]: https://daringfireball.net/projects/markdown/syntax diff --git a/docs/extensions/api.md b/docs/extensions/api.md index ce2a873..161e886 100644 --- a/docs/extensions/api.md +++ b/docs/extensions/api.md @@ -551,9 +551,27 @@ def set_link_class(self, element): set_link_class(child) # run recursively on children ``` -For more information about working with ElementTree see the ElementTree -[Documentation](https://effbot.org/zone/element-index.htm) ([Python -Docs](https://docs.python.org/3/library/xml.etree.elementtree.html)). +For more information about working with ElementTree see the [ElementTree +Documentation][ElementTree]. + +## Working with Raw HTML {: #working_with_raw_html } + +Occasionally an extension may need to call out to a third party library which returns a pre-made string +of raw HTML that needs to be inserted into the document unmodified. Raw strings can be stashed for later +retrieval using an `htmlStash` instance, rather than converting them into `ElementTree` objects. A raw string +(which may or may not be raw HTML) passed to `self.md.htmlStash.store()` will be saved to the stash and a +placeholder string will be returned which should be inserted into the tree instead. After the tree is +serialized, a postprocessor will replace the placeholder with the raw string. This prevents subsequent +processing steps from modifying the HTML data. For example, + +```python +html = "

This is some raw HTML data

" +el = etree.Element("div") +el.text = self.md.htmlStash.store(html) +``` + +For the global `htmlStash` instance to be available from a processor, the `markdown.Markdown` instance must +be passed to the processor from [extendMarkdown](#extendmarkdown) and will be available as `self.md.htmlStash`. ## Integrating Your Code Into Markdown {: #integrating_into_markdown } @@ -858,7 +876,7 @@ assert someitem in registry [registerExtension]: #registerextension [Config Settings]: #configsettings [makeExtension]: #makeextension -[ElementTree]: https://effbot.org/zone/element-index.htm +[ElementTree]: https://docs.python.org/3/library/xml.etree.elementtree.html [Available Extensions]: index.md [Footnotes]: https://github.com/Python-Markdown/markdown/blob/master/markdown/extensions/footnotes.py [Definition Lists]: https://github.com/Python-Markdown/markdown/blob/master/markdown/extensions/definition_lists diff --git a/docs/test_tools.md b/docs/test_tools.md index 3a83d8e..f3ed4e5 100644 --- a/docs/test_tools.md +++ b/docs/test_tools.md @@ -22,12 +22,16 @@ Properties test. The defaults can be overridden on individual tests. Methods -: `assertMarkdownRenders`: accepts the source text, the expected output, - and any keywords to pass to Markdown. The `default_kwargs` defined on the - class are used except where overridden by keyword arguments. The output and - expected output are passed to `TestCase.assertMultiLineEqual`. An - `AssertionError` is raised with a diff if the actual output does not equal the - expected output. +: `assertMarkdownRenders`: accepts the source text, the expected output, an optional + dictionary of `expected_attrs`, and any keywords to pass to Markdown. The + `default_kwargs` defined on the class are used except where overridden by + keyword arguments. The output and expected output are passed to + `TestCase.assertMultiLineEqual`. An `AssertionError` is raised with a diff + if the actual output does not equal the expected output. The optional + keyword `expected_attrs` accepts a dictionary of attribute names as keys with + expected values. Each value is checked against the attribute of that + name on the instance of the `Markdown` class using `TestCase.assertEqual`. An + `AssertionError` is raised if any value does not match the expected value. : `dedent`: Dedent triple-quoted strings. diff --git a/markdown/__meta__.py b/markdown/__meta__.py index c5b5a33..79ae2f8 100644 --- a/markdown/__meta__.py +++ b/markdown/__meta__.py @@ -26,7 +26,7 @@ License: BSD (see LICENSE.md for details). # (1, 2, 0, 'beta', 2) => "1.2b2" # (1, 2, 0, 'rc', 4) => "1.2rc4" # (1, 2, 0, 'final', 0) => "1.2" -__version_info__ = (3, 3, 3, 'final', 0) +__version_info__ = (3, 3, 4, 'final', 0) def _get_version(version_info): diff --git a/markdown/blockprocessors.py b/markdown/blockprocessors.py index 7d31a7f..8518e50 100644 --- a/markdown/blockprocessors.py +++ b/markdown/blockprocessors.py @@ -78,13 +78,15 @@ class BlockProcessor: else: return None - def detab(self, text): + def detab(self, text, length=None): """ Remove a tab from the front of each line of the given text. """ + if length is None: + length = self.tab_length newtext = [] lines = text.split('\n') for line in lines: - if line.startswith(' '*self.tab_length): - newtext.append(line[self.tab_length:]) + if line.startswith(' ' * length): + newtext.append(line[length:]) elif not line.strip(): newtext.append('') else: diff --git a/markdown/extensions/admonition.py b/markdown/extensions/admonition.py index 01f9940..9c66b4f 100644 --- a/markdown/extensions/admonition.py +++ b/markdown/extensions/admonition.py @@ -48,7 +48,7 @@ class AdmonitionProcessor(BlockProcessor): self.current_sibling = None self.content_indention = 0 - def get_sibling(self, parent, block): + def parse_content(self, parent, block): """Get sibling admontion. Retrieve the appropriate siblimg element. This can get trickly when @@ -56,13 +56,16 @@ class AdmonitionProcessor(BlockProcessor): """ + old_block = block + the_rest = '' + # We already acquired the block via test if self.current_sibling is not None: sibling = self.current_sibling - block = block[self.content_indent:] + block, the_rest = self.detab(block, self.content_indent) self.current_sibling = None self.content_indent = 0 - return sibling, block + return sibling, block, the_rest sibling = self.lastChild(parent) @@ -96,28 +99,31 @@ class AdmonitionProcessor(BlockProcessor): sibling = None if sibling is not None: + indent += self.tab_length + block, the_rest = self.detab(old_block, indent) self.current_sibling = sibling self.content_indent = indent - return sibling, block + return sibling, block, the_rest def test(self, parent, block): if self.RE.search(block): return True else: - return self.get_sibling(parent, block)[0] is not None + return self.parse_content(parent, block)[0] is not None def run(self, parent, blocks): block = blocks.pop(0) m = self.RE.search(block) if m: + if m.start() > 0: + self.parser.parseBlocks(parent, [block[:m.start()]]) block = block[m.end():] # removes the first line + block, theRest = self.detab(block) else: - sibling, block = self.get_sibling(parent, block) - - block, theRest = self.detab(block) + sibling, block, theRest = self.parse_content(parent, block) if m: klass, title = self.get_class_and_title(m) diff --git a/markdown/extensions/md_in_html.py b/markdown/extensions/md_in_html.py index eb8902e..86cf00d 100644 --- a/markdown/extensions/md_in_html.py +++ b/markdown/extensions/md_in_html.py @@ -19,7 +19,7 @@ from ..blockprocessors import BlockProcessor from ..preprocessors import Preprocessor from ..postprocessors import RawHtmlPostprocessor from .. import util -from ..htmlparser import HTMLExtractor +from ..htmlparser import HTMLExtractor, blank_line_re import xml.etree.ElementTree as etree @@ -85,17 +85,9 @@ class HTMLExtractorExtra(HTMLExtractor): else: # pragma: no cover return None - def at_line_start(self): - """At line start.""" - - value = super().at_line_start() - if not value and self.cleandoc and self.cleandoc[-1].endswith('\n'): - value = True - return value - def handle_starttag(self, tag, attrs): # Handle tags that should always be empty and do not specify a closing tag - if tag in self.empty_tags: + if tag in self.empty_tags and (self.at_line_start() or self.intail): attrs = {key: value if value is not None else key for key, value in attrs} if "markdown" in attrs: attrs.pop('markdown') @@ -106,13 +98,12 @@ class HTMLExtractorExtra(HTMLExtractor): self.handle_empty_tag(data, True) return - if tag in self.block_level_tags: + if tag in self.block_level_tags and (self.at_line_start() or self.intail): # Valueless attr (ex: ``) results in `[('checked', None)]`. # Convert to `{'checked': 'checked'}`. attrs = {key: value if value is not None else key for key, value in attrs} state = self.get_state(tag, attrs) - - if self.inraw or (state in [None, 'off'] and not self.mdstack) or not self.at_line_start(): + if self.inraw or (state in [None, 'off'] and not self.mdstack): # fall back to default behavior attrs.pop('markdown', None) super().handle_starttag(tag, attrs) @@ -134,6 +125,9 @@ class HTMLExtractorExtra(HTMLExtractor): self.handle_data(self.md.htmlStash.store(text)) else: self.handle_data(text) + if tag in self.CDATA_CONTENT_ELEMENTS: + # This is presumably a standalone tag in a code span (see #1036). + self.clear_cdata_mode() def handle_endtag(self, tag): if tag in self.block_level_tags: @@ -159,6 +153,11 @@ class HTMLExtractorExtra(HTMLExtractor): self.cleandoc.append(self.md.htmlStash.store(element)) self.cleandoc.append('\n\n') self.state = [] + # Check if element has a tail + if not blank_line_re.match( + self.rawdata[self.line_offset + self.offset + len(self.get_endtag_text(tag)):]): + # More content exists after endtag. + self.intail = True else: # Treat orphan closing tag as a span level tag. text = self.get_endtag_text(tag) @@ -191,6 +190,8 @@ class HTMLExtractorExtra(HTMLExtractor): self.handle_empty_tag(data, is_block=self.md.is_block_level(tag)) def handle_data(self, data): + if self.intail and '\n' in data: + self.intail = False if self.inraw or not self.mdstack: super().handle_data(data) else: @@ -203,10 +204,27 @@ class HTMLExtractorExtra(HTMLExtractor): if self.at_line_start() and is_block: self.handle_data('\n' + self.md.htmlStash.store(data) + '\n\n') else: - if self.mdstate and self.mdstate[-1] == "off": - self.handle_data(self.md.htmlStash.store(data)) - else: - self.handle_data(data) + self.handle_data(self.md.htmlStash.store(data)) + + def parse_pi(self, i): + if self.at_line_start() or self.intail or self.mdstack: + # The same override exists in HTMLExtractor without the check + # for mdstack. Therefore, use HTMLExtractor's parent instead. + return super(HTMLExtractor, self).parse_pi(i) + # This is not the beginning of a raw block so treat as plain data + # and avoid consuming any tags which may follow (see #1066). + self.handle_data(' self.toc_bottom: - continue text = get_name(el) # Do not override pre-existing ids @@ -278,14 +276,15 @@ class TocTreeprocessor(Treeprocessor): innertext = unescape(stashedHTML2text(text, self.md)) el.attrib["id"] = unique(self.slugify(innertext, self.sep), used_ids) - toc_tokens.append({ - 'level': int(el.tag[-1]), - 'id': el.attrib["id"], - 'name': unescape(stashedHTML2text( - code_escape(el.attrib.get('data-toc-label', text)), - self.md, strip_entities=False - )) - }) + if int(el.tag[-1]) >= self.toc_top and int(el.tag[-1]) <= self.toc_bottom: + toc_tokens.append({ + 'level': int(el.tag[-1]), + 'id': el.attrib["id"], + 'name': unescape(stashedHTML2text( + code_escape(el.attrib.get('data-toc-label', text)), + self.md, strip_entities=False + )) + }) # Remove the data-toc-label attribute as it is no longer needed if 'data-toc-label' in el.attrib: diff --git a/markdown/htmlparser.py b/markdown/htmlparser.py index fee9cd5..c08856a 100644 --- a/markdown/htmlparser.py +++ b/markdown/htmlparser.py @@ -39,6 +39,22 @@ htmlparser.entityref = re.compile(r'&([a-zA-Z][-.a-zA-Z0-9]*);') # so the 'incomplete' functionality is unnecessary. As the entityref regex is run right before incomplete, # and the two regex are the same, then incomplete will simply never match and we avoid the logic within. htmlparser.incomplete = htmlparser.entityref +# Monkeypatch HTMLParser to not accept a backtick in a tag name, attribute name, or bare value. +htmlparser.locatestarttagend_tolerant = re.compile(r""" + <[a-zA-Z][^`\t\n\r\f />\x00]* # tag name <= added backtick here + (?:[\s/]* # optional whitespace before attribute name + (?:(?<=['"\s/])[^`\s/>][^\s/=>]* # attribute name <= added backtick here + (?:\s*=+\s* # value indicator + (?:'[^']*' # LITA-enclosed value + |"[^"]*" # LIT-enclosed value + |(?!['"])[^`>\s]* # bare value <= added backtick here + ) + (?:\s*,)* # possibly followed by a comma + )?(?:\s|/(?!>))* + )* + )? + \s* # trailing whitespace +""", re.VERBOSE) # Match a blank line at the start of a block of text (two newlines). # The newlines may be preceded by additional whitespace. @@ -91,8 +107,14 @@ class HTMLExtractor(htmlparser.HTMLParser): @property def line_offset(self): """Returns char index in self.rawdata for the start of the current line. """ - if self.lineno > 1: - return re.match(r'([^\n]*\n){{{}}}'.format(self.lineno-1), self.rawdata).end() + if self.lineno > 1 and '\n' in self.rawdata: + m = re.match(r'([^\n]*\n){{{}}}'.format(self.lineno-1), self.rawdata) + if m: + return m.end() + else: # pragma: no cover + # Value of self.lineno must exceed total number of lines. + # Find index of begining of last line. + return self.rawdata.rfind('\n') return 0 def at_line_start(self): @@ -224,6 +246,22 @@ class HTMLExtractor(htmlparser.HTMLParser): end = ']]>' if data.startswith('CDATA[') else ']>' self.handle_empty_tag('{ replacements[key[3:-4]] }

' + else: + return key + + return replacements[key] + if replacements: - pattern = re.compile("|".join(re.escape(k) for k in replacements)) - processed_text = pattern.sub(lambda m: replacements[m.group(0)], text) + base_placeholder = util.HTML_PLACEHOLDER % r'([0-9]+)' + pattern = re.compile(f'

{ base_placeholder }

|{ base_placeholder }') + processed_text = pattern.sub(substitute_match, text) else: return text diff --git a/markdown/test_tools.py b/markdown/test_tools.py index 5f33619..21ae1a7 100644 --- a/markdown/test_tools.py +++ b/markdown/test_tools.py @@ -23,7 +23,7 @@ import os import sys import unittest import textwrap -from . import markdown, util +from . import markdown, Markdown, util try: import tidylib @@ -54,15 +54,25 @@ class TestCase(unittest.TestCase): default_kwargs = {} - def assertMarkdownRenders(self, source, expected, **kwargs): + def assertMarkdownRenders(self, source, expected, expected_attrs=None, **kwargs): """ Test that source Markdown text renders to expected output with given keywords. + + `expected_attrs` accepts a dict. Each key should be the name of an attribute + on the `Markdown` instance and the value should be the expected value after + the source text is parsed by Markdown. After the expected output is tested, + the expected value for each attribute is compared against the actual + attribute of the `Markdown` instance using `TestCase.assertEqual`. """ + expected_attrs = expected_attrs or {} kws = self.default_kwargs.copy() kws.update(kwargs) - output = markdown(source, **kws) + md = Markdown(**kws) + output = md.convert(source) self.assertMultiLineEqual(output, expected) + for key, value in expected_attrs.items(): + self.assertEqual(getattr(md, key), value) def dedent(self, text): """ diff --git a/markdown/treeprocessors.py b/markdown/treeprocessors.py index 055d8ac..eb6bf41 100644 --- a/markdown/treeprocessors.py +++ b/markdown/treeprocessors.py @@ -126,7 +126,8 @@ class InlineProcessor(Treeprocessor): """ if not isinstance(data, util.AtomicString): startIndex = 0 - while patternIndex < len(self.inlinePatterns): + count = len(self.inlinePatterns) + while patternIndex < count: data, matched, startIndex = self.__applyPattern( self.inlinePatterns[patternIndex], data, patternIndex, startIndex ) diff --git a/setup.py b/setup.py index 2e0ce18..5a29677 100755 --- a/setup.py +++ b/setup.py @@ -69,9 +69,9 @@ setup( long_description=long_description, long_description_content_type='text/markdown', author='Manfred Stienstra, Yuri takhteyev and Waylan limberg', - author_email='waylan.limberg@icloud.com', + author_email='python.markdown@gmail.com', maintainer='Waylan Limberg', - maintainer_email='waylan.limberg@icloud.com', + maintainer_email='python.markdown@gmail.com', license='BSD License', packages=['markdown', 'markdown.extensions'], python_requires='>=3.6', diff --git a/tests/test_extensions.py b/tests/test_extensions.py index 2b5f024..7a412b3 100644 --- a/tests/test_extensions.py +++ b/tests/test_extensions.py @@ -536,90 +536,6 @@ class TestTOC(TestCaseWithAssertStartsWith): {'level': 1, 'id': 'some-header-with-markup', 'name': 'Some Header with markup.', 'children': []}, ]) - def testAnchorLink(self): - """ Test TOC Anchorlink. """ - md = markdown.Markdown( - extensions=[markdown.extensions.toc.TocExtension(anchorlink=True)] - ) - text = '# Header 1\n\n## Header *2*' - self.assertEqual( - md.convert(text), - '

Header 1

\n' - '

Header 2

' - ) - - def testAnchorLinkWithSingleInlineCode(self): - """ Test TOC Anchorlink with single inline code. """ - md = markdown.Markdown( - extensions=[markdown.extensions.toc.TocExtension(anchorlink=True)] - ) - text = '# This is `code`.' - self.assertEqual( - md.convert(text), - '

' # noqa - '' # noqa - 'This is code.' # noqa - '' # noqa - '

' # noqa - ) - - def testAnchorLinkWithDoubleInlineCode(self): - """ Test TOC Anchorlink with double inline code. """ - md = markdown.Markdown( - extensions=[markdown.extensions.toc.TocExtension(anchorlink=True)] - ) - text = '# This is `code` and `this` too.' - self.assertEqual( - md.convert(text), - '

' # noqa - '' # noqa - 'This is code and this too.' # noqa - '' # noqa - '

' # noqa - ) - - def testPermalink(self): - """ Test TOC Permalink. """ - md = markdown.Markdown( - extensions=[markdown.extensions.toc.TocExtension(permalink=True)] - ) - text = '# Header' - self.assertEqual( - md.convert(text), - '

' # noqa - 'Header' # noqa - '' # noqa - '

' # noqa - ) - - def testPermalinkWithSingleInlineCode(self): - """ Test TOC Permalink with single inline code. """ - md = markdown.Markdown( - extensions=[markdown.extensions.toc.TocExtension(permalink=True)] - ) - text = '# This is `code`.' - self.assertEqual( - md.convert(text), - '

' # noqa - 'This is code.' # noqa - '' # noqa - '

' # noqa - ) - - def testPermalinkWithDoubleInlineCode(self): - """ Test TOC Permalink with double inline code. """ - md = markdown.Markdown( - extensions=[markdown.extensions.toc.TocExtension(permalink=True)] - ) - text = '# This is `code` and `this` too.' - self.assertEqual( - md.convert(text), - '

' # noqa - 'This is code and this too.' # noqa - '' # noqa - '

' # noqa - ) - def testTitle(self): """ Test TOC Title. """ md = markdown.Markdown( @@ -714,115 +630,6 @@ class TestTOC(TestCaseWithAssertStartsWith): '

[TOC]

' # noqa ) - def testMinMaxLevel(self): - """ Test toc_height setting """ - md = markdown.Markdown( - extensions=[markdown.extensions.toc.TocExtension(toc_depth='3-4')] - ) - text = '# Header 1 not in TOC\n\n## Header 2 not in TOC\n\n### Header 3\n\n####Header 4' - self.assertEqual( - md.convert(text), - '

Header 1 not in TOC

\n' - '

Header 2 not in TOC

\n' - '

Header 3

\n' - '

Header 4

' - ) - self.assertEqual( - md.toc, - '
\n' - '
    \n' # noqa - '
  • Header 3' # noqa - '\n' # noqa - '
  • \n' # noqa - '
\n' # noqa - '
\n' - ) - - self.assertNotIn("Header 1", md.toc) - - def testMaxLevel(self): - """ Test toc_depth setting """ - md = markdown.Markdown( - extensions=[markdown.extensions.toc.TocExtension(toc_depth=2)] - ) - text = '# Header 1\n\n## Header 2\n\n###Header 3 not in TOC' - self.assertEqual( - md.convert(text), - '

Header 1

\n' - '

Header 2

\n' - '

Header 3 not in TOC

' - ) - self.assertEqual( - md.toc, - '
\n' - '
    \n' # noqa - '
  • Header 1' # noqa - '\n' # noqa - '
  • \n' # noqa - '
\n' # noqa - '
\n' - ) - - self.assertNotIn("Header 3", md.toc) - - def testMinMaxLevelwithBaseLevel(self): - """ Test toc_height setting together with baselevel """ - md = markdown.Markdown( - extensions=[markdown.extensions.toc.TocExtension(toc_depth='4-6', - baselevel=3)] - ) - text = '# First Header\n\n## Second Level\n\n### Third Level' - self.assertEqual( - md.convert(text), - '

First Header

\n' - '

Second Level

\n' - '
Third Level
' - ) - self.assertEqual( - md.toc, - '
\n' - '\n' # noqa - '
\n' - ) - self.assertNotIn("First Header", md.toc) - - def testMaxLevelwithBaseLevel(self): - """ Test toc_depth setting together with baselevel """ - md = markdown.Markdown( - extensions=[markdown.extensions.toc.TocExtension(toc_depth=3, - baselevel=2)] - ) - text = '# Some Header\n\n## Next Level\n\n### Too High' - self.assertEqual( - md.convert(text), - '

Some Header

\n' - '

Next Level

\n' - '

Too High

' - ) - self.assertEqual( - md.toc, - '
\n' - '\n' # noqa - '
\n' - ) - self.assertNotIn("Too High", md.toc) - class TestSmarty(unittest.TestCase): def setUp(self): diff --git a/tests/test_syntax/blocks/test_html_blocks.py b/tests/test_syntax/blocks/test_html_blocks.py index 589f682..0ee47b6 100644 --- a/tests/test_syntax/blocks/test_html_blocks.py +++ b/tests/test_syntax/blocks/test_html_blocks.py @@ -21,6 +21,7 @@ License: BSD (see LICENSE.md for details). """ from markdown.test_tools import TestCase +import markdown class TestHTMLBlocks(TestCase): @@ -663,6 +664,48 @@ class TestHTMLBlocks(TestCase): '

<foo

' ) + def test_raw_unclosed_tag_in_code_span(self): + self.assertMarkdownRenders( + self.dedent( + """ + ` + hello + + """ + ), + self.dedent( + """ +

<div.

+
+ hello +
+ """ + ) + ) + + def test_raw_unclosed_tag_in_code_span_space(self): + self.assertMarkdownRenders( + self.dedent( + """ + `
+ hello +
+ """ + ), + self.dedent( + """ +

<div.

+
+ hello +
+ """ + ) + ) + def test_raw_attributes(self): self.assertMarkdownRenders( '

text

', @@ -1073,6 +1116,27 @@ class TestHTMLBlocks(TestCase): ) ) + def test_raw_processing_instruction_code_span(self): + self.assertMarkdownRenders( + self.dedent( + """ + ` + foo + + """ + ), + self.dedent( + """ +

<?php

+
+ foo +
+ """ + ) + ) + def test_raw_declaration_one_line(self): self.assertMarkdownRenders( '', @@ -1110,6 +1174,27 @@ class TestHTMLBlocks(TestCase): ) ) + def test_raw_declaration_code_span(self): + self.assertMarkdownRenders( + self.dedent( + """ + ` + foo + + """ + ), + self.dedent( + """ +

<!

+
+ foo +
+ """ + ) + ) + def test_raw_cdata_one_line(self): self.assertMarkdownRenders( '"); ]]>', @@ -1190,6 +1275,27 @@ class TestHTMLBlocks(TestCase): ) ) + def test_raw_cdata_code_span(self): + self.assertMarkdownRenders( + self.dedent( + """ + ` + foo + + """ + ), + self.dedent( + """ +

<![

+
+ foo +
+ """ + ) + ) + def test_charref(self): self.assertMarkdownRenders( '§', @@ -1501,3 +1607,13 @@ class TestHTMLBlocks(TestCase): """ ) ) + + def test_placeholder_in_source(self): + # This should never occur, but third party extensions could create weird edge cases. + md = markdown.Markdown() + # Ensure there is an htmlstash so relevant code (nested in `if replacements`) is run. + md.htmlStash.store('foo') + # Run with a placeholder which is not in the stash + placeholder = md.htmlStash.get_placeholder(md.htmlStash.html_counter + 1) + result = md.postprocessors['raw_html'].run(placeholder) + self.assertEqual(placeholder, result) diff --git a/tests/test_syntax/extensions/test_admonition.py b/tests/test_syntax/extensions/test_admonition.py index 39be997..44c70d1 100644 --- a/tests/test_syntax/extensions/test_admonition.py +++ b/tests/test_syntax/extensions/test_admonition.py @@ -192,3 +192,54 @@ class TestAdmonition(TestCase): ), extensions=['admonition', 'def_list'] ) + + def test_with_preceding_text(self): + self.assertMarkdownRenders( + self.dedent( + ''' + foo + **foo** + !!! note "Admonition" + ''' + ), + self.dedent( + ''' +

foo + foo

+
+

Admonition

+
+ ''' + ), + extensions=['admonition'] + ) + + def test_admontion_detabbing(self): + self.assertMarkdownRenders( + self.dedent( + ''' + !!! note "Admonition" + - Parent 1 + + - Child 1 + - Child 2 + ''' + ), + self.dedent( + ''' +
+

Admonition

+
    +
  • +

    Parent 1

    +
      +
    • Child 1
    • +
    • Child 2
    • +
    +
  • +
+
+ ''' + ), + extensions=['admonition'] + ) diff --git a/tests/test_syntax/extensions/test_md_in_html.py b/tests/test_syntax/extensions/test_md_in_html.py index 824917c..8655607 100644 --- a/tests/test_syntax/extensions/test_md_in_html.py +++ b/tests/test_syntax/extensions/test_md_in_html.py @@ -126,6 +126,72 @@ class TestMdInHTML(TestCase): ) ) + def test_md1_code_span(self): + self.assertMarkdownRenders( + self.dedent( + """ +
+ `

code span

` +
+ """ + ), + self.dedent( + """ +
+

<h1>code span</h1>

+
+ """ + ) + ) + + def test_md1_code_span_oneline(self): + self.assertMarkdownRenders( + '
`

code span

`
', + self.dedent( + """ +
+

<h1>code span</h1>

+
+ """ + ) + ) + + def test_md1_code_span_unclosed(self): + self.assertMarkdownRenders( + self.dedent( + """ +
+ `

` +

+ """ + ), + self.dedent( + """ +
+

<p>

+
+ """ + ) + ) + + def test_md1_code_span_script_tag(self): + self.assertMarkdownRenders( + self.dedent( + """ +
+ `