"""
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-2019 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
from markdown.extensions.codehilite import CodeHiliteExtension, CodeHilite
import os
try:
import pygments # noqa
has_pygments = True
except ImportError:
has_pygments = False
# The version required by the tests is the version specified and installed in the `pygments` tox environment.
# In any environment where the `PYGMENTS_VERSION` environment variable 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):
"""
Test that source code block results in the expected output with given options.
"""
output = CodeHilite(source, **options).hilite()
self.assertMultiLineEqual(output.strip(), expected)
def test_codehilite_defaults(self):
if has_pygments:
# Odd result as no `lang` given and a single comment is not enough for guessing.
expected = (
'
` is added to end of code block when `lang` explicitly set.
# Compare with expected output for `test_guess_lang`. Not sure why this happens.
expected = (
'<?php '
'print("Hello World"'
'); ?>\n'
'
'
)
else:
expected = (
'<?php print("Hello World"); ?>\n'
'
'
)
self.assertOutputEquals('', expected, lang='php')
def test_codehilite_bad_lang(self):
if has_pygments:
expected = (
'<?php '
'print('
'"Hello World"); ?>\n'
'
'
)
else:
# Note that without Pygments there is no way to check that the language name is bad.
expected = (
''
'<?php print("Hello World"); ?>\n'
'
'
)
# The starting `', expected, lang='unkown')
def test_codehilite_use_pygments_false(self):
expected = (
'<?php print("Hello World"); ?>\n'
'
'
)
self.assertOutputEquals('', expected, lang='php', use_pygments=False)
def test_codehilite_lang_prefix_empty(self):
expected = (
'<?php print("Hello World"); ?>\n'
'
'
)
self.assertOutputEquals(
'', expected, lang='php', use_pygments=False, lang_prefix=''
)
def test_codehilite_lang_prefix(self):
expected = (
'<?php print("Hello World"); ?>\n'
'
'
)
self.assertOutputEquals(
'', expected, lang='php', use_pygments=False, lang_prefix='lang-'
)
def test_codehilite_linenos_true(self):
if has_pygments:
expected = (
''
)
else:
expected = (
'plain text\n'
'
'
)
self.assertOutputEquals('plain text', expected, lang='text', linenos=True)
def test_codehilite_linenos_false(self):
if has_pygments:
expected = (
''
)
else:
expected = (
'plain text\n'
'
'
)
self.assertOutputEquals('plain text', expected, lang='text', linenos=False)
def test_codehilite_linenos_none(self):
if has_pygments:
expected = (
''
)
else:
expected = (
'plain text\n'
'
'
)
self.assertOutputEquals('plain text', expected, lang='text', linenos=None)
def test_codehilite_linenos_table(self):
if has_pygments:
expected = (
''
)
else:
expected = (
'plain text\n'
'
'
)
self.assertOutputEquals('plain text', expected, lang='text', linenos='table')
def test_codehilite_linenos_inline(self):
if has_pygments:
expected = (
''
)
else:
expected = (
'plain text\n'
'
'
)
self.assertOutputEquals('plain text', expected, lang='text', linenos='inline')
def test_codehilite_linenums_true(self):
if has_pygments:
expected = (
''
)
else:
expected = (
'plain text\n'
'
'
)
self.assertOutputEquals('plain text', expected, lang='text', linenums=True)
def test_codehilite_set_cssclass(self):
if has_pygments:
expected = (
''
)
else:
expected = (
'plain text\n'
'
'
)
self.assertOutputEquals('plain text', expected, lang='text', cssclass='override')
def test_codehilite_set_css_class(self):
if has_pygments:
expected = (
''
)
else:
expected = (
'plain text\n'
'
'
)
self.assertOutputEquals('plain text', expected, lang='text', css_class='override')
def test_codehilite_linenostart(self):
if has_pygments:
expected = (
''
)
else:
# TODO: Implement `linenostart` for no-Pygments. Will need to check what JavaScript libraries look for.
expected = (
'plain text\n'
'
'
)
self.assertOutputEquals('plain text', expected, lang='text', linenos='inline', linenostart=42)
def test_codehilite_linenos_hl_lines(self):
if has_pygments:
expected = (
''
'1line 1\n'
'2line 2\n'
'3line 3\n'
'
'
)
else:
expected = (
'line 1\n'
'line 2\n'
'line 3\n'
'
'
)
self.assertOutputEquals('line 1\nline 2\nline 3', expected, lang='text', linenos='inline', hl_lines=[1, 3])
def test_codehilite_linenos_linenostep(self):
if has_pygments:
expected = (
' line 1\n'
'2line 2\n'
' line 3\n'
'
'
)
else:
expected = (
'line 1\n'
'line 2\n'
'line 3\n'
'
'
)
self.assertOutputEquals('line 1\nline 2\nline 3', expected, lang='text', linenos='inline', linenostep=2)
def test_codehilite_linenos_linenospecial(self):
if has_pygments:
expected = (
'1line 1\n'
'2line 2\n'
'3line 3\n'
'
'
)
else:
expected = (
'line 1\n'
'line 2\n'
'line 3\n'
'
'
)
self.assertOutputEquals('line 1\nline 2\nline 3', expected, lang='text', linenos='inline', linenospecial=2)
def test_codehilite_startinline(self):
if has_pygments:
expected = (
'print('
'"Hello World");\n'
'
'
)
else:
expected = (
'print("Hello World");\n'
'
'
)
self.assertOutputEquals('print("Hello World");', expected, lang='php', startinline=True)
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')
# Define a custom Pygments formatter (same example in the documentation)
if has_pygments:
class CustomAddLangHtmlFormatter(pygments.formatters.HtmlFormatter):
def __init__(self, lang_str='', **options):
super().__init__(**options)
self.lang_str = lang_str
def _wrap_code(self, source):
yield 0, f''
yield from source
yield 0, '
'
else:
CustomAddLangHtmlFormatter = None
self.custom_pygments_formatter = CustomAddLangHtmlFormatter
maxDiff = None
def testBasicCodeHilite(self):
if has_pygments:
# Odd result as no `lang` given and a single comment is not enough for guessing.
expected = (
''
)
else:
expected = (
'# A Code Comment\n'
'
'
)
self.assertMarkdownRenders(
'\t# A Code Comment',
expected,
extensions=['codehilite']
)
def testLinenumsTrue(self):
if has_pygments:
expected = (
''
)
else:
expected = (
'# A Code Comment\n'
'
'
)
self.assertMarkdownRenders(
'\t# A Code Comment',
expected,
extensions=[CodeHiliteExtension(linenums=True)]
)
def testLinenumsFalse(self):
if has_pygments:
expected = (
''
)
else:
expected = (
'# A Code Comment\n'
'
'
)
self.assertMarkdownRenders(
(
'\t#!Python\n'
'\t# A Code Comment'
),
expected,
extensions=[CodeHiliteExtension(linenums=False)]
)
def testLinenumsNone(self):
if has_pygments:
expected = (
''
)
else:
expected = (
'# A Code Comment\n'
'
'
)
self.assertMarkdownRenders(
'\t# A Code Comment',
expected,
extensions=[CodeHiliteExtension(linenums=None)]
)
def testLinenumsNoneWithShebang(self):
if has_pygments:
expected = (
''
)
else:
expected = (
'# A Code Comment\n'
'
'
)
self.assertMarkdownRenders(
(
'\t#!Python\n'
'\t# A Code Comment'
),
expected,
extensions=[CodeHiliteExtension(linenums=None)]
)
def testLinenumsNoneWithColon(self):
if has_pygments:
expected = (
''
)
else:
expected = (
'# A Code Comment\n'
'
'
)
self.assertMarkdownRenders(
(
'\t:::Python\n'
'\t# A Code Comment'
),
expected,
extensions=[CodeHiliteExtension(linenums=None)]
)
def testHighlightLinesWithColon(self):
if has_pygments:
expected = (
'#line 1\n'
'#line 2\n'
'#line 3\n'
'
'
)
else:
expected = (
'#line 1\n'
'#line 2\n'
'#line 3\n'
'
'
)
# Double quotes
self.assertMarkdownRenders(
(
'\t:::Python hl_lines="1"\n'
'\t#line 1\n'
'\t#line 2\n'
'\t#line 3'
),
expected,
extensions=['codehilite']
)
# Single quotes
self.assertMarkdownRenders(
(
"\t:::Python hl_lines='1'\n"
'\t#line 1\n'
'\t#line 2\n'
'\t#line 3'
),
expected,
extensions=['codehilite']
)
def testUsePygmentsFalse(self):
self.assertMarkdownRenders(
(
'\t:::Python\n'
'\t# A Code Comment'
),
(
'# A Code Comment\n'
'
'
),
extensions=[CodeHiliteExtension(use_pygments=False)]
)
def testLangPrefixEmpty(self):
self.assertMarkdownRenders(
(
'\t:::Python\n'
'\t# A Code Comment'
),
(
'# A Code Comment\n'
'
'
),
extensions=[CodeHiliteExtension(use_pygments=False, lang_prefix='')]
)
def testLangPrefix(self):
self.assertMarkdownRenders(
(
'\t:::Python\n'
'\t# A Code Comment'
),
(
'# A Code Comment\n'
'
'
),
extensions=[CodeHiliteExtension(use_pygments=False, lang_prefix='lang-')]
)
def testDoubleEscape(self):
if has_pygments:
expected = (
''
''
'<span>'
'This&That'
'</span>'
'\n
'
)
else:
expected = (
''
'<span>This&That</span>\n'
'
'
)
self.assertMarkdownRenders(
(
'\t:::html\n'
'\tThis&That'
),
expected,
extensions=['codehilite']
)
def testEntitiesIntact(self):
if has_pygments:
expected = (
''
''
'< < and > >'
'\n
'
)
else:
expected = (
''
'< < and > >\n'
'
'
)
self.assertMarkdownRenders(
(
'\t:::text\n'
'\t< < and > >'
),
expected,
extensions=['codehilite']
)
def testHighlightAmps(self):
if has_pygments:
expected = (
'&\n'
'&\n'
'&amp;\n'
'
'
)
else:
expected = (
'&\n'
'&\n'
'&amp;\n'
'
'
)
self.assertMarkdownRenders(
(
'\t:::text\n'
'\t&\n'
'\t&\n'
'\t&'
),
expected,
extensions=['codehilite']
)
def testUnknownOption(self):
if has_pygments:
# Odd result as no `lang` given and a single comment is not enough for guessing.
expected = (
''
)
else:
expected = (
'# A Code Comment\n'
'
'
)
self.assertMarkdownRenders(
'\t# A Code Comment',
expected,
extensions=[CodeHiliteExtension(unknown='some value')],
)
def testMultipleBlocksSameStyle(self):
if has_pygments:
# See also: https://github.com/Python-Markdown/markdown/issues/1240
expected = (
''
'# First Code Block\n'
'
\n\n'
'Normal paragraph
\n'
''
'# Second Code Block\n'
'
'
)
else:
expected = (
'# First Code Block\n'
'
\n\n'
'Normal paragraph
\n'
'# Second Code Block\n'
'
'
)
self.assertMarkdownRenders(
(
'\t:::Python\n'
'\t# First Code Block\n\n'
'Normal paragraph\n\n'
'\t:::Python\n'
'\t# Second Code Block'
),
expected,
extensions=[CodeHiliteExtension(pygments_style="native", noclasses=True)]
)
def testFormatterLangStr(self):
if has_pygments:
expected = (
''
)
else:
expected = (
'# A Code Comment\n'
'
'
)
self.assertMarkdownRenders(
'\t:::Python\n'
'\t# A Code Comment',
expected,
extensions=[
CodeHiliteExtension(
guess_lang=False,
pygments_formatter=self.custom_pygments_formatter
)
]
)
def testFormatterLangStrGuessLang(self):
if has_pygments:
expected = (
''
'<?php '
'print('
'"Hello World"'
'); ?>\n'
'
'
)
else:
expected = (
'<?php print("Hello World"); ?>\n'
'
'
)
# Use PHP as the the starting `',
expected,
extensions=[CodeHiliteExtension(pygments_formatter=self.custom_pygments_formatter)]
)
def testFormatterLangStrEmptyLang(self):
if has_pygments:
expected = (
''
)
else:
expected = (
'# A Code Comment\n'
'
'
)
self.assertMarkdownRenders(
'\t# A Code Comment',
expected,
extensions=[
CodeHiliteExtension(
guess_lang=False,
pygments_formatter=self.custom_pygments_formatter,
)
]
)