1620 lines
40 KiB
Python
1620 lines
40 KiB
Python
# -*- coding: utf-8 -*-
|
|
"""
|
|
Python Markdown
|
|
|
|
A Python implementation of John Gruber's Markdown.
|
|
|
|
Documentation: https://python-markdown.github.io/
|
|
GitHub: https://github.com/Python-Markdown/markdown/
|
|
PyPI: https://pypi.org/project/Markdown/
|
|
|
|
Started by Manfred Stienstra (http://www.dwerg.net/).
|
|
Maintained for a few years by Yuri Takhteyev (http://www.freewisdom.org).
|
|
Currently maintained by Waylan Limberg (https://github.com/waylan),
|
|
Dmitry Shachnev (https://github.com/mitya57) and Isaac Muse (https://github.com/facelessuser).
|
|
|
|
Copyright 2007-2018 The Python Markdown Project (v. 1.7 and later)
|
|
Copyright 2004, 2005, 2006 Yuri Takhteyev (v. 0.2-1.6b)
|
|
Copyright 2004 Manfred Stienstra (the original version)
|
|
|
|
License: BSD (see LICENSE.md for details).
|
|
"""
|
|
|
|
from markdown.test_tools import TestCase
|
|
import markdown
|
|
|
|
|
|
class TestHTMLBlocks(TestCase):
|
|
|
|
def test_raw_paragraph(self):
|
|
self.assertMarkdownRenders(
|
|
'<p>A raw paragraph.</p>',
|
|
'<p>A raw paragraph.</p>'
|
|
)
|
|
|
|
def test_raw_skip_inline_markdown(self):
|
|
self.assertMarkdownRenders(
|
|
'<p>A *raw* paragraph.</p>',
|
|
'<p>A *raw* paragraph.</p>'
|
|
)
|
|
|
|
def test_raw_indent_one_space(self):
|
|
self.assertMarkdownRenders(
|
|
' <p>A *raw* paragraph.</p>',
|
|
'<p>A *raw* paragraph.</p>'
|
|
)
|
|
|
|
def test_raw_indent_two_spaces(self):
|
|
self.assertMarkdownRenders(
|
|
' <p>A *raw* paragraph.</p>',
|
|
'<p>A *raw* paragraph.</p>'
|
|
)
|
|
|
|
def test_raw_indent_three_spaces(self):
|
|
self.assertMarkdownRenders(
|
|
' <p>A *raw* paragraph.</p>',
|
|
'<p>A *raw* paragraph.</p>'
|
|
)
|
|
|
|
def test_raw_indent_four_spaces(self):
|
|
self.assertMarkdownRenders(
|
|
' <p>code block</p>',
|
|
self.dedent(
|
|
"""
|
|
<pre><code><p>code block</p>
|
|
</code></pre>
|
|
"""
|
|
)
|
|
)
|
|
|
|
def test_raw_span(self):
|
|
self.assertMarkdownRenders(
|
|
'<span>*inline*</span>',
|
|
'<p><span><em>inline</em></span></p>'
|
|
)
|
|
|
|
def test_code_span(self):
|
|
self.assertMarkdownRenders(
|
|
'`<p>code span</p>`',
|
|
'<p><code><p>code span</p></code></p>'
|
|
)
|
|
|
|
def test_code_span_open_gt(self):
|
|
self.assertMarkdownRenders(
|
|
'*bar* `<` *foo*',
|
|
'<p><em>bar</em> <code><</code> <em>foo</em></p>'
|
|
)
|
|
|
|
def test_raw_empty(self):
|
|
self.assertMarkdownRenders(
|
|
'<p></p>',
|
|
'<p></p>'
|
|
)
|
|
|
|
def test_raw_empty_space(self):
|
|
self.assertMarkdownRenders(
|
|
'<p> </p>',
|
|
'<p> </p>'
|
|
)
|
|
|
|
def test_raw_empty_newline(self):
|
|
self.assertMarkdownRenders(
|
|
'<p>\n</p>',
|
|
'<p>\n</p>'
|
|
)
|
|
|
|
def test_raw_empty_blank_line(self):
|
|
self.assertMarkdownRenders(
|
|
'<p>\n\n</p>',
|
|
'<p>\n\n</p>'
|
|
)
|
|
|
|
def test_raw_uppercase(self):
|
|
self.assertMarkdownRenders(
|
|
'<DIV>*foo*</DIV>',
|
|
'<DIV>*foo*</DIV>'
|
|
)
|
|
|
|
def test_raw_uppercase_multiline(self):
|
|
self.assertMarkdownRenders(
|
|
self.dedent(
|
|
"""
|
|
<DIV>
|
|
*foo*
|
|
</DIV>
|
|
"""
|
|
),
|
|
self.dedent(
|
|
"""
|
|
<DIV>
|
|
*foo*
|
|
</DIV>
|
|
"""
|
|
)
|
|
)
|
|
|
|
def test_multiple_raw_single_line(self):
|
|
self.assertMarkdownRenders(
|
|
'<p>*foo*</p><div>*bar*</div>',
|
|
self.dedent(
|
|
"""
|
|
<p>*foo*</p>
|
|
<div>*bar*</div>
|
|
"""
|
|
)
|
|
)
|
|
|
|
def test_multiple_raw_single_line_with_pi(self):
|
|
self.assertMarkdownRenders(
|
|
"<p>*foo*</p><?php echo '>'; ?>",
|
|
self.dedent(
|
|
"""
|
|
<p>*foo*</p>
|
|
<?php echo '>'; ?>
|
|
"""
|
|
)
|
|
)
|
|
|
|
def test_multiline_raw(self):
|
|
self.assertMarkdownRenders(
|
|
self.dedent(
|
|
"""
|
|
<p>
|
|
A raw paragraph
|
|
with multiple lines.
|
|
</p>
|
|
"""
|
|
),
|
|
self.dedent(
|
|
"""
|
|
<p>
|
|
A raw paragraph
|
|
with multiple lines.
|
|
</p>
|
|
"""
|
|
)
|
|
)
|
|
|
|
def test_blank_lines_in_raw(self):
|
|
self.assertMarkdownRenders(
|
|
self.dedent(
|
|
"""
|
|
<p>
|
|
|
|
A raw paragraph...
|
|
|
|
with many blank lines.
|
|
|
|
</p>
|
|
"""
|
|
),
|
|
self.dedent(
|
|
"""
|
|
<p>
|
|
|
|
A raw paragraph...
|
|
|
|
with many blank lines.
|
|
|
|
</p>
|
|
"""
|
|
)
|
|
)
|
|
|
|
def test_raw_surrounded_by_Markdown(self):
|
|
self.assertMarkdownRenders(
|
|
self.dedent(
|
|
"""
|
|
Some *Markdown* text.
|
|
|
|
<p>*Raw* HTML.</p>
|
|
|
|
More *Markdown* text.
|
|
"""
|
|
),
|
|
self.dedent(
|
|
"""
|
|
<p>Some <em>Markdown</em> text.</p>
|
|
<p>*Raw* HTML.</p>
|
|
|
|
<p>More <em>Markdown</em> text.</p>
|
|
"""
|
|
)
|
|
)
|
|
|
|
def test_raw_surrounded_by_text_without_blank_lines(self):
|
|
self.assertMarkdownRenders(
|
|
self.dedent(
|
|
"""
|
|
Some *Markdown* text.
|
|
<p>*Raw* HTML.</p>
|
|
More *Markdown* text.
|
|
"""
|
|
),
|
|
self.dedent(
|
|
"""
|
|
<p>Some <em>Markdown</em> text.</p>
|
|
<p>*Raw* HTML.</p>
|
|
<p>More <em>Markdown</em> text.</p>
|
|
"""
|
|
)
|
|
)
|
|
|
|
def test_multiline_markdown_with_code_span(self):
|
|
self.assertMarkdownRenders(
|
|
self.dedent(
|
|
"""
|
|
A paragraph with a block-level
|
|
`<p>code span</p>`, which is
|
|
at the start of a line.
|
|
"""
|
|
),
|
|
self.dedent(
|
|
"""
|
|
<p>A paragraph with a block-level
|
|
<code><p>code span</p></code>, which is
|
|
at the start of a line.</p>
|
|
"""
|
|
)
|
|
)
|
|
|
|
def test_raw_block_preceded_by_markdown_code_span_with_unclosed_block_tag(self):
|
|
self.assertMarkdownRenders(
|
|
self.dedent(
|
|
"""
|
|
A paragraph with a block-level code span: `<div>`.
|
|
|
|
<p>*not markdown*</p>
|
|
|
|
This is *markdown*
|
|
"""
|
|
),
|
|
self.dedent(
|
|
"""
|
|
<p>A paragraph with a block-level code span: <code><div></code>.</p>
|
|
<p>*not markdown*</p>
|
|
|
|
<p>This is <em>markdown</em></p>
|
|
"""
|
|
)
|
|
)
|
|
|
|
def test_raw_one_line_followed_by_text(self):
|
|
self.assertMarkdownRenders(
|
|
'<p>*foo*</p>*bar*',
|
|
self.dedent(
|
|
"""
|
|
<p>*foo*</p>
|
|
<p><em>bar</em></p>
|
|
"""
|
|
)
|
|
)
|
|
|
|
def test_raw_one_line_followed_by_span(self):
|
|
self.assertMarkdownRenders(
|
|
"<p>*foo*</p><span>*bar*</span>",
|
|
self.dedent(
|
|
"""
|
|
<p>*foo*</p>
|
|
<p><span><em>bar</em></span></p>
|
|
"""
|
|
)
|
|
)
|
|
|
|
def test_raw_with_markdown_blocks(self):
|
|
self.assertMarkdownRenders(
|
|
self.dedent(
|
|
"""
|
|
<div>
|
|
Not a Markdown paragraph.
|
|
|
|
* Not a list item.
|
|
* Another non-list item.
|
|
|
|
Another non-Markdown paragraph.
|
|
</div>
|
|
"""
|
|
),
|
|
self.dedent(
|
|
"""
|
|
<div>
|
|
Not a Markdown paragraph.
|
|
|
|
* Not a list item.
|
|
* Another non-list item.
|
|
|
|
Another non-Markdown paragraph.
|
|
</div>
|
|
"""
|
|
)
|
|
)
|
|
|
|
def test_adjacent_raw_blocks(self):
|
|
self.assertMarkdownRenders(
|
|
self.dedent(
|
|
"""
|
|
<p>A raw paragraph.</p>
|
|
<p>A second raw paragraph.</p>
|
|
"""
|
|
),
|
|
self.dedent(
|
|
"""
|
|
<p>A raw paragraph.</p>
|
|
<p>A second raw paragraph.</p>
|
|
"""
|
|
)
|
|
)
|
|
|
|
def test_adjacent_raw_blocks_with_blank_lines(self):
|
|
self.assertMarkdownRenders(
|
|
self.dedent(
|
|
"""
|
|
<p>A raw paragraph.</p>
|
|
|
|
<p>A second raw paragraph.</p>
|
|
"""
|
|
),
|
|
self.dedent(
|
|
"""
|
|
<p>A raw paragraph.</p>
|
|
|
|
<p>A second raw paragraph.</p>
|
|
"""
|
|
)
|
|
)
|
|
|
|
def test_nested_raw_one_line(self):
|
|
self.assertMarkdownRenders(
|
|
'<div><p>*foo*</p></div>',
|
|
'<div><p>*foo*</p></div>'
|
|
)
|
|
|
|
def test_nested_raw_block(self):
|
|
self.assertMarkdownRenders(
|
|
self.dedent(
|
|
"""
|
|
<div>
|
|
<p>A raw paragraph.</p>
|
|
</div>
|
|
"""
|
|
),
|
|
self.dedent(
|
|
"""
|
|
<div>
|
|
<p>A raw paragraph.</p>
|
|
</div>
|
|
"""
|
|
)
|
|
)
|
|
|
|
def test_nested_indented_raw_block(self):
|
|
self.assertMarkdownRenders(
|
|
self.dedent(
|
|
"""
|
|
<div>
|
|
<p>A raw paragraph.</p>
|
|
</div>
|
|
"""
|
|
),
|
|
self.dedent(
|
|
"""
|
|
<div>
|
|
<p>A raw paragraph.</p>
|
|
</div>
|
|
"""
|
|
)
|
|
)
|
|
|
|
def test_nested_raw_blocks(self):
|
|
self.assertMarkdownRenders(
|
|
self.dedent(
|
|
"""
|
|
<div>
|
|
<p>A raw paragraph.</p>
|
|
<p>A second raw paragraph.</p>
|
|
</div>
|
|
"""
|
|
),
|
|
self.dedent(
|
|
"""
|
|
<div>
|
|
<p>A raw paragraph.</p>
|
|
<p>A second raw paragraph.</p>
|
|
</div>
|
|
"""
|
|
)
|
|
)
|
|
|
|
def test_nested_raw_blocks_with_blank_lines(self):
|
|
self.assertMarkdownRenders(
|
|
self.dedent(
|
|
"""
|
|
<div>
|
|
|
|
<p>A raw paragraph.</p>
|
|
|
|
<p>A second raw paragraph.</p>
|
|
|
|
</div>
|
|
"""
|
|
),
|
|
self.dedent(
|
|
"""
|
|
<div>
|
|
|
|
<p>A raw paragraph.</p>
|
|
|
|
<p>A second raw paragraph.</p>
|
|
|
|
</div>
|
|
"""
|
|
)
|
|
)
|
|
|
|
def test_nested_inline_one_line(self):
|
|
self.assertMarkdownRenders(
|
|
'<p><em>foo</em><br></p>',
|
|
'<p><em>foo</em><br></p>'
|
|
)
|
|
|
|
def test_raw_nested_inline(self):
|
|
self.assertMarkdownRenders(
|
|
self.dedent(
|
|
"""
|
|
<div>
|
|
<p>
|
|
<span>*text*</span>
|
|
</p>
|
|
</div>
|
|
"""
|
|
),
|
|
self.dedent(
|
|
"""
|
|
<div>
|
|
<p>
|
|
<span>*text*</span>
|
|
</p>
|
|
</div>
|
|
"""
|
|
)
|
|
)
|
|
|
|
def test_raw_nested_inline_with_blank_lines(self):
|
|
self.assertMarkdownRenders(
|
|
self.dedent(
|
|
"""
|
|
<div>
|
|
|
|
<p>
|
|
|
|
<span>*text*</span>
|
|
|
|
</p>
|
|
|
|
</div>
|
|
"""
|
|
),
|
|
self.dedent(
|
|
"""
|
|
<div>
|
|
|
|
<p>
|
|
|
|
<span>*text*</span>
|
|
|
|
</p>
|
|
|
|
</div>
|
|
"""
|
|
)
|
|
)
|
|
|
|
def test_raw_html5(self):
|
|
self.assertMarkdownRenders(
|
|
self.dedent(
|
|
"""
|
|
<section>
|
|
<header>
|
|
<hgroup>
|
|
<h1>Hello :-)</h1>
|
|
</hgroup>
|
|
</header>
|
|
<figure>
|
|
<img src="image.png" alt="" />
|
|
<figcaption>Caption</figcaption>
|
|
</figure>
|
|
<footer>
|
|
<p>Some footer</p>
|
|
</footer>
|
|
</section>
|
|
"""
|
|
),
|
|
self.dedent(
|
|
"""
|
|
<section>
|
|
<header>
|
|
<hgroup>
|
|
<h1>Hello :-)</h1>
|
|
</hgroup>
|
|
</header>
|
|
<figure>
|
|
<img src="image.png" alt="" />
|
|
<figcaption>Caption</figcaption>
|
|
</figure>
|
|
<footer>
|
|
<p>Some footer</p>
|
|
</footer>
|
|
</section>
|
|
"""
|
|
)
|
|
)
|
|
|
|
def test_raw_pre_tag(self):
|
|
self.assertMarkdownRenders(
|
|
self.dedent(
|
|
"""
|
|
Preserve whitespace in raw html
|
|
|
|
<pre>
|
|
class Foo():
|
|
bar = 'bar'
|
|
|
|
@property
|
|
def baz(self):
|
|
return self.bar
|
|
</pre>
|
|
"""
|
|
),
|
|
self.dedent(
|
|
"""
|
|
<p>Preserve whitespace in raw html</p>
|
|
<pre>
|
|
class Foo():
|
|
bar = 'bar'
|
|
|
|
@property
|
|
def baz(self):
|
|
return self.bar
|
|
</pre>
|
|
"""
|
|
)
|
|
)
|
|
|
|
def test_raw_pre_tag_nested_escaped_html(self):
|
|
self.assertMarkdownRenders(
|
|
self.dedent(
|
|
"""
|
|
<pre>
|
|
<p>foo</p>
|
|
</pre>
|
|
"""
|
|
),
|
|
self.dedent(
|
|
"""
|
|
<pre>
|
|
<p>foo</p>
|
|
</pre>
|
|
"""
|
|
)
|
|
)
|
|
|
|
def test_raw_p_no_end_tag(self):
|
|
self.assertMarkdownRenders(
|
|
'<p>*text*',
|
|
'<p>*text*'
|
|
)
|
|
|
|
def test_raw_multiple_p_no_end_tag(self):
|
|
self.assertMarkdownRenders(
|
|
self.dedent(
|
|
"""
|
|
<p>*text*'
|
|
|
|
<p>more *text*
|
|
"""
|
|
),
|
|
self.dedent(
|
|
"""
|
|
<p>*text*'
|
|
|
|
<p>more *text*
|
|
"""
|
|
)
|
|
)
|
|
|
|
def test_raw_p_no_end_tag_followed_by_blank_line(self):
|
|
self.assertMarkdownRenders(
|
|
self.dedent(
|
|
"""
|
|
<p>*raw text*'
|
|
|
|
Still part of *raw* text.
|
|
"""
|
|
),
|
|
self.dedent(
|
|
"""
|
|
<p>*raw text*'
|
|
|
|
Still part of *raw* text.
|
|
"""
|
|
)
|
|
)
|
|
|
|
def test_raw_nested_p_no_end_tag(self):
|
|
self.assertMarkdownRenders(
|
|
'<div><p>*text*</div>',
|
|
'<div><p>*text*</div>'
|
|
)
|
|
|
|
def test_raw_open_bracket_only(self):
|
|
self.assertMarkdownRenders(
|
|
'<',
|
|
'<p><</p>'
|
|
)
|
|
|
|
def test_raw_open_bracket_followed_by_space(self):
|
|
self.assertMarkdownRenders(
|
|
'< foo',
|
|
'<p>< foo</p>'
|
|
)
|
|
|
|
def test_raw_missing_close_bracket(self):
|
|
self.assertMarkdownRenders(
|
|
'<foo',
|
|
'<p><foo</p>'
|
|
)
|
|
|
|
def test_raw_unclosed_tag_in_code_span(self):
|
|
self.assertMarkdownRenders(
|
|
self.dedent(
|
|
"""
|
|
`<div`.
|
|
|
|
<div>
|
|
hello
|
|
</div>
|
|
"""
|
|
),
|
|
self.dedent(
|
|
"""
|
|
<p><code><div</code>.</p>
|
|
<div>
|
|
hello
|
|
</div>
|
|
"""
|
|
)
|
|
)
|
|
|
|
def test_raw_unclosed_tag_in_code_span_space(self):
|
|
self.assertMarkdownRenders(
|
|
self.dedent(
|
|
"""
|
|
` <div `.
|
|
|
|
<div>
|
|
hello
|
|
</div>
|
|
"""
|
|
),
|
|
self.dedent(
|
|
"""
|
|
<p><code><div</code>.</p>
|
|
<div>
|
|
hello
|
|
</div>
|
|
"""
|
|
)
|
|
)
|
|
|
|
def test_raw_attributes(self):
|
|
self.assertMarkdownRenders(
|
|
'<p id="foo", class="bar baz", style="margin: 15px; line-height: 1.5; text-align: center;">text</p>',
|
|
'<p id="foo", class="bar baz", style="margin: 15px; line-height: 1.5; text-align: center;">text</p>'
|
|
)
|
|
|
|
def test_raw_attributes_nested(self):
|
|
self.assertMarkdownRenders(
|
|
self.dedent(
|
|
"""
|
|
<div id="foo, class="bar", style="background: #ffe7e8; border: 2px solid #e66465;">
|
|
<p id="baz", style="margin: 15px; line-height: 1.5; text-align: center;">
|
|
<img scr="../foo.jpg" title="with 'quoted' text." valueless_attr weirdness="<i>foo</i>" />
|
|
</p>
|
|
</div>
|
|
"""
|
|
),
|
|
self.dedent(
|
|
"""
|
|
<div id="foo, class="bar", style="background: #ffe7e8; border: 2px solid #e66465;">
|
|
<p id="baz", style="margin: 15px; line-height: 1.5; text-align: center;">
|
|
<img scr="../foo.jpg" title="with 'quoted' text." valueless_attr weirdness="<i>foo</i>" />
|
|
</p>
|
|
</div>
|
|
"""
|
|
)
|
|
)
|
|
|
|
def test_raw_comment_one_line(self):
|
|
self.assertMarkdownRenders(
|
|
'<!-- *foo* -->',
|
|
'<!-- *foo* -->'
|
|
)
|
|
|
|
def test_raw_comment_one_line_with_tag(self):
|
|
self.assertMarkdownRenders(
|
|
'<!-- <tag> -->',
|
|
'<!-- <tag> -->'
|
|
)
|
|
|
|
def test_comment_in_code_span(self):
|
|
self.assertMarkdownRenders(
|
|
'`<!-- *foo* -->`',
|
|
'<p><code><!-- *foo* --></code></p>'
|
|
)
|
|
|
|
def test_raw_comment_one_line_followed_by_text(self):
|
|
self.assertMarkdownRenders(
|
|
'<!-- *foo* -->*bar*',
|
|
self.dedent(
|
|
"""
|
|
<!-- *foo* -->
|
|
<p><em>bar</em></p>
|
|
"""
|
|
)
|
|
)
|
|
|
|
def test_raw_comment_one_line_followed_by_html(self):
|
|
self.assertMarkdownRenders(
|
|
'<!-- *foo* --><p>*bar*</p>',
|
|
self.dedent(
|
|
"""
|
|
<!-- *foo* -->
|
|
<p>*bar*</p>
|
|
"""
|
|
)
|
|
)
|
|
|
|
# Note: Trailing (insignificant) whitespace is not preserved, which does not match the
|
|
# reference implementation. However, it is not a change in behavior for Python-Markdown.
|
|
def test_raw_comment_trailing_whitespace(self):
|
|
self.assertMarkdownRenders(
|
|
'<!-- *foo* --> ',
|
|
'<!-- *foo* -->'
|
|
)
|
|
|
|
# Note: this is a change in behavior for Python-Markdown, which does *not* match the reference
|
|
# implementation. However, it does match the HTML5 spec. Declarations must start with either
|
|
# `<!DOCTYPE` or `<![`. Anything else that starts with `<!` is a comment. According to the
|
|
# HTML5 spec, a comment without the hyphens is a "bogus comment", but a comment nonetheless.
|
|
# See https://www.w3.org/TR/html52/syntax.html#markup-declaration-open-state.
|
|
# If we wanted to change this behavior, we could override `HTMLParser.parse_bogus_comment()`.
|
|
def test_bogus_comment(self):
|
|
self.assertMarkdownRenders(
|
|
'<!*foo*>',
|
|
'<!--*foo*-->'
|
|
)
|
|
|
|
def test_raw_multiline_comment(self):
|
|
self.assertMarkdownRenders(
|
|
self.dedent(
|
|
"""
|
|
<!--
|
|
*foo*
|
|
-->
|
|
"""
|
|
),
|
|
self.dedent(
|
|
"""
|
|
<!--
|
|
*foo*
|
|
-->
|
|
"""
|
|
)
|
|
)
|
|
|
|
def test_raw_multiline_comment_with_tag(self):
|
|
self.assertMarkdownRenders(
|
|
self.dedent(
|
|
"""
|
|
<!--
|
|
<tag>
|
|
-->
|
|
"""
|
|
),
|
|
self.dedent(
|
|
"""
|
|
<!--
|
|
<tag>
|
|
-->
|
|
"""
|
|
)
|
|
)
|
|
|
|
def test_raw_multiline_comment_first_line(self):
|
|
self.assertMarkdownRenders(
|
|
self.dedent(
|
|
"""
|
|
<!-- *foo*
|
|
-->
|
|
"""
|
|
),
|
|
self.dedent(
|
|
"""
|
|
<!-- *foo*
|
|
-->
|
|
"""
|
|
)
|
|
)
|
|
|
|
def test_raw_multiline_comment_last_line(self):
|
|
self.assertMarkdownRenders(
|
|
self.dedent(
|
|
"""
|
|
<!--
|
|
*foo* -->
|
|
"""
|
|
),
|
|
self.dedent(
|
|
"""
|
|
<!--
|
|
*foo* -->
|
|
"""
|
|
)
|
|
)
|
|
|
|
def test_raw_comment_with_blank_lines(self):
|
|
self.assertMarkdownRenders(
|
|
self.dedent(
|
|
"""
|
|
<!--
|
|
|
|
*foo*
|
|
|
|
-->
|
|
"""
|
|
),
|
|
self.dedent(
|
|
"""
|
|
<!--
|
|
|
|
*foo*
|
|
|
|
-->
|
|
"""
|
|
)
|
|
)
|
|
|
|
def test_raw_comment_with_blank_lines_with_tag(self):
|
|
self.assertMarkdownRenders(
|
|
self.dedent(
|
|
"""
|
|
<!--
|
|
|
|
<tag>
|
|
|
|
-->
|
|
"""
|
|
),
|
|
self.dedent(
|
|
"""
|
|
<!--
|
|
|
|
<tag>
|
|
|
|
-->
|
|
"""
|
|
)
|
|
)
|
|
|
|
def test_raw_comment_with_blank_lines_first_line(self):
|
|
self.assertMarkdownRenders(
|
|
self.dedent(
|
|
"""
|
|
<!-- *foo*
|
|
|
|
-->
|
|
"""
|
|
),
|
|
self.dedent(
|
|
"""
|
|
<!-- *foo*
|
|
|
|
-->
|
|
"""
|
|
)
|
|
)
|
|
|
|
def test_raw_comment_with_blank_lines_last_line(self):
|
|
self.assertMarkdownRenders(
|
|
self.dedent(
|
|
"""
|
|
<!--
|
|
|
|
*foo* -->
|
|
"""
|
|
),
|
|
self.dedent(
|
|
"""
|
|
<!--
|
|
|
|
*foo* -->
|
|
"""
|
|
)
|
|
)
|
|
|
|
def test_raw_comment_indented(self):
|
|
self.assertMarkdownRenders(
|
|
self.dedent(
|
|
"""
|
|
<!--
|
|
|
|
*foo*
|
|
|
|
-->
|
|
"""
|
|
),
|
|
self.dedent(
|
|
"""
|
|
<!--
|
|
|
|
*foo*
|
|
|
|
-->
|
|
"""
|
|
)
|
|
)
|
|
|
|
def test_raw_comment_indented_with_tag(self):
|
|
self.assertMarkdownRenders(
|
|
self.dedent(
|
|
"""
|
|
<!--
|
|
|
|
<tag>
|
|
|
|
-->
|
|
"""
|
|
),
|
|
self.dedent(
|
|
"""
|
|
<!--
|
|
|
|
<tag>
|
|
|
|
-->
|
|
"""
|
|
)
|
|
)
|
|
|
|
def test_raw_comment_nested(self):
|
|
self.assertMarkdownRenders(
|
|
self.dedent(
|
|
"""
|
|
<div>
|
|
<!-- *foo* -->
|
|
</div>
|
|
"""
|
|
),
|
|
self.dedent(
|
|
"""
|
|
<div>
|
|
<!-- *foo* -->
|
|
</div>
|
|
"""
|
|
)
|
|
)
|
|
|
|
def test_comment_in_code_block(self):
|
|
self.assertMarkdownRenders(
|
|
' <!-- *foo* -->',
|
|
self.dedent(
|
|
"""
|
|
<pre><code><!-- *foo* -->
|
|
</code></pre>
|
|
"""
|
|
)
|
|
)
|
|
|
|
# Note: This is a change in behavior. Previously, Python-Markdown interpreted this in the same manner
|
|
# as browsers and all text after the opening comment tag was considered to be in a comment. However,
|
|
# that did not match the reference implementation. The new behavior does.
|
|
def test_unclosed_comment_(self):
|
|
self.assertMarkdownRenders(
|
|
self.dedent(
|
|
"""
|
|
<!-- unclosed comment
|
|
|
|
*not* a comment
|
|
"""
|
|
),
|
|
self.dedent(
|
|
"""
|
|
<p><!-- unclosed comment</p>
|
|
<p><em>not</em> a comment</p>
|
|
"""
|
|
)
|
|
)
|
|
|
|
def test_raw_processing_instruction_one_line(self):
|
|
self.assertMarkdownRenders(
|
|
"<?php echo '>'; ?>",
|
|
"<?php echo '>'; ?>"
|
|
)
|
|
|
|
# This is a change in behavior and does not match the reference implementation.
|
|
# We have no way to determine if text is on the same line, so we get this. TODO: reevaluate!
|
|
def test_raw_processing_instruction_one_line_followed_by_text(self):
|
|
self.assertMarkdownRenders(
|
|
"<?php echo '>'; ?>*bar*",
|
|
self.dedent(
|
|
"""
|
|
<?php echo '>'; ?>
|
|
<p><em>bar</em></p>
|
|
"""
|
|
)
|
|
)
|
|
|
|
def test_raw_multiline_processing_instruction(self):
|
|
self.assertMarkdownRenders(
|
|
self.dedent(
|
|
"""
|
|
<?php
|
|
echo '>';
|
|
?>
|
|
"""
|
|
),
|
|
self.dedent(
|
|
"""
|
|
<?php
|
|
echo '>';
|
|
?>
|
|
"""
|
|
)
|
|
)
|
|
|
|
def test_raw_processing_instruction_with_blank_lines(self):
|
|
self.assertMarkdownRenders(
|
|
self.dedent(
|
|
"""
|
|
<?php
|
|
|
|
echo '>';
|
|
|
|
?>
|
|
"""
|
|
),
|
|
self.dedent(
|
|
"""
|
|
<?php
|
|
|
|
echo '>';
|
|
|
|
?>
|
|
"""
|
|
)
|
|
)
|
|
|
|
def test_raw_processing_instruction_indented(self):
|
|
self.assertMarkdownRenders(
|
|
self.dedent(
|
|
"""
|
|
<?php
|
|
|
|
echo '>';
|
|
|
|
?>
|
|
"""
|
|
),
|
|
self.dedent(
|
|
"""
|
|
<?php
|
|
|
|
echo '>';
|
|
|
|
?>
|
|
"""
|
|
)
|
|
)
|
|
|
|
def test_raw_processing_instruction_code_span(self):
|
|
self.assertMarkdownRenders(
|
|
self.dedent(
|
|
"""
|
|
`<?php`
|
|
|
|
<div>
|
|
foo
|
|
</div>
|
|
"""
|
|
),
|
|
self.dedent(
|
|
"""
|
|
<p><code><?php</code></p>
|
|
<div>
|
|
foo
|
|
</div>
|
|
"""
|
|
)
|
|
)
|
|
|
|
def test_raw_declaration_one_line(self):
|
|
self.assertMarkdownRenders(
|
|
'<!DOCTYPE html>',
|
|
'<!DOCTYPE html>'
|
|
)
|
|
|
|
# This is a change in behavior and does not match the reference implementation.
|
|
# We have no way to determine if text is on the same line, so we get this. TODO: reevaluate!
|
|
def test_raw_declaration_one_line_followed_by_text(self):
|
|
self.assertMarkdownRenders(
|
|
'<!DOCTYPE html>*bar*',
|
|
self.dedent(
|
|
"""
|
|
<!DOCTYPE html>
|
|
<p><em>bar</em></p>
|
|
"""
|
|
)
|
|
)
|
|
|
|
def test_raw_multiline_declaration(self):
|
|
self.assertMarkdownRenders(
|
|
self.dedent(
|
|
"""
|
|
<!DOCTYPE html PUBLIC
|
|
"-//W3C//DTD XHTML 1.1//EN"
|
|
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
|
|
"""
|
|
),
|
|
self.dedent(
|
|
"""
|
|
<!DOCTYPE html PUBLIC
|
|
"-//W3C//DTD XHTML 1.1//EN"
|
|
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
|
|
"""
|
|
)
|
|
)
|
|
|
|
def test_raw_declaration_code_span(self):
|
|
self.assertMarkdownRenders(
|
|
self.dedent(
|
|
"""
|
|
`<!`
|
|
|
|
<div>
|
|
foo
|
|
</div>
|
|
"""
|
|
),
|
|
self.dedent(
|
|
"""
|
|
<p><code><!</code></p>
|
|
<div>
|
|
foo
|
|
</div>
|
|
"""
|
|
)
|
|
)
|
|
|
|
def test_raw_cdata_one_line(self):
|
|
self.assertMarkdownRenders(
|
|
'<![CDATA[ document.write(">"); ]]>',
|
|
'<![CDATA[ document.write(">"); ]]>'
|
|
)
|
|
|
|
# Note: this is a change. Neither previous output nor this match reference implementation.
|
|
def test_raw_cdata_one_line_followed_by_text(self):
|
|
self.assertMarkdownRenders(
|
|
'<![CDATA[ document.write(">"); ]]>*bar*',
|
|
self.dedent(
|
|
"""
|
|
<![CDATA[ document.write(">"); ]]>
|
|
<p><em>bar</em></p>
|
|
"""
|
|
)
|
|
)
|
|
|
|
def test_raw_multiline_cdata(self):
|
|
self.assertMarkdownRenders(
|
|
self.dedent(
|
|
"""
|
|
<![CDATA[
|
|
document.write(">");
|
|
]]>
|
|
"""
|
|
),
|
|
self.dedent(
|
|
"""
|
|
<![CDATA[
|
|
document.write(">");
|
|
]]>
|
|
"""
|
|
)
|
|
)
|
|
|
|
def test_raw_cdata_with_blank_lines(self):
|
|
self.assertMarkdownRenders(
|
|
self.dedent(
|
|
"""
|
|
<![CDATA[
|
|
|
|
document.write(">");
|
|
|
|
]]>
|
|
"""
|
|
),
|
|
self.dedent(
|
|
"""
|
|
<![CDATA[
|
|
|
|
document.write(">");
|
|
|
|
]]>
|
|
"""
|
|
)
|
|
)
|
|
|
|
def test_raw_cdata_indented(self):
|
|
self.assertMarkdownRenders(
|
|
self.dedent(
|
|
"""
|
|
<![CDATA[
|
|
|
|
document.write(">");
|
|
|
|
]]>
|
|
"""
|
|
),
|
|
self.dedent(
|
|
"""
|
|
<![CDATA[
|
|
|
|
document.write(">");
|
|
|
|
]]>
|
|
"""
|
|
)
|
|
)
|
|
|
|
def test_raw_cdata_code_span(self):
|
|
self.assertMarkdownRenders(
|
|
self.dedent(
|
|
"""
|
|
`< stuff>
|
|
|
|
<some>> <<unbalanced>> <<brackets>
|
|
"""
|
|
),
|
|
self.dedent(
|
|
"""
|
|
<p><some <a href="http://example.com">weird</a> stuff></p>
|
|
<p><some>> <<unbalanced>> <<brackets></p>
|
|
"""
|
|
)
|
|
)
|
|
|
|
def test_script_tags(self):
|
|
self.assertMarkdownRenders(
|
|
self.dedent(
|
|
"""
|
|
<script>
|
|
*random stuff* <div> &
|
|
</script>
|
|
|
|
<style>
|
|
**more stuff**
|
|
</style>
|
|
"""
|
|
),
|
|
self.dedent(
|
|
"""
|
|
<script>
|
|
*random stuff* <div> &
|
|
</script>
|
|
|
|
<style>
|
|
**more stuff**
|
|
</style>
|
|
"""
|
|
)
|
|
)
|
|
|
|
def test_unclosed_script_tag(self):
|
|
# Ensure we have a working fix for https://bugs.python.org/issue41989
|
|
self.assertMarkdownRenders(
|
|
self.dedent(
|
|
"""
|
|
<script>
|
|
*random stuff* <div> &
|
|
|
|
Still part of the *script* tag
|
|
"""
|
|
),
|
|
self.dedent(
|
|
"""
|
|
<script>
|
|
*random stuff* <div> &
|
|
|
|
Still part of the *script* tag
|
|
"""
|
|
)
|
|
)
|
|
|
|
def test_inline_script_tags(self):
|
|
# Ensure inline script tags doesn't cause the parser to eat content (see #1036).
|
|
self.assertMarkdownRenders(
|
|
self.dedent(
|
|
"""
|
|
Text `<script>` more *text*.
|
|
|
|
<div>
|
|
*foo*
|
|
</div>
|
|
|
|
<div>
|
|
|
|
bar
|
|
|
|
</div>
|
|
|
|
A new paragraph with a closing `</script>` tag.
|
|
"""
|
|
),
|
|
self.dedent(
|
|
"""
|
|
<p>Text <code><script></code> more <em>text</em>.</p>
|
|
<div>
|
|
*foo*
|
|
</div>
|
|
|
|
<div>
|
|
|
|
bar
|
|
|
|
</div>
|
|
|
|
<p>A new paragraph with a closing <code></script></code> tag.</p>
|
|
"""
|
|
)
|
|
)
|
|
|
|
def test_hr_only_start(self):
|
|
self.assertMarkdownRenders(
|
|
self.dedent(
|
|
"""
|
|
*emphasis1*
|
|
<hr>
|
|
*emphasis2*
|
|
"""
|
|
),
|
|
self.dedent(
|
|
"""
|
|
<p><em>emphasis1</em></p>
|
|
<hr>
|
|
<p><em>emphasis2</em></p>
|
|
"""
|
|
)
|
|
)
|
|
|
|
def test_hr_self_close(self):
|
|
self.assertMarkdownRenders(
|
|
self.dedent(
|
|
"""
|
|
*emphasis1*
|
|
<hr/>
|
|
*emphasis2*
|
|
"""
|
|
),
|
|
self.dedent(
|
|
"""
|
|
<p><em>emphasis1</em></p>
|
|
<hr/>
|
|
<p><em>emphasis2</em></p>
|
|
"""
|
|
)
|
|
)
|
|
|
|
def test_hr_start_and_end(self):
|
|
# Browsers ignore ending hr tags, so we don't try to do anything to handle them special.
|
|
self.assertMarkdownRenders(
|
|
self.dedent(
|
|
"""
|
|
*emphasis1*
|
|
<hr></hr>
|
|
*emphasis2*
|
|
"""
|
|
),
|
|
self.dedent(
|
|
"""
|
|
<p><em>emphasis1</em></p>
|
|
<hr>
|
|
<p></hr>
|
|
<em>emphasis2</em></p>
|
|
"""
|
|
)
|
|
)
|
|
|
|
def test_hr_only_end(self):
|
|
# Browsers ignore ending hr tags, so we don't try to do anything to handle them special.
|
|
self.assertMarkdownRenders(
|
|
self.dedent(
|
|
"""
|
|
*emphasis1*
|
|
</hr>
|
|
*emphasis2*
|
|
"""
|
|
),
|
|
self.dedent(
|
|
"""
|
|
<p><em>emphasis1</em>
|
|
</hr>
|
|
<em>emphasis2</em></p>
|
|
"""
|
|
)
|
|
)
|
|
|
|
def test_hr_with_content(self):
|
|
# Browsers ignore ending hr tags, so we don't try to do anything to handle them special.
|
|
# Content is not allowed and will be treated as normal content between two hr tags.
|
|
self.assertMarkdownRenders(
|
|
self.dedent(
|
|
"""
|
|
*emphasis1*
|
|
<hr>
|
|
**content**
|
|
</hr>
|
|
*emphasis2*
|
|
"""
|
|
),
|
|
self.dedent(
|
|
"""
|
|
<p><em>emphasis1</em></p>
|
|
<hr>
|
|
<p><strong>content</strong>
|
|
</hr>
|
|
<em>emphasis2</em></p>
|
|
"""
|
|
)
|
|
)
|
|
|
|
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)
|