Spaces:
Sleeping
Sleeping
| # 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-2023 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). | |
| """ A collection of tools for testing the Markdown code base and extensions. """ | |
| from __future__ import annotations | |
| import os | |
| import sys | |
| import unittest | |
| import textwrap | |
| from typing import Any | |
| from . import markdown, Markdown, util | |
| try: | |
| import tidylib | |
| except ImportError: | |
| tidylib = None | |
| __all__ = ['TestCase', 'LegacyTestCase', 'Kwargs'] | |
| class TestCase(unittest.TestCase): | |
| """ | |
| A [`unittest.TestCase`][] subclass with helpers for testing Markdown output. | |
| Define `default_kwargs` as a `dict` of keywords to pass to Markdown for each | |
| test. The defaults can be overridden on individual tests. | |
| The `assertMarkdownRenders` method accepts the source text, the expected | |
| output, and any keywords to pass to Markdown. The `default_kwargs` are used | |
| except where overridden by `kwargs`. 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 `dedent` method is available to dedent triple-quoted strings if | |
| necessary. | |
| In all other respects, behaves as `unittest.TestCase`. | |
| """ | |
| default_kwargs: dict[str, Any] = {} | |
| """ Default options to pass to Markdown for each test. """ | |
| 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) | |
| 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): | |
| """ | |
| Dedent text. | |
| """ | |
| # TODO: If/when actual output ends with a newline, then use: | |
| # return textwrap.dedent(text.strip('/n')) | |
| return textwrap.dedent(text).strip() | |
| class recursionlimit: | |
| """ | |
| A context manager which temporarily modifies the Python recursion limit. | |
| The testing framework, coverage, etc. may add an arbitrary number of levels to the depth. To maintain consistency | |
| in the tests, the current stack depth is determined when called, then added to the provided limit. | |
| Example usage: | |
| ``` python | |
| with recursionlimit(20): | |
| # test code here | |
| ``` | |
| See <https://stackoverflow.com/a/50120316/866026>. | |
| """ | |
| def __init__(self, limit): | |
| self.limit = util._get_stack_depth() + limit | |
| self.old_limit = sys.getrecursionlimit() | |
| def __enter__(self): | |
| sys.setrecursionlimit(self.limit) | |
| def __exit__(self, type, value, tb): | |
| sys.setrecursionlimit(self.old_limit) | |
| ######################### | |
| # Legacy Test Framework # | |
| ######################### | |
| class Kwargs(dict): | |
| """ A `dict` like class for holding keyword arguments. """ | |
| pass | |
| def _normalize_whitespace(text): | |
| """ Normalize whitespace for a string of HTML using `tidylib`. """ | |
| output, errors = tidylib.tidy_fragment(text, options={ | |
| 'drop_empty_paras': 0, | |
| 'fix_backslash': 0, | |
| 'fix_bad_comments': 0, | |
| 'fix_uri': 0, | |
| 'join_styles': 0, | |
| 'lower_literals': 0, | |
| 'merge_divs': 0, | |
| 'output_xhtml': 1, | |
| 'quote_ampersand': 0, | |
| 'newline': 'LF' | |
| }) | |
| return output | |
| class LegacyTestMeta(type): | |
| def __new__(cls, name, bases, dct): | |
| def generate_test(infile, outfile, normalize, kwargs): | |
| def test(self): | |
| with open(infile, encoding="utf-8") as f: | |
| input = f.read() | |
| with open(outfile, encoding="utf-8") as f: | |
| # Normalize line endings | |
| # (on Windows, git may have altered line endings). | |
| expected = f.read().replace("\r\n", "\n") | |
| output = markdown(input, **kwargs) | |
| if tidylib and normalize: | |
| 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) | |
| return test | |
| location = dct.get('location', '') | |
| exclude = dct.get('exclude', []) | |
| normalize = dct.get('normalize', False) | |
| input_ext = dct.get('input_ext', '.txt') | |
| output_ext = dct.get('output_ext', '.html') | |
| kwargs = dct.get('default_kwargs', Kwargs()) | |
| if os.path.isdir(location): | |
| for file in os.listdir(location): | |
| infile = os.path.join(location, file) | |
| if os.path.isfile(infile): | |
| tname, ext = os.path.splitext(file) | |
| if ext == input_ext: | |
| outfile = os.path.join(location, tname + output_ext) | |
| tname = tname.replace(' ', '_').replace('-', '_') | |
| kws = kwargs.copy() | |
| if tname in dct: | |
| kws.update(dct[tname]) | |
| test_name = 'test_%s' % tname | |
| if tname not in exclude: | |
| dct[test_name] = generate_test(infile, outfile, normalize, kws) | |
| else: | |
| dct[test_name] = unittest.skip('Excluded')(lambda: None) | |
| return type.__new__(cls, name, bases, dct) | |
| class LegacyTestCase(unittest.TestCase, metaclass=LegacyTestMeta): | |
| """ | |
| A [`unittest.TestCase`][] subclass for running Markdown's legacy file-based tests. | |
| A subclass should define various properties which point to a directory of | |
| text-based test files and define various behaviors/defaults for those tests. | |
| The following properties are supported: | |
| Attributes: | |
| location (str): A path to the directory of test files. An absolute path is preferred. | |
| exclude (list[str]): A list of tests to exclude. Each test name should comprise the filename | |
| without an extension. | |
| normalize (bool): A boolean value indicating if the HTML should be normalized. Default: `False`. | |
| input_ext (str): A string containing the file extension of input files. Default: `.txt`. | |
| output_ext (str): A string containing the file extension of expected output files. Default: `html`. | |
| default_kwargs (Kwargs[str, Any]): The default set of keyword arguments for all test files in the directory. | |
| In addition, properties can be defined for each individual set of test files within | |
| the directory. The property should be given the name of the file without the file | |
| extension. Any spaces and dashes in the filename should be replaced with | |
| underscores. The value of the property should be a `Kwargs` instance which | |
| contains the keyword arguments that should be passed to `Markdown` for that | |
| test file. The keyword arguments will "update" the `default_kwargs`. | |
| When the class instance is created, it will walk the given directory and create | |
| a separate `Unitttest` for each set of test files using the naming scheme: | |
| `test_filename`. One `Unittest` will be run for each set of input and output files. | |
| """ | |
| pass | |