Python-Markdown Change Log
=========================
+Oct 12, 2020: version 3.3.1 (a bug-fix release).
+
+* Correctly parse raw `script` and `style` tags (#1036).
+* Ensure consistent class handling by `fenced_code` and `codehilite` (#1032).
+
Oct 6, 2020: version 3.3 ([Notes](release-3.3.md)).
May 8, 2020: version 3.2.2 (a bug-fix release).
Python-Markdown follows [Semantic Versioning] and uses the
`MAJOR.MINOR.PATCH[.dev#|a#|b#|rc#]` format for identifying releases. The status
of the `master` branch should always be identified in the `__version_info__`
-tuple defined in [`markdown/__init__.py`][markdown/__init__.py]. The contents of
+tuple defined in [`markdown/__meta__.py`][markdown/__meta__.py]. The contents of
that tuple will automatically be converted into a normalized version which
conforms to [PEP 440]. An invalid `__version_info__` tuple will raise an error,
preventing the library from running and the package from building.
2. Confirm that the release notes and change log have been updated and indicate
the date of the new release.
-3. Update the version defined in [`markdown/__init__.py`][markdown/__init__.py].
+3. Update the version defined in [`markdown/__meta__.py`][markdown/__meta__.py].
4. Build a local copy of the documentation, browse through the pages and
confirm that no obvious issues exist with the documentation.
[aspell]: http://aspell.net/
[test tools]: test_tools.md
[Semantic Versioning]: https://semver.org/
-[markdown/__init__.py]: https://github.com/Python-Markdown/markdown/blob/master/markdown/__init__.py#L43
+[markdown/__meta__.py]: https://github.com/Python-Markdown/markdown/blob/master/markdown/__meta__.py#L29
[PEP 440]: https://www.python.org/dev/peps/pep-0440/
[PyPI]: https://pypi.org/project/Markdown/
[Python-Markdown/Python-Markdown.github.io]: https://github.com/Python-Markdown/Python-Markdown.github.io
````
When defining multiple classes, only the first class will be used as the "language" for the code block. All others are
-assigned to the `<code>` tag unaltered. Additionally, the curly braces and dot are required for all classes, including
+assigned to the `<pre>` tag unaltered. Additionally, the curly braces and dot are required for all classes, including
the language class if more than one class is defined.
The above example will output the following HTML:
```html
-<pre><code class="language-html foo bar"><p>HTML Document</p>
+<pre class="foo bar"><code class="language-html"><p>HTML Document</p>
</code></pre>
```
# (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, 0, 'final', 0)
+__version_info__ = (3, 3, 1, 'final', 0)
def _get_version(version_info):
if m.group('attrs'):
id, classes, config = self.handle_attrs(get_attrs(m.group('attrs')))
if len(classes):
- lang = classes[0]
+ lang = classes.pop(0)
else:
if m.group('lang'):
lang = m.group('lang')
- classes.append(lang)
if m.group('hl_lines'):
# Support hl_lines outside of attrs for backward-compatibility
config['hl_lines'] = parse_hl_lines(m.group('hl_lines'))
code = highliter.hilite()
else:
- id_attr = class_attr = kv_pairs = ''
+ id_attr = lang_attr = class_attr = kv_pairs = ''
+ if lang:
+ lang_attr = ' class="{}{}"'.format(self.config.get('lang_prefix', 'language-'), lang)
if classes:
- class_attr = ' class="{}{}"'.format(
- self.config.get('lang_prefix', 'language-'),
- ' '.join(classes)
- )
+ class_attr = ' class="{}"'.format(' '.join(classes))
if id:
id_attr = ' id="{}"'.format(id)
if self.use_attr_list and config and not config.get('use_pygments', False):
kv_pairs = ' ' + ' '.join(
'{k}="{v}"'.format(k=k, v=v) for k, v in config.items() if k != 'use_pygments'
)
- code = '<pre{id}><code{cls}{kv}>{code}</code></pre>'.format(
+ code = '<pre{id}{cls}><code{lang}{kv}>{code}</code></pre>'.format(
id=id_attr,
cls=class_attr,
+ lang=lang_attr,
kv=kv_pairs,
code=self._escape(m.group('code'))
)
def close(self):
"""Handle any buffered data."""
super().close()
+ if len(self.rawdata):
+ # Temp fix for https://bugs.python.org/issue41989
+ # TODO: remove this when the bug is fixed in all supported Python versions.
+ if self.convert_charrefs and not self.cdata_elem: # pragma: no cover
+ self.handle_data(htmlparser.unescape(self.rawdata))
+ else:
+ self.handle_data(self.rawdata)
# Handle any unclosed tags.
if len(self._cache):
self.cleandoc.append(self.md.htmlStash.store(''.join(self._cache)))
self._cache.append(text)
else:
self.cleandoc.append(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):
text = self.get_endtag_text(tag)
def unknown_decl(self, data):
end = ']]>' if data.startswith('CDATA[') else ']>'
self.handle_empty_tag('<![{}{}'.format(data, end), is_block=True)
+
+ # The rest has been copied from base class in standard lib to address #1036.
+ # As __startag_text is private, all references to it must be in this subclass.
+ # The last few lines of parse_starttag are reversed so that handle_starttag
+ # can override cdata_mode in certain situations (in a code span).
+ __starttag_text = None
+
+ def get_starttag_text(self):
+ """Return full source of start tag: '<...>'."""
+ return self.__starttag_text
+
+ def parse_starttag(self, i): # pragma: no cover
+ self.__starttag_text = None
+ endpos = self.check_for_whole_start_tag(i)
+ if endpos < 0:
+ return endpos
+ rawdata = self.rawdata
+ self.__starttag_text = rawdata[i:endpos]
+
+ # Now parse the data between i+1 and j into a tag and attrs
+ attrs = []
+ match = htmlparser.tagfind_tolerant.match(rawdata, i+1)
+ assert match, 'unexpected call to parse_starttag()'
+ k = match.end()
+ self.lasttag = tag = match.group(1).lower()
+ while k < endpos:
+ m = htmlparser.attrfind_tolerant.match(rawdata, k)
+ if not m:
+ break
+ attrname, rest, attrvalue = m.group(1, 2, 3)
+ if not rest:
+ attrvalue = None
+ elif attrvalue[:1] == '\'' == attrvalue[-1:] or \
+ attrvalue[:1] == '"' == attrvalue[-1:]: # noqa: E127
+ attrvalue = attrvalue[1:-1]
+ if attrvalue:
+ attrvalue = htmlparser.unescape(attrvalue)
+ attrs.append((attrname.lower(), attrvalue))
+ k = m.end()
+
+ end = rawdata[k:endpos].strip()
+ if end not in (">", "/>"):
+ lineno, offset = self.getpos()
+ if "\n" in self.__starttag_text:
+ lineno = lineno + self.__starttag_text.count("\n")
+ offset = len(self.__starttag_text) \
+ - self.__starttag_text.rfind("\n") # noqa: E127
+ else:
+ offset = offset + len(self.__starttag_text)
+ self.handle_data(rawdata[i:endpos])
+ return endpos
+ if end.endswith('/>'):
+ # XHTML-style empty tag: <span attr="value" />
+ self.handle_startendtag(tag, attrs)
+ else:
+ # *** set cdata_mode first so we can override it in handle_starttag (see #1036) ***
+ if tag in self.CDATA_CONTENT_ELEMENTS:
+ self.set_cdata_mode(tag)
+ self.handle_starttag(tag, attrs)
+ return endpos
expected = f.read().replace("\r\n", "\n")
output = markdown(input, **kwargs)
if tidylib and normalize:
- expected = _normalize_whitespace(expected)
- output = _normalize_whitespace(output)
+ try:
+ expected = _normalize_whitespace(expected)
+ output = _normalize_whitespace(output)
+ except OSError:
+ self.skipTest("Tidylib's c library not available.")
elif normalize:
self.skipTest('Tidylib not available.')
self.assertMultiLineEqual(output, expected)
"""
)
)
+
+ 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>
+ """
+ )
+ )
from markdown.test_tools import TestCase
from markdown.extensions.codehilite import CodeHiliteExtension, CodeHilite
+import os
try:
import pygments # noqa
except ImportError:
has_pygments = False
+# The version required by the tests is the version specified and installed in the 'pygments' tox env.
+# In any environment where the PYGMENTS_VERSION environment variabe is either not defined or doesn't
+# match the version of Pygments installed, all tests which rely in pygments will be skipped.
+required_pygments_version = os.environ.get('PYGMENTS_VERSION', '')
+
class TestCodeHiliteClass(TestCase):
""" Test the markdown.extensions.codehilite.CodeHilite class. """
+ def setUp(self):
+ if has_pygments and pygments.__version__ != required_pygments_version:
+ self.skipTest(f'Pygments=={required_pygments_version} is required')
+
maxDiff = None
def assertOutputEquals(self, source, expected, **options):
def test_codehilite_linenos_inline(self):
if has_pygments:
expected = (
- '<div class="codehilite"><pre><span></span><code><span class="lineno">1 </span>plain text\n'
+ '<div class="codehilite"><pre><span></span><code><span class="linenos">1</span>plain text\n'
'</code></pre></div>'
)
else:
def test_codehilite_linenostart(self):
if has_pygments:
expected = (
- '<div class="codehilite"><pre><span></span><code><span class="lineno">42 </span>plain text\n'
+ '<div class="codehilite"><pre><span></span><code><span class="linenos">42</span>plain text\n'
'</code></pre></div>'
)
else:
if has_pygments:
expected = (
'<div class="codehilite"><pre><span></span><code>'
- '<span class="lineno">1 </span><span class="hll">line 1\n'
- '</span><span class="lineno">2 </span>line 2\n'
- '<span class="lineno">3 </span><span class="hll">line 3\n'
+ '<span class="linenos">1</span><span class="hll">line 1\n'
+ '</span><span class="linenos">2</span>line 2\n'
+ '<span class="linenos">3</span><span class="hll">line 3\n'
'</span></code></pre></div>'
)
else:
def test_codehilite_linenos_linenostep(self):
if has_pygments:
expected = (
- '<div class="codehilite"><pre><span></span><code><span class="lineno"> </span>line 1\n'
- '<span class="lineno">2 </span>line 2\n'
- '<span class="lineno"> </span>line 3\n'
+ '<div class="codehilite"><pre><span></span><code><span class="linenos"> </span>line 1\n'
+ '<span class="linenos">2</span>line 2\n'
+ '<span class="linenos"> </span>line 3\n'
'</code></pre></div>'
)
else:
def test_codehilite_linenos_linenospecial(self):
if has_pygments:
expected = (
- '<div class="codehilite"><pre><span></span><code><span class="lineno">1 </span>line 1\n'
- '<span class="lineno special">2 </span>line 2\n'
- '<span class="lineno">3 </span>line 3\n'
+ '<div class="codehilite"><pre><span></span><code><span class="linenos">1</span>line 1\n'
+ '<span class="linenos special">2</span>line 2\n'
+ '<span class="linenos">3</span>line 3\n'
'</code></pre></div>'
)
else:
class TestCodeHiliteExtension(TestCase):
""" Test codehilite extension. """
+ def setUp(self):
+ if has_pygments and pygments.__version__ != required_pygments_version:
+ self.skipTest(f'Pygments=={required_pygments_version} is required')
+
maxDiff = None
def testBasicCodeHilite(self):
from markdown.test_tools import TestCase
import markdown
+import os
+try:
+ import pygments # noqa
+ has_pygments = True
+except ImportError:
+ has_pygments = False
-class TestFencedCode(TestCase):
+# The version required by the tests is the version specified and installed in the 'pygments' tox env.
+# In any environment where the PYGMENTS_VERSION environment variabe is either not defined or doesn't
+# match the version of Pygments installed, all tests which rely in pygments will be skipped.
+required_pygments_version = os.environ.get('PYGMENTS_VERSION', '')
- def setUp(self):
- self.has_pygments = True
- try:
- import pygments # noqa
- except ImportError:
- self.has_pygments = False
+
+class TestFencedCode(TestCase):
def testBasicFence(self):
self.assertMarkdownRenders(
extensions=['fenced_code']
)
+ def test_fenced_code_in_raw_html(self):
+ self.assertMarkdownRenders(
+ self.dedent(
+ """
+ <details>
+ ```
+ Begone placeholders!
+ ```
+ </details>
+ """
+ ),
+ self.dedent(
+ """
+ <details>
+
+ <pre><code>Begone placeholders!
+ </code></pre>
+
+ </details>
+ """
+ ),
+ extensions=['fenced_code']
+ )
+
+ def testFencedLanguageInAttr(self):
+ self.assertMarkdownRenders(
+ self.dedent(
+ '''
+ ``` {.python}
+ # Some python code
+ ```
+ '''
+ ),
+ self.dedent(
+ '''
+ <pre><code class="language-python"># Some python code
+ </code></pre>
+ '''
+ ),
+ extensions=['fenced_code']
+ )
+
+ def testFencedMultipleClassesInAttr(self):
+ self.assertMarkdownRenders(
+ self.dedent(
+ '''
+ ``` {.python .foo .bar}
+ # Some python code
+ ```
+ '''
+ ),
+ self.dedent(
+ '''
+ <pre class="foo bar"><code class="language-python"># Some python code
+ </code></pre>
+ '''
+ ),
+ extensions=['fenced_code']
+ )
+
+ def testFencedIdInAttr(self):
+ self.assertMarkdownRenders(
+ self.dedent(
+ '''
+ ``` { #foo }
+ # Some python code
+ ```
+ '''
+ ),
+ self.dedent(
+ '''
+ <pre id="foo"><code># Some python code
+ </code></pre>
+ '''
+ ),
+ extensions=['fenced_code']
+ )
+
+ def testFencedIdAndLangInAttr(self):
+ self.assertMarkdownRenders(
+ self.dedent(
+ '''
+ ``` { .python #foo }
+ # Some python code
+ ```
+ '''
+ ),
+ self.dedent(
+ '''
+ <pre id="foo"><code class="language-python"># Some python code
+ </code></pre>
+ '''
+ ),
+ extensions=['fenced_code']
+ )
+
+ def testFencedIdAndLangAndClassInAttr(self):
+ self.assertMarkdownRenders(
+ self.dedent(
+ '''
+ ``` { .python #foo .bar }
+ # Some python code
+ ```
+ '''
+ ),
+ self.dedent(
+ '''
+ <pre id="foo" class="bar"><code class="language-python"># Some python code
+ </code></pre>
+ '''
+ ),
+ extensions=['fenced_code']
+ )
+
+ def testFencedLanguageIdAndPygmentsDisabledInAttrNoCodehilite(self):
+ self.assertMarkdownRenders(
+ self.dedent(
+ '''
+ ``` { .python #foo use_pygments=False }
+ # Some python code
+ ```
+ '''
+ ),
+ self.dedent(
+ '''
+ <pre id="foo"><code class="language-python"># Some python code
+ </code></pre>
+ '''
+ ),
+ extensions=['fenced_code']
+ )
+
+ def testFencedLanguageIdAndPygmentsEnabledInAttrNoCodehilite(self):
+ self.assertMarkdownRenders(
+ self.dedent(
+ '''
+ ``` { .python #foo use_pygments=True }
+ # Some python code
+ ```
+ '''
+ ),
+ self.dedent(
+ '''
+ <pre id="foo"><code class="language-python"># Some python code
+ </code></pre>
+ '''
+ ),
+ extensions=['fenced_code']
+ )
+
+ def testFencedLanguageNoCodehiliteWithAttrList(self):
+ self.assertMarkdownRenders(
+ self.dedent(
+ '''
+ ``` { .python foo=bar }
+ # Some python code
+ ```
+ '''
+ ),
+ self.dedent(
+ '''
+ <pre><code class="language-python" foo="bar"># Some python code
+ </code></pre>
+ '''
+ ),
+ extensions=['fenced_code', 'attr_list']
+ )
+
+ def testFencedLanguagePygmentsDisabledInAttrNoCodehiliteWithAttrList(self):
+ self.assertMarkdownRenders(
+ self.dedent(
+ '''
+ ``` { .python foo=bar use_pygments=False }
+ # Some python code
+ ```
+ '''
+ ),
+ self.dedent(
+ '''
+ <pre><code class="language-python" foo="bar"># Some python code
+ </code></pre>
+ '''
+ ),
+ extensions=['fenced_code', 'attr_list']
+ )
+
+ def testFencedLanguagePygmentsEnabledInAttrNoCodehiliteWithAttrList(self):
+ self.assertMarkdownRenders(
+ self.dedent(
+ '''
+ ``` { .python foo=bar use_pygments=True }
+ # Some python code
+ ```
+ '''
+ ),
+ self.dedent(
+ '''
+ <pre><code class="language-python"># Some python code
+ </code></pre>
+ '''
+ ),
+ extensions=['fenced_code', 'attr_list']
+ )
+
+ def testFencedLanguageNoPrefix(self):
+ self.assertMarkdownRenders(
+ self.dedent(
+ '''
+ ``` python
+ # Some python code
+ ```
+ '''
+ ),
+ self.dedent(
+ '''
+ <pre><code class="python"># Some python code
+ </code></pre>
+ '''
+ ),
+ extensions=[markdown.extensions.fenced_code.FencedCodeExtension(lang_prefix='')]
+ )
+
+ def testFencedLanguageAltPrefix(self):
+ self.assertMarkdownRenders(
+ self.dedent(
+ '''
+ ``` python
+ # Some python code
+ ```
+ '''
+ ),
+ self.dedent(
+ '''
+ <pre><code class="lang-python"># Some python code
+ </code></pre>
+ '''
+ ),
+ extensions=[markdown.extensions.fenced_code.FencedCodeExtension(lang_prefix='lang-')]
+ )
+
+
+class TestFencedCodeWithCodehilite(TestCase):
+
+ def setUp(self):
+ if has_pygments and pygments.__version__ != required_pygments_version:
+ self.skipTest(f'Pygments=={required_pygments_version} is required')
+
def testFencedCodeWithHighlightLines(self):
- if self.has_pygments:
+ if has_pygments:
expected = self.dedent(
'''
<div class="codehilite"><pre><span></span><code><span class="hll">line 1
)
def testFencedLanguageAndHighlightLines(self):
- if self.has_pygments:
+ if has_pygments:
expected = (
- '<div class="python codehilite"><pre><span></span><code>'
+ '<div class="codehilite"><pre><span></span><code>'
'<span class="hll"><span class="n">line</span> <span class="mi">1</span>\n'
'</span><span class="n">line</span> <span class="mi">2</span>\n'
'<span class="hll"><span class="n">line</span> <span class="mi">3</span>\n'
else:
expected = self.dedent(
'''
- <pre class="python codehilite"><code class="language-python">line 1
+ <pre class="codehilite"><code class="language-python">line 1
line 2
line 3
</code></pre>
)
def testFencedLanguageDoubleEscape(self):
- if self.has_pygments:
+ if has_pygments:
expected = (
- '<div class="html codehilite"><pre><span></span><code>'
+ '<div class="codehilite"><pre><span></span><code>'
'<span class="p"><</span><span class="nt">span</span>'
'<span class="p">></span>This<span class="ni">&amp;</span>'
'That<span class="p"></</span><span class="nt">span</span>'
)
else:
expected = (
- '<pre class="html codehilite"><code class="language-html">'
+ '<pre class="codehilite"><code class="language-html">'
'<span>This&amp;That</span>\n'
'</code></pre>'
)
)
def testFencedAmps(self):
- if self.has_pygments:
+ if has_pygments:
expected = self.dedent(
'''
- <div class="text codehilite"><pre><span></span><code>&
+ <div class="codehilite"><pre><span></span><code>&
&amp;
&amp;amp;
</code></pre></div>
else:
expected = self.dedent(
'''
- <pre class="text codehilite"><code class="language-text">&
+ <pre class="codehilite"><code class="language-text">&
&amp;
&amp;amp;
</code></pre>
]
)
- def test_fenced_code_in_raw_html(self):
- self.assertMarkdownRenders(
- self.dedent(
- """
- <details>
- ```
- Begone placeholders!
- ```
- </details>
- """
- ),
- self.dedent(
- """
- <details>
-
- <pre><code>Begone placeholders!
- </code></pre>
-
- </details>
- """
- ),
- extensions=['fenced_code']
- )
-
- def testFencedLanguageInAttr(self):
- self.assertMarkdownRenders(
- self.dedent(
- '''
- ``` {.python}
- # Some python code
- ```
- '''
- ),
- self.dedent(
- '''
- <pre><code class="language-python"># Some python code
- </code></pre>
- '''
- ),
- extensions=['fenced_code']
- )
-
- def testFencedMultipleClassesInAttr(self):
- self.assertMarkdownRenders(
- self.dedent(
- '''
- ``` {.python .foo .bar}
- # Some python code
- ```
- '''
- ),
- self.dedent(
- '''
- <pre><code class="language-python foo bar"># Some python code
- </code></pre>
- '''
- ),
- extensions=['fenced_code']
- )
-
- def testFencedIdInAttr(self):
- self.assertMarkdownRenders(
- self.dedent(
- '''
- ``` { #foo }
- # Some python code
- ```
- '''
- ),
- self.dedent(
- '''
- <pre id="foo"><code># Some python code
- </code></pre>
- '''
- ),
- extensions=['fenced_code']
- )
-
- def testFencedIdAndLangInAttr(self):
- self.assertMarkdownRenders(
- self.dedent(
- '''
- ``` { .python #foo }
- # Some python code
- ```
- '''
- ),
- self.dedent(
- '''
- <pre id="foo"><code class="language-python"># Some python code
- </code></pre>
- '''
- ),
- extensions=['fenced_code']
- )
-
- def testFencedIdAndLangAndClassInAttr(self):
- self.assertMarkdownRenders(
- self.dedent(
- '''
- ``` { .python #foo .bar }
- # Some python code
- ```
- '''
- ),
- self.dedent(
- '''
- <pre id="foo"><code class="language-python bar"># Some python code
- </code></pre>
- '''
- ),
- extensions=['fenced_code']
- )
-
def testFencedCodeWithHighlightLinesInAttr(self):
- if self.has_pygments:
+ if has_pygments:
expected = self.dedent(
'''
<div class="codehilite"><pre><span></span><code><span class="hll">line 1
)
def testFencedLanguageAndHighlightLinesInAttr(self):
- if self.has_pygments:
+ if has_pygments:
expected = (
- '<div class="python codehilite"><pre><span></span><code>'
+ '<div class="codehilite"><pre><span></span><code>'
'<span class="hll"><span class="n">line</span> <span class="mi">1</span>\n'
'</span><span class="n">line</span> <span class="mi">2</span>\n'
'<span class="hll"><span class="n">line</span> <span class="mi">3</span>\n'
else:
expected = self.dedent(
'''
- <pre class="python codehilite"><code class="language-python">line 1
+ <pre class="codehilite"><code class="language-python">line 1
line 2
line 3
</code></pre>
extensions=['codehilite', 'fenced_code']
)
- def testFencedLanguageIdAndPygmentsDisabledInAttrNoCodehilite(self):
- self.assertMarkdownRenders(
- self.dedent(
- '''
- ``` { .python #foo use_pygments=False }
- # Some python code
- ```
- '''
- ),
- self.dedent(
- '''
- <pre id="foo"><code class="language-python"># Some python code
- </code></pre>
- '''
- ),
- extensions=['fenced_code']
- )
-
- def testFencedLanguageIdAndPygmentsEnabledInAttrNoCodehilite(self):
- self.assertMarkdownRenders(
- self.dedent(
- '''
- ``` { .python #foo use_pygments=True }
- # Some python code
- ```
- '''
- ),
- self.dedent(
- '''
- <pre id="foo"><code class="language-python"># Some python code
- </code></pre>
- '''
- ),
- extensions=['fenced_code']
- )
-
def testFencedLanguageAttrCssclass(self):
- if self.has_pygments:
+ if has_pygments:
expected = self.dedent(
'''
- <div class="python pygments"><pre><span></span><code><span class="c1"># Some python code</span>
+ <div class="pygments"><pre><span></span><code><span class="c1"># Some python code</span>
</code></pre></div>
'''
)
else:
expected = (
- '<pre class="python pygments"><code class="language-python"># Some python code\n'
+ '<pre class="pygments"><code class="language-python"># Some python code\n'
'</code></pre>'
)
self.assertMarkdownRenders(
)
def testFencedLanguageAttrLinenums(self):
- if self.has_pygments:
+ if has_pygments:
expected = (
- '<table class="python codehilitetable"><tr>'
+ '<table class="codehilitetable"><tr>'
'<td class="linenos"><div class="linenodiv"><pre>1</pre></div></td>'
- '<td class="code"><div class="python codehilite"><pre><span></span>'
+ '<td class="code"><div class="codehilite"><pre><span></span>'
'<code><span class="c1"># Some python code</span>\n'
'</code></pre></div>\n'
'</td></tr></table>'
)
else:
expected = (
- '<pre class="python codehilite"><code class="language-python linenums"># Some python code\n'
+ '<pre class="codehilite"><code class="language-python linenums"># Some python code\n'
'</code></pre>'
)
self.assertMarkdownRenders(
)
def testFencedLanguageAttrGuesslang(self):
- if self.has_pygments:
+ if has_pygments:
expected = self.dedent(
'''
<div class="codehilite"><pre><span></span><code># Some python code
)
def testFencedLanguageAttrNoclasses(self):
- if self.has_pygments:
+ if has_pygments:
expected = (
- '<div class="python codehilite" style="background: #f8f8f8">'
- '<pre style="line-height: 125%"><span></span><code>'
+ '<div class="codehilite" style="background: #f8f8f8">'
+ '<pre style="line-height: 125%; margin: 0;"><span></span><code>'
'<span style="color: #408080; font-style: italic"># Some python code</span>\n'
'</code></pre></div>'
)
else:
expected = (
- '<pre class="python codehilite"><code class="language-python"># Some python code\n'
+ '<pre class="codehilite"><code class="language-python"># Some python code\n'
'</code></pre>'
)
self.assertMarkdownRenders(
expected,
extensions=['codehilite', 'fenced_code']
)
-
- def testFencedLanguageNoCodehiliteWithAttrList(self):
- self.assertMarkdownRenders(
- self.dedent(
- '''
- ``` { .python foo=bar }
- # Some python code
- ```
- '''
- ),
- self.dedent(
- '''
- <pre><code class="language-python" foo="bar"># Some python code
- </code></pre>
- '''
- ),
- extensions=['fenced_code', 'attr_list']
- )
-
- def testFencedLanguagePygmentsDisabledInAttrNoCodehiliteWithAttrList(self):
- self.assertMarkdownRenders(
- self.dedent(
- '''
- ``` { .python foo=bar use_pygments=False }
- # Some python code
- ```
- '''
- ),
- self.dedent(
- '''
- <pre><code class="language-python" foo="bar"># Some python code
- </code></pre>
- '''
- ),
- extensions=['fenced_code', 'attr_list']
- )
-
- def testFencedLanguagePygmentsEnabledInAttrNoCodehiliteWithAttrList(self):
- self.assertMarkdownRenders(
- self.dedent(
- '''
- ``` { .python foo=bar use_pygments=True }
- # Some python code
- ```
- '''
- ),
- self.dedent(
- '''
- <pre><code class="language-python"># Some python code
- </code></pre>
- '''
- ),
- extensions=['fenced_code', 'attr_list']
- )
-
- def testFencedLanguageNoPrefix(self):
- self.assertMarkdownRenders(
- self.dedent(
- '''
- ``` python
- # Some python code
- ```
- '''
- ),
- self.dedent(
- '''
- <pre><code class="python"># Some python code
- </code></pre>
- '''
- ),
- extensions=[markdown.extensions.fenced_code.FencedCodeExtension(lang_prefix='')]
- )
-
- def testFencedLanguageAltPrefix(self):
- self.assertMarkdownRenders(
- self.dedent(
- '''
- ``` python
- # Some python code
- ```
- '''
- ),
- self.dedent(
- '''
- <pre><code class="lang-python"># Some python code
- </code></pre>
- '''
- ),
- extensions=[markdown.extensions.fenced_code.FencedCodeExtension(lang_prefix='lang-')]
- )
[testenv:pygments]
# Run tests with pygments installed (override deps only).
+setenv =
+ PYGMENTS_VERSION = 2.7.1
deps =
pytidylib
- pygments==2.5.2
+ pygments=={env:PYGMENTS_VERSION}
[testenv:flake8]
deps = flake8