diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index af4770e..b3b7aeb 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -2,7 +2,7 @@ # See https://pre-commit.com/hooks.html for more hooks repos: - repo: https://github.com/pre-commit/pre-commit-hooks - rev: 'v4.5.0' + rev: 'v4.6.0' hooks: - id: trailing-whitespace - id: end-of-file-fixer @@ -49,7 +49,7 @@ repos: additional_dependencies: ['flake8-docstrings'] - repo: https://github.com/PyCQA/bandit - rev: '1.7.7' + rev: '1.7.8' hooks: - id: bandit args: ['-c', 'pyproject.toml', '--level', 'LOW'] @@ -61,7 +61,7 @@ repos: - id: isort - repo: https://codeberg.org/frnmst/md-toc - rev: '8.2.3' + rev: '9.0.0' hooks: - id: md-toc args: ['-p', 'cmark', '-l6'] diff --git a/md_toc/api.py b/md_toc/api.py index a4f13ad..162f832 100644 --- a/md_toc/api.py +++ b/md_toc/api.py @@ -156,7 +156,7 @@ def write_strings_on_files_between_markers( :rtype: bool :raises: an fpyutils exception or a built-in exception. """ - if not len(filenames) == len(strings): + if len(filenames) != len(strings): raise ValueError file_id: int = 0 @@ -264,7 +264,7 @@ def build_toc( # Help the developers: override the list_marker in case # this function is called with the default unordered list marker, # for example like this: - # print(md_toc.build_toc('test.md', ordered=True)) + # print(md_toc.api.build_toc('test.md', ordered=True)) # This avoids an AssertionError later on. if (ordered and list_marker == md_parser[parser]['list']['unordered']['default_marker']): @@ -282,10 +282,17 @@ def build_toc( loop: bool = True line_counter = 1 while loop: - if line_counter > skip_lines or f.readline() == '': - loop = False - line_counter += 1 + try: + if line_counter > skip_lines or f.readline() == '': + loop = False + line_counter += 1 + except UnicodeDecodeError: + return ''.join([ + '' + ]) + # Read next line after possible skips. try: line = f.readline() except UnicodeDecodeError: @@ -294,9 +301,9 @@ def build_toc( indentation_log: dict[types.IndentationLogElement] = init_indentation_log( parser, list_marker) - is_within_code_fence = False + is_within_code_fence: bool = False code_fence = None - is_document_end = False + is_document_end: bool = False while line: # Document ending detection. # @@ -532,9 +539,7 @@ def increase_index_ordered_list( :raises: GithubOverflowOrderedListMarker or a built-in exception. """ # header_type_prev might be 0 while header_type_curr can't. - if not header_type_prev >= 0: - raise ValueError - if not header_type_curr >= 1: + if not (header_type_prev >= 0 or header_type_curr >= 1): raise ValueError # Base cases for a new table of contents or a new index type. @@ -624,29 +629,24 @@ def compute_toc_line_indentation_spaces( .. warning:: In case of ordered TOCs you must explicitly pass one of the supported ordered list markers. """ - if not header_type_curr >= 1: - raise ValueError - if not header_type_prev >= 0: - raise ValueError - if parser in ['github', 'cmark', 'gitlab', 'commonmarker', 'goldmark']: - if not len(indentation_log - ) == md_parser['github']['header']['max_levels']: + if (header_type_curr >= 1 and header_type_prev >= 0 and index >= 1): + if (parser + in ['github', 'cmark', 'gitlab', 'commonmarker', 'goldmark'] + and len(indentation_log) != + md_parser[parser]['header']['max_levels']): raise ValueError - if not index >= 1: - raise ValueError - - if parser in [ - 'github', 'cmark', 'gitlab', 'commonmarker', 'goldmark', - 'redcarpet' - ]: - if ordered: - if list_marker not in md_parser[parser]['list']['ordered'][ - 'closing_markers']: + if (parser in [ + 'github', 'cmark', 'gitlab', 'commonmarker', 'goldmark', + 'redcarpet' + ]): + if ordered and list_marker not in md_parser[parser]['list'][ + 'ordered']['closing_markers']: raise ValueError - else: - if list_marker not in md_parser[parser]['list']['unordered'][ - 'bullet_markers']: + elif not ordered and list_marker not in md_parser[parser]['list'][ + 'unordered']['bullet_markers']: raise ValueError + else: + raise ValueError if parser in ['github', 'cmark', 'gitlab', 'commonmarker', 'goldmark']: index_length: int @@ -799,8 +799,7 @@ def build_toc_line( if not no_of_indentation_spaces >= 0: raise ValueError - indentation: str = no_of_indentation_spaces * ' ' - return ''.join([indentation, toc_line_no_indent]) + return ''.join([no_of_indentation_spaces * ' ', toc_line_no_indent]) def remove_html_tags(line: str, parser: str = 'github') -> str: @@ -850,10 +849,7 @@ def remove_emphasis(line: str, parser: str = 'github') -> str: >>> md_toc.api.remove_emphasis('__my string__ *is this* one') 'my string is this one' """ - if parser in [ - 'github', 'cmark', 'gitlab', 'commonmarker', 'goldmark', - 'redcarpet' - ]: + if parser in ['github', 'cmark', 'gitlab', 'commonmarker', 'goldmark']: mem = None refmap = references_h._cmarkCmarkReferenceMap() @@ -1064,8 +1060,6 @@ def build_anchor_link( return header_text_trimmed_middle_stage_str - return None - def replace_and_split_newlines(line: str) -> list[str]: r"""Replace all the newline characters with line feeds and separate the components. @@ -1434,8 +1428,6 @@ def is_valid_code_fence_indent(line: str, parser: str = 'github') -> bool: # TODO. return False - return False - def is_opening_code_fence(line: str, parser: str = 'github') -> str | None: r"""Determine if the given line is possibly the opening of a fenced code block. @@ -1510,8 +1502,6 @@ def is_opening_code_fence(line: str, parser: str = 'github') -> str | None: # TODO. return None - return None - def is_closing_code_fence( line: str, @@ -1551,20 +1541,19 @@ def is_closing_code_fence( False """ if parser in ['github', 'cmark', 'gitlab', 'commonmarker', 'goldmark']: - markers = md_parser['github']['code_fence']['marker'] - marker_min_length = md_parser['github']['code_fence'][ - 'min_marker_characters'] - if not is_valid_code_fence_indent(line): return False # Remove opening fence indentation after it is known to be valid. fence = fence.lstrip(' ') # Check if fence uses valid characters. - if not fence.startswith((markers['backtick'], markers['tilde'])): + if not fence.startswith( + (md_parser[parser]['code_fence']['marker']['backtick'], + md_parser[parser]['code_fence']['marker']['tilde'])): return False - if len(fence) < marker_min_length: + if len(fence + ) < md_parser[parser]['code_fence']['min_marker_characters']: return False # Additional security. @@ -1618,8 +1607,6 @@ def is_closing_code_fence( # TODO. return False - return False - if __name__ == '__main__': pass diff --git a/md_toc/tests/tests.py b/md_toc/tests/tests.py index 70d6eee..3c52c72 100644 --- a/md_toc/tests/tests.py +++ b/md_toc/tests/tests.py @@ -1,7 +1,7 @@ # # tests.py # -# Copyright (C) 2017-2023 Franco Masotti (see /README.md) +# Copyright (C) 2017-2024 Franco Masotti (see /README.md) # # This file is part of md-toc. # @@ -842,13 +842,31 @@ def test_write_string_on_file_between_markers(self): def test_write_strings_on_files_between_markers(self): r"""Test that the TOC is written correctly on the files.""" - @unittest.skip('empty test') def test_build_toc(self): r"""Test that the TOC is built correctly. - TODO: tests will be needed eventually because the complexity of - this function is growing. + .. note:: Specifically test edge cases. """ + # Simple. + with open('foo.md', 'w') as f: + f.write('# This\n# Is an\n## Example\n') + self.assertEqual( + api.build_toc('foo.md'), + '- [This](#this)\n- [Is an](#is-an)\n - [Example](#example)\n') + + with open('foo.md', 'w') as f: + f.write('# This\n# Is an\n## Example\n') + self.assertEqual(api.build_toc('foo.md', skip_lines=2), + '- [Example](#example)\n') + + with open('foo.md', 'w') as f: + f.write('# This\n') + with open('foo.md', 'ab') as f: + f.write(b'\xff') + with open('foo.md', 'a') as f: + f.write('# Is an\n## Example\n') + self.assertEqual(api.build_toc('foo.md', skip_lines=1), + '') @unittest.skip('empty test') def test_build_multiple_tocs(self): @@ -1348,6 +1366,10 @@ def test_remove_emphasis(self): .. note: not all tests are enabled because of a missing implementation and possible bugs. """ + # Test not-emphasis. + self.assertEqual(api.remove_emphasis(''), '') + self.assertEqual(api.remove_emphasis(CMARK_LINE_FOO), CMARK_LINE_FOO) + # Example 331 [Commonmark 0.28]. # Example 350 [Commonmark 0.29]. # Example 350 [Commonmark 0.30]. @@ -2122,7 +2144,8 @@ def test_remove_emphasis(self): '[link](/my uri)') self.assertEqual(api.remove_emphasis('[link](*/my uri)*'), '[link](/my uri)') - # self.assertEqual(api.remove_emphasis('_[link_](/my uri)'), '[link](/my uri)') + self.assertEqual(api.remove_emphasis('_[link_](/my uri)'), + '[link](/my uri)') self.assertEqual(api.remove_emphasis('[link](_/my uri)_'), '[link](/my uri)') @@ -2141,7 +2164,8 @@ def test_remove_emphasis(self): '[link](foo\nbar)') self.assertEqual(api.remove_emphasis('[link](*foo\nbar)*'), '[link](foo\nbar)') - # self.assertEqual(api.remove_emphasis('_[link_](foo\nbar)'), '[link](foo\nbar)') + self.assertEqual(api.remove_emphasis('_[link_](foo\nbar)'), + '[link](foo\nbar)') self.assertEqual(api.remove_emphasis('[link](_foo\nbar)_'), '[link](foo\nbar)') @@ -2150,7 +2174,8 @@ def test_remove_emphasis(self): '[link]()') self.assertEqual(api.remove_emphasis('[link](*)*'), '[link]()') - # self.assertEqual(api.remove_emphasis('_[link_]()'), '[link]()') + self.assertEqual(api.remove_emphasis('_[link_]()'), + '[link]()') self.assertEqual(api.remove_emphasis('[link](_)_'), '[link]()') @@ -2209,7 +2234,8 @@ def test_remove_emphasis(self): '[link](foo(and(bar))') self.assertEqual(api.remove_emphasis('[link](*foo(and(bar))*'), '[link](foo(and(bar))') - # self.assertEqual(api.remove_emphasis('_[link_](foo(and(bar))'), '[link](foo(and(bar))') + self.assertEqual(api.remove_emphasis('_[link_](foo(and(bar))'), + '[link](foo(and(bar))') self.assertEqual(api.remove_emphasis('[link](_foo(and(bar))_'), '[link](foo(and(bar))') @@ -2310,10 +2336,8 @@ def test_remove_emphasis(self): # Example 504 [Commonmark 0.30]. self.assertEqual(api.remove_emphasis(r'*[link*](/url "title")'), r'*[link*](/url "title")') - self.assertEqual(api.remove_emphasis(r"*[link*](/url 'title')"), r"*[link*](/url 'title')") - # self.assertEqual(api.remove_emphasis(r'*[link*](/url (title))'), r'*[link*](/url (title))') # Example 505 [Commonmark 0.30]. @@ -2342,7 +2366,8 @@ def test_remove_emphasis(self): r'[link] (uri)') self.assertEqual(api.remove_emphasis(r'[link] (*uri)*'), r'[link] (uri)') - # self.assertEqual(api.remove_emphasis(r'_[link_] (uri)'), r'[link] (uri)') + self.assertEqual(api.remove_emphasis(r'_[link_] (uri)'), + r'[link] (uri)') self.assertEqual(api.remove_emphasis(r'[link] (_uri)_'), r'[link] (uri)') @@ -2415,6 +2440,11 @@ def test_remove_emphasis(self): self.assertEqual(api.remove_emphasis(r'_j\_'), '_j' + LINE_ESCAPE + '_') + # redcarpet. + self.assertEqual(api.remove_emphasis('', parser='redcarpet'), '') + self.assertEqual(api.remove_emphasis(r'__a__', parser='redcarpet'), + r'__a__') + def test_remove_html_tags(self): r"""Test remove html tags.""" # Example 584 [Commonmark 0.28]. @@ -2851,6 +2881,22 @@ def test_build_anchor_link(self): header_duplicate_counter, parser='gitlab'), LINE_DASH + CMARK_LINE_FOO) + # redcarpet. + header_duplicate_counter = dict() + self.assertEqual( + api.build_anchor_link(REDCARPET_LINE_FOO, + header_duplicate_counter, + parser='redcarpet'), + REDCARPET_LINE_FOO, + ) + header_duplicate_counter = dict() + self.assertEqual( + api.build_anchor_link(REDCARPET_LINE_FOO + S1 + REDCARPET_LINE_FOO, + header_duplicate_counter, + parser='redcarpet'), + REDCARPET_LINE_FOO + LINE_DASH + REDCARPET_LINE_FOO, + ) + def test_replace_and_split_newlines(self): r"""Test the replacement and splitting of newlines in a string.""" self.assertEqual( @@ -2895,13 +2941,13 @@ def test_get_atx_heading(self): in the test directory of the source code. """ # github - m_github = md_parser['github']['header']['max levels'] = 6 - # Example 32 [Commonmark 0.28]. # Example 32 [Commonmark 0.29]. # Example 62 [Commonmark 0.30]. self.assertEqual( - api.get_atx_heading(H1 + S1 + CMARK_LINE_FOO, m_github, 'github'), + api.get_atx_heading(H1 + S1 + CMARK_LINE_FOO, + md_parser['github']['header']['max_levels'], + 'github'), [{ 'header_type': 1, 'header_text_trimmed': CMARK_LINE_FOO, @@ -2909,7 +2955,9 @@ def test_get_atx_heading(self): }], ) self.assertEqual( - api.get_atx_heading(H1 + T1 + CMARK_LINE_FOO, m_github, 'github'), + api.get_atx_heading(H1 + T1 + CMARK_LINE_FOO, + md_parser['github']['header']['max_levels'], + 'github'), [{ 'header_type': None, 'header_text_trimmed': None, @@ -2917,7 +2965,9 @@ def test_get_atx_heading(self): }], ) self.assertEqual( - api.get_atx_heading(H1 + T1 + CMARK_LINE_FOO, m_github, 'cmark'), + api.get_atx_heading(H1 + T1 + CMARK_LINE_FOO, + md_parser['github']['header']['max_levels'], + 'cmark'), [{ 'header_type': 1, 'header_text_trimmed': CMARK_LINE_FOO, @@ -2926,7 +2976,9 @@ def test_get_atx_heading(self): ) self.assertEqual( - api.get_atx_heading(H2 + S1 + CMARK_LINE_FOO, m_github, 'github'), + api.get_atx_heading(H2 + S1 + CMARK_LINE_FOO, + md_parser['github']['header']['max_levels'], + 'github'), [{ 'header_type': 2, 'header_text_trimmed': CMARK_LINE_FOO, @@ -2934,7 +2986,9 @@ def test_get_atx_heading(self): }], ) self.assertEqual( - api.get_atx_heading(H2 + T1 + CMARK_LINE_FOO, m_github, 'github'), + api.get_atx_heading(H2 + T1 + CMARK_LINE_FOO, + md_parser['github']['header']['max_levels'], + 'github'), [{ 'header_type': None, 'header_text_trimmed': None, @@ -2942,7 +2996,9 @@ def test_get_atx_heading(self): }], ) self.assertEqual( - api.get_atx_heading(H2 + T1 + CMARK_LINE_FOO, m_github, 'cmark'), + api.get_atx_heading(H2 + T1 + CMARK_LINE_FOO, + md_parser['github']['header']['max_levels'], + 'cmark'), [{ 'header_type': 2, 'header_text_trimmed': CMARK_LINE_FOO, @@ -2951,7 +3007,9 @@ def test_get_atx_heading(self): ) self.assertEqual( - api.get_atx_heading(H3 + S1 + CMARK_LINE_FOO, m_github, 'github'), + api.get_atx_heading(H3 + S1 + CMARK_LINE_FOO, + md_parser['github']['header']['max_levels'], + 'github'), [{ 'header_type': 3, 'header_text_trimmed': CMARK_LINE_FOO, @@ -2959,7 +3017,9 @@ def test_get_atx_heading(self): }], ) self.assertEqual( - api.get_atx_heading(H3 + T1 + CMARK_LINE_FOO, m_github, 'github'), + api.get_atx_heading(H3 + T1 + CMARK_LINE_FOO, + md_parser['github']['header']['max_levels'], + 'github'), [{ 'header_type': None, 'header_text_trimmed': None, @@ -2967,7 +3027,9 @@ def test_get_atx_heading(self): }], ) self.assertEqual( - api.get_atx_heading(H3 + T1 + CMARK_LINE_FOO, m_github, 'cmark'), + api.get_atx_heading(H3 + T1 + CMARK_LINE_FOO, + md_parser['github']['header']['max_levels'], + 'cmark'), [{ 'header_type': 3, 'header_text_trimmed': CMARK_LINE_FOO, @@ -2976,7 +3038,9 @@ def test_get_atx_heading(self): ) self.assertEqual( - api.get_atx_heading(H4 + S1 + CMARK_LINE_FOO, m_github, 'github'), + api.get_atx_heading(H4 + S1 + CMARK_LINE_FOO, + md_parser['github']['header']['max_levels'], + 'github'), [{ 'header_type': 4, 'header_text_trimmed': CMARK_LINE_FOO, @@ -2984,7 +3048,9 @@ def test_get_atx_heading(self): }], ) self.assertEqual( - api.get_atx_heading(H4 + T1 + CMARK_LINE_FOO, m_github, 'github'), + api.get_atx_heading(H4 + T1 + CMARK_LINE_FOO, + md_parser['github']['header']['max_levels'], + 'github'), [{ 'header_type': None, 'header_text_trimmed': None, @@ -2992,7 +3058,9 @@ def test_get_atx_heading(self): }], ) self.assertEqual( - api.get_atx_heading(H4 + T1 + CMARK_LINE_FOO, m_github, 'cmark'), + api.get_atx_heading(H4 + T1 + CMARK_LINE_FOO, + md_parser['github']['header']['max_levels'], + 'cmark'), [{ 'header_type': 4, 'header_text_trimmed': CMARK_LINE_FOO, @@ -3001,7 +3069,9 @@ def test_get_atx_heading(self): ) self.assertEqual( - api.get_atx_heading(H5 + S1 + CMARK_LINE_FOO, m_github, 'github'), + api.get_atx_heading(H5 + S1 + CMARK_LINE_FOO, + md_parser['github']['header']['max_levels'], + 'github'), [{ 'header_type': 5, 'header_text_trimmed': CMARK_LINE_FOO, @@ -3009,7 +3079,9 @@ def test_get_atx_heading(self): }], ) self.assertEqual( - api.get_atx_heading(H5 + T1 + CMARK_LINE_FOO, m_github, 'github'), + api.get_atx_heading(H5 + T1 + CMARK_LINE_FOO, + md_parser['github']['header']['max_levels'], + 'github'), [{ 'header_type': None, 'header_text_trimmed': None, @@ -3017,7 +3089,9 @@ def test_get_atx_heading(self): }], ) self.assertEqual( - api.get_atx_heading(H5 + T1 + CMARK_LINE_FOO, m_github, 'cmark'), + api.get_atx_heading(H5 + T1 + CMARK_LINE_FOO, + md_parser['github']['header']['max_levels'], + 'cmark'), [{ 'header_type': 5, 'header_text_trimmed': CMARK_LINE_FOO, @@ -3026,7 +3100,9 @@ def test_get_atx_heading(self): ) self.assertEqual( - api.get_atx_heading(H6 + S1 + CMARK_LINE_FOO, m_github, 'github'), + api.get_atx_heading(H6 + S1 + CMARK_LINE_FOO, + md_parser['github']['header']['max_levels'], + 'github'), [{ 'header_type': 6, 'header_text_trimmed': CMARK_LINE_FOO, @@ -3034,7 +3110,9 @@ def test_get_atx_heading(self): }], ) self.assertEqual( - api.get_atx_heading(H6 + T1 + CMARK_LINE_FOO, m_github, 'github'), + api.get_atx_heading(H6 + T1 + CMARK_LINE_FOO, + md_parser['github']['header']['max_levels'], + 'github'), [{ 'header_type': None, 'header_text_trimmed': None, @@ -3042,7 +3120,9 @@ def test_get_atx_heading(self): }], ) self.assertEqual( - api.get_atx_heading(H6 + T1 + CMARK_LINE_FOO, m_github, 'cmark'), + api.get_atx_heading(H6 + T1 + CMARK_LINE_FOO, + md_parser['github']['header']['max_levels'], + 'cmark'), [{ 'header_type': 6, 'header_text_trimmed': CMARK_LINE_FOO, @@ -3068,7 +3148,8 @@ def test_get_atx_heading(self): self.assertEqual( api.get_atx_heading( H1 + CMARK_LINE_5_BOLT + LINE_LINE_FEED + LINE_LINE_FEED + H1 + - CMARK_LINE_HASHTAG, m_github, 'github'), + CMARK_LINE_HASHTAG, + md_parser['github']['header']['max_levels'], 'github'), [{ 'header_type': None, 'header_text_trimmed': None, @@ -3089,7 +3170,7 @@ def test_get_atx_heading(self): self.assertEqual( api.get_atx_heading( LINE_ESCAPE + H1 + S1 + CMARK_LINE_FOO, - m_github, + md_parser['github']['header']['max_levels'], 'github', ), [{ @@ -3103,7 +3184,7 @@ def test_get_atx_heading(self): self.assertEqual( api.get_atx_heading( LINE_ESCAPE + H2 + S1 + CMARK_LINE_FOO, - m_github, + md_parser['github']['header']['max_levels'], 'github', ), [{ @@ -3119,7 +3200,7 @@ def test_get_atx_heading(self): api.get_atx_heading( H1 + S1 + CMARK_LINE_FOO + S1 + '*' + CMARK_LINE_BAR + '*' + S1 + LINE_ESCAPE + '*' + CMARK_LINE_BAZ + LINE_ESCAPE + '*', - m_github, + md_parser['github']['header']['max_levels'], 'github', ), [{ @@ -3155,7 +3236,7 @@ def test_get_atx_heading(self): self.assertEqual( api.get_atx_heading( H1 + S18 + CMARK_LINE_FOO + S21, - m_github, + md_parser['github']['header']['max_levels'], 'github', ), [{ @@ -3167,7 +3248,7 @@ def test_get_atx_heading(self): self.assertEqual( api.get_atx_heading( H1 + T18 + CMARK_LINE_FOO + T21, - m_github, + md_parser['github']['header']['max_levels'], 'github', ), [{ @@ -3179,7 +3260,7 @@ def test_get_atx_heading(self): self.assertEqual( api.get_atx_heading( H1 + T18 + CMARK_LINE_FOO + T21, - m_github, + md_parser['github']['header']['max_levels'], 'cmark', ), [{ @@ -3195,7 +3276,7 @@ def test_get_atx_heading(self): self.assertEqual( api.get_atx_heading( S1 + H3 + S1 + CMARK_LINE_FOO, - m_github, + md_parser['github']['header']['max_levels'], 'github', ), [{ @@ -3207,7 +3288,7 @@ def test_get_atx_heading(self): self.assertEqual( api.get_atx_heading( S2 + H2 + S1 + CMARK_LINE_FOO, - m_github, + md_parser['github']['header']['max_levels'], 'github', ), [{ @@ -3219,7 +3300,7 @@ def test_get_atx_heading(self): self.assertEqual( api.get_atx_heading( S3 + H1 + S1 + CMARK_LINE_FOO, - m_github, + md_parser['github']['header']['max_levels'], 'github', ), [{ @@ -3235,7 +3316,7 @@ def test_get_atx_heading(self): self.assertEqual( api.get_atx_heading( S4 + H1 + S1 + CMARK_LINE_FOO, - m_github, + md_parser['github']['header']['max_levels'], 'github', ), [{ @@ -3252,7 +3333,7 @@ def test_get_atx_heading(self): api.get_atx_heading( CMARK_LINE_FOO + LINE_LINE_FEED + S4 + H1 + S1 + CMARK_LINE_BAR, - m_github, + md_parser['github']['header']['max_levels'], 'github', ), [{ @@ -3272,7 +3353,7 @@ def test_get_atx_heading(self): self.assertEqual( api.get_atx_heading( H2 + S1 + CMARK_LINE_FOO + S1 + H2, - m_github, + md_parser['github']['header']['max_levels'], 'github', ), [{ @@ -3284,7 +3365,7 @@ def test_get_atx_heading(self): self.assertEqual( api.get_atx_heading( S2 + H3 + S3 + CMARK_LINE_BAR + S4 + H3, - m_github, + md_parser['github']['header']['max_levels'], 'github', ), [{ @@ -3300,7 +3381,7 @@ def test_get_atx_heading(self): self.assertEqual( api.get_atx_heading( H1 + S1 + CMARK_LINE_FOO + S1 + H34, - m_github, + md_parser['github']['header']['max_levels'], 'github', ), [{ @@ -3313,7 +3394,7 @@ def test_get_atx_heading(self): self.assertEqual( api.get_atx_heading( H5 + S1 + CMARK_LINE_FOO + S1 + H2, - m_github, + md_parser['github']['header']['max_levels'], 'github', ), [{ @@ -3325,7 +3406,9 @@ def test_get_atx_heading(self): # Extra test. self.assertEqual( - api.get_atx_heading(H5 * 7, m_github, 'github'), + api.get_atx_heading(H5 * 7, + md_parser['github']['header']['max_levels'], + 'github'), [{ 'header_type': None, 'header_text_trimmed': None, @@ -3339,7 +3422,7 @@ def test_get_atx_heading(self): self.assertEqual( api.get_atx_heading( H3 + S1 + CMARK_LINE_FOO + S1 + H3 + S5, - m_github, + md_parser['github']['header']['max_levels'], 'github', ), [{ @@ -3351,7 +3434,7 @@ def test_get_atx_heading(self): self.assertEqual( api.get_atx_heading( H3 + S1 + CMARK_LINE_FOO + S1 + H3 + T5, - m_github, + md_parser['github']['header']['max_levels'], 'github', ), [{ @@ -3363,7 +3446,7 @@ def test_get_atx_heading(self): self.assertEqual( api.get_atx_heading( H3 + S1 + CMARK_LINE_FOO + S1 + H3 + T5, - m_github, + md_parser['github']['header']['max_levels'], 'cmark', ), [{ @@ -3379,7 +3462,7 @@ def test_get_atx_heading(self): self.assertEqual( api.get_atx_heading( H3 + S1 + CMARK_LINE_FOO + S1 + H3 + S1 + CMARK_LINE_B, - m_github, + md_parser['github']['header']['max_levels'], 'github', ), [{ @@ -3396,7 +3479,7 @@ def test_get_atx_heading(self): self.assertEqual( api.get_atx_heading( H1 + S1 + CMARK_LINE_FOO + H1, - m_github, + md_parser['github']['header']['max_levels'], 'github', ), [{ @@ -3408,7 +3491,7 @@ def test_get_atx_heading(self): self.assertEqual( api.get_atx_heading( H1 + S1 + CMARK_LINE_FOO + S1 + H1, - m_github, + md_parser['github']['header']['max_levels'], 'github', ), [{ @@ -3420,7 +3503,7 @@ def test_get_atx_heading(self): self.assertEqual( api.get_atx_heading( H1 + S1 + CMARK_LINE_FOO + T1 + H1, - m_github, + md_parser['github']['header']['max_levels'], 'github', ), [{ @@ -3432,7 +3515,7 @@ def test_get_atx_heading(self): self.assertEqual( api.get_atx_heading( H1 + S1 + CMARK_LINE_FOO + T1 + H1, - m_github, + md_parser['github']['header']['max_levels'], 'cmark', ), [{ @@ -3456,7 +3539,7 @@ def test_get_atx_heading(self): self.assertEqual( api.get_atx_heading( H3 + S1 + CMARK_LINE_FOO + S1 + LINE_ESCAPE + H3, - m_github, + md_parser['github']['header']['max_levels'], 'github', ), [{ @@ -3468,7 +3551,7 @@ def test_get_atx_heading(self): self.assertEqual( api.get_atx_heading( H2 + S1 + CMARK_LINE_FOO + S1 + H1 + LINE_ESCAPE + H2, - m_github, + md_parser['github']['header']['max_levels'], 'github', ), [{ @@ -3481,7 +3564,7 @@ def test_get_atx_heading(self): self.assertEqual( api.get_atx_heading( H1 + S1 + CMARK_LINE_FOO + S1 + LINE_ESCAPE + H1, - m_github, + md_parser['github']['header']['max_levels'], 'github', ), [{ @@ -3498,7 +3581,7 @@ def test_get_atx_heading(self): api.get_atx_heading( '****' + LINE_LINE_FEED + H2 + S1 + CMARK_LINE_FOO + LINE_LINE_FEED + '****', - m_github, + md_parser['github']['header']['max_levels'], 'github', ), [{ @@ -3523,7 +3606,7 @@ def test_get_atx_heading(self): api.get_atx_heading( 'Foo' + S1 + CMARK_LINE_BAR + LINE_LINE_FEED + H1 + S1 + CMARK_LINE_BAZ + LINE_LINE_FEED + 'Bar' + S1 + CMARK_LINE_FOO, - m_github, + md_parser['github']['header']['max_levels'], 'github', ), [{ @@ -3545,7 +3628,9 @@ def test_get_atx_heading(self): # Example 49 [Commonmark 0.29]. # Example 79 [Commonmark 0.30]. self.assertEqual( - api.get_atx_heading(H2 + S1, m_github, 'github', True), + api.get_atx_heading(H2 + S1, + md_parser['github']['header']['max_levels'], + 'github', True), [{ 'header_type': 2, 'header_text_trimmed': LINE_EMPTY, @@ -3553,7 +3638,9 @@ def test_get_atx_heading(self): }], ) self.assertEqual( - api.get_atx_heading(H1, m_github, 'github', True), + api.get_atx_heading(H1, + md_parser['github']['header']['max_levels'], + 'github', True), [{ 'header_type': 1, 'header_text_trimmed': LINE_EMPTY, @@ -3561,7 +3648,9 @@ def test_get_atx_heading(self): }], ) self.assertEqual( - api.get_atx_heading(H3 + S1 + H3, m_github, 'github', True), + api.get_atx_heading(H3 + S1 + H3, + md_parser['github']['header']['max_levels'], + 'github', True), [{ 'header_type': 3, 'header_text_trimmed': LINE_EMPTY, @@ -3574,11 +3663,17 @@ def test_get_atx_heading(self): # Example 49 [Commonmark 0.29]. # Example 79 [Commonmark 0.30]. with self.assertRaises(exceptions.GithubEmptyLinkLabel): - api.get_atx_heading(H2 + S1, m_github, 'github', False) + api.get_atx_heading(H2 + S1, + md_parser['github']['header']['max_levels'], + 'github', False) with self.assertRaises(exceptions.GithubEmptyLinkLabel): - api.get_atx_heading(H1, m_github, 'github', False) + api.get_atx_heading(H1, + md_parser['github']['header']['max_levels'], + 'github', False) with self.assertRaises(exceptions.GithubEmptyLinkLabel): - api.get_atx_heading(H3 + S1 + H3, m_github, 'github', False) + api.get_atx_heading(H3 + S1 + H3, + md_parser['github']['header']['max_levels'], + 'github', False) # Test escaping examples reported in the documentation. # A modified Example 302 and Example 496 (for square brackets). @@ -3587,7 +3682,7 @@ def test_get_atx_heading(self): H1 + S1 + LINE_SQUARE_BRACKET_OPEN + S1 + CMARK_LINE_FOO + S1 + LINE_SQUARE_BRACKET_OPEN + CMARK_LINE_BAR + LINE_SQUARE_BRACKET_CLOSE + LINE_SQUARE_BRACKET_CLOSE, - m_github, + md_parser['github']['header']['max_levels'], 'github', ), [{ @@ -3606,7 +3701,7 @@ def test_get_atx_heading(self): api.get_atx_heading( H1 + S1 + LINE_ESCAPE + LINE_ESCAPE + LINE_SQUARE_BRACKET_OPEN + S1 + CMARK_LINE_FOO, - m_github, + md_parser['github']['header']['max_levels'], 'github', ), [{ @@ -3624,7 +3719,7 @@ def test_get_atx_heading(self): self.assertEqual( api.get_atx_heading( H2 + S1 + CMARK_LINE_FOO + LINE_ESCAPE, - m_github, + md_parser['github']['header']['max_levels'], 'github', ), [{ @@ -3638,13 +3733,15 @@ def test_get_atx_heading(self): with self.assertRaises(exceptions.GithubOverflowCharsLinkLabel): api.get_atx_heading( H1 + S1 + CMARK_LINE_1000_CHARS, - m_github, + md_parser['github']['header']['max_levels'], 'github', ) # Test an empty line. self.assertEqual( - api.get_atx_heading(LINE_EMPTY, m_github, 'github'), + api.get_atx_heading(LINE_EMPTY, + md_parser['github']['header']['max_levels'], + 'github'), [{ 'header_type': None, 'header_text_trimmed': None, @@ -3657,7 +3754,7 @@ def test_get_atx_heading(self): api.get_atx_heading( H1 + S1 + CMARK_LINE_FOO + LINE_LINE_FEED + LINE_LINE_FEED + H1 + S1 + CMARK_LINE_FOO + LINE_LINE_FEED, - m_github, + md_parser['github']['header']['max_levels'], 'github', ), [ @@ -3681,7 +3778,9 @@ def test_get_atx_heading(self): # Test line endings. self.assertEqual( - api.get_atx_heading(H1 + LINE_LINE_FEED, m_github, 'github', True), + api.get_atx_heading(H1 + LINE_LINE_FEED, + md_parser['github']['header']['max_levels'], + 'github', True), [{ 'header_type': 1, 'header_text_trimmed': LINE_EMPTY, @@ -3691,7 +3790,7 @@ def test_get_atx_heading(self): self.assertEqual( api.get_atx_heading( H1 + LINE_CARRIAGE_RETURN, - m_github, + md_parser['github']['header']['max_levels'], 'github', True, ), @@ -3706,7 +3805,7 @@ def test_get_atx_heading(self): self.assertEqual( api.get_atx_heading( H1 + S1 + CMARK_LINE_FOO + LINE_LINE_FEED + CMARK_LINE_FOO, - m_github, + md_parser['github']['header']['max_levels'], 'github', ), [{ @@ -3724,7 +3823,7 @@ def test_get_atx_heading(self): api.get_atx_heading( H1 + S1 + CMARK_LINE_FOO + LINE_CARRIAGE_RETURN + CMARK_LINE_FOO, - m_github, + md_parser['github']['header']['max_levels'], 'github', ), [{ @@ -3740,11 +3839,13 @@ def test_get_atx_heading(self): # Test line endings with link labels. with self.assertRaises(exceptions.GithubEmptyLinkLabel): - api.get_atx_heading(H1 + LINE_LINE_FEED, m_github, 'github', False) + api.get_atx_heading(H1 + LINE_LINE_FEED, + md_parser['github']['header']['max_levels'], + 'github', False) with self.assertRaises(exceptions.GithubEmptyLinkLabel): api.get_atx_heading( H1 + LINE_CARRIAGE_RETURN, - m_github, + md_parser['github']['header']['max_levels'], 'github', False, ) @@ -4358,8 +4459,39 @@ def test_is_opening_code_fence(self): # Example 147 [Commonmark 0.30]. # See test_is_closing_code_fence. + # redcarpet + self.assertIsNone( + api.is_opening_code_fence(BACKTICK3, parser='redcarpet')) + self.assertIsNone( + api.is_opening_code_fence(BACKTICK4, parser='redcarpet')) + self.assertIsNone( + api.is_opening_code_fence(BACKTICK10, parser='redcarpet')) + self.assertIsNone(api.is_opening_code_fence(TILDE3, + parser='redcarpet')) + self.assertIsNone(api.is_opening_code_fence(TILDE4, + parser='redcarpet')) + self.assertIsNone( + api.is_opening_code_fence(TILDE10, parser='redcarpet')) + def test_is_closing_code_fence(self): r"""Test detection of closing code fence.""" + # Test non-closing code fences. + self.assertFalse( + api.is_closing_code_fence(CMARK_LINE_FOO, CMARK_LINE_FOO)) + self.assertFalse(api.is_closing_code_fence(BACKTICK3, CMARK_LINE_FOO)) + self.assertFalse(api.is_closing_code_fence(TILDE3, CMARK_LINE_FOO)) + self.assertFalse(api.is_closing_code_fence(BACKTICK2, BACKTICK3)) + self.assertFalse(api.is_closing_code_fence(TILDE2, TILDE3)) + + # len(fence) < marker_min_length + self.assertFalse(api.is_closing_code_fence(BACKTICK3, BACKTICK2)) + self.assertFalse(api.is_closing_code_fence(TILDE3, TILDE2)) + + # fence != len(fence) * fence[0] + self.assertFalse( + api.is_closing_code_fence(BACKTICK3, BACKTICK3 + LINE_DASH)) + self.assertFalse(api.is_closing_code_fence(TILDE3, TILDE3 + LINE_DASH)) + # github. # Example 91 [Commonmark 0.28]. # Example 92 [Commonmark 0.29]. @@ -4478,6 +4610,13 @@ def test_is_closing_code_fence(self): api.is_closing_code_fence(TILDE3 + T1, TILDE3, 'cmark'), ) self.assertFalse(api.is_closing_code_fence(TILDE3 + T4 + S4, TILDE3), ) + # redcarpet + self.assertFalse( + api.is_closing_code_fence(BACKTICK3, BACKTICK3, + parser='redcarpet')) + self.assertFalse( + api.is_closing_code_fence(TILDE3, TILDE3, parser='redcarpet')) + if __name__ == '__main__': unittest.main() diff --git a/requirements-dev.txt b/requirements-dev.txt index 5e7c5bb..66c116d 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -20,6 +20,7 @@ asciinema>=2,<3 build>=1.0,<1.1 +coverage>=7.4,<8 pre-commit>=3,<4 pyfakefs>=5,<6 sphinx-book-theme>=1.0,<1.1 diff --git a/requirements-freeze.txt b/requirements-freeze.txt index a8b88c3..fe08923 100644 --- a/requirements-freeze.txt +++ b/requirements-freeze.txt @@ -2,6 +2,7 @@ accessible-pygments==0.0.4 alabaster==0.7.16 asciinema==2.4.0 Babel==2.14.0 +backports.tarfile==1.0.0 beautifulsoup4==4.12.3 build==1.0.3 cachetools==5.3.3 @@ -11,35 +12,36 @@ cfgv==3.4.0 chardet==5.2.0 charset-normalizer==3.3.2 colorama==0.4.6 +coverage==7.4.4 cryptography==42.0.5 distlib==0.3.8 docutils==0.19 -filelock==3.13.2 +filelock==3.13.4 fpyutils==4.0.1 identify==2.5.35 -idna==3.6 +idna==3.7 imagesize==1.4.1 importlib_metadata==7.1.0 -jaraco.classes==3.3.1 -jaraco.context==4.3.0 +jaraco.classes==3.4.0 +jaraco.context==5.3.0 jaraco.functools==4.0.0 jeepney==0.8.0 Jinja2==3.1.3 -keyring==25.0.0 +keyring==25.1.0 markdown-it-py==3.0.0 MarkupSafe==2.1.5 mdurl==0.1.2 more-itertools==10.2.0 -nh3==0.2.15 +nh3==0.2.17 nodeenv==1.8.0 packaging==24.0 pkginfo==1.10.0 platformdirs==4.2.0 pluggy==1.4.0 pre-commit==3.7.0 -pycparser==2.21 +pycparser==2.22 pydata-sphinx-theme==0.15.2 -pyfakefs==5.3.5 +pyfakefs==5.4.1 Pygments==2.17.2 pyproject-api==1.6.1 pyproject_hooks==1.0.0 @@ -63,7 +65,7 @@ sphinxcontrib-qthelp==1.0.7 sphinxcontrib-serializinghtml==1.1.10 tox==4.14.2 twine==4.0.2 -typing_extensions==4.10.0 +typing_extensions==4.11.0 urllib3==2.2.1 virtualenv==20.25.1 zipp==3.18.1