diff --git a/src/jcloud_docsgen/core/python/_core.py b/src/jcloud_docsgen/core/python/_core.py index 483829a..a8a8025 100644 --- a/src/jcloud_docsgen/core/python/_core.py +++ b/src/jcloud_docsgen/core/python/_core.py @@ -28,7 +28,7 @@ from ...exceptions import ( InvalidNamespaceError, PythonArgumentStructureError, ) -from ...utils import fit_row_length, get_relative_path_segments, non_empty_str +from ...utils import get_relative_path_segments, markdown_table, non_empty_str from .arguments import PythonArgumentKind, PythonFunctionArgument from .definitions import ( PythonAsyncFunctionDefinition, @@ -184,108 +184,6 @@ def _signature_argument_list( return args -def _markdown_table( - header: tuple, - body: list[tuple], - *, - allow_markdown_table: bool = False, - force_row_lengths: bool = False, - one_line_values: bool = True, - missing_value: str = '--', -) -> str: - """ - Generates a table. - - If ``allow_markdown_table`` is ``False``, an alternative - representation is used. - - :param headers: The table headers. - :type headers: tuple - :param body: The table body. - :type body: list[tuple] - :param allow_markdown_table: Controls whether a markdown table may be - used. - :type allow_markdown_table: bool - :param force_row_lengths: Controls whether an exception is raised if - a row does not have the correct length. - :type force_row_lengths: bool - :param one_line_values: Controls whether all newlines in all values - are replaced with spaces. - :type one_line_values: bool - :param missing_value: The string that is used if a value is missing - (only if ``force_row_lengths`` is ``False``). - :type missing_value: str - """ - - if not header or not body: - return '' - - header_len = len(header) - - if allow_markdown_table: - table = ( - '| ' + ' | '.join(header) + ' |\n|' + ' --- |' * len(header) + '\n' - ) - - for row in body: - if len(row) != header_len: - if force_row_lengths: - raise ValueError( - f'expected {header_len} value' - f'{"s" if header_len > 1 else ""}, got {len(row)}' - ) - else: - row = fit_row_length(header_len, row) - - table += ( - '| ' - + ' | '.join( - [ - str(col if col is not None else missing_value) - .replace('\n', ' ') - .replace('|', '\\|') - if one_line_values - else str( - col if col is not None else missing_value - ).replace('|', '\\|') - for col in row - ] - ) - + ' |\n' - ) - else: - table = '' - - for row in body: - if len(row) != header_len: - if force_row_lengths: - raise ValueError( - f'expected {header_len} value' - f'{"s" if header_len > 1 else ""}, got {len(row)}' - ) - else: - row = fit_row_length(header_len, row) - - sep = '- ' - for i, col in enumerate(row): - table += ( - sep - + header[i] - + ': ' - + ( - str(col if col is not None else missing_value).replace( - '\n', ' ' - ) - if one_line_values - else str(col if col is not None else missing_value) - ) - + '\n\n' - ) - sep = ' ' - - return table.strip() - - def _is_docstring(docstring: str | PythonDocstring | None) -> bool: """ Checks whether the docstring is a docstring or an empty docstring. @@ -621,7 +519,7 @@ class PythonDefinitionDocumentationGenerator: ) if params_types: - md += _markdown_table( + md += markdown_table( ['Parameter', 'Type', 'Kind', 'Default value', 'Description'], [ ( @@ -700,7 +598,7 @@ class PythonDefinitionDocumentationGenerator: md += self.level * '#' + '# Exceptions\n\n' if self._docstring.raises: - md += _markdown_table( + md += markdown_table( ('Exception', 'Description'), [ ( diff --git a/src/jcloud_docsgen/utils.py b/src/jcloud_docsgen/utils.py index 4c355b7..d11665e 100644 --- a/src/jcloud_docsgen/utils.py +++ b/src/jcloud_docsgen/utils.py @@ -35,6 +35,7 @@ __all__ = [ 'non_empty_str', 'get_relative_path_segments', 'fit_row_length', + 'markdown_table', ] @@ -260,3 +261,105 @@ def fit_row_length(expected: int, row: tuple) -> tuple: return row[:expected] else: return row + (None,) * (expected - len(row)) + + +def markdown_table( + header: tuple, + body: list[tuple], + *, + allow_markdown_table: bool = False, + force_row_lengths: bool = False, + one_line_values: bool = True, + missing_value: str = '--', +) -> str: + """ + Generates a table. + + If ``allow_markdown_table`` is ``False``, an alternative + representation is used. + + :param headers: The table headers. + :type headers: tuple + :param body: The table body. + :type body: list[tuple] + :param allow_markdown_table: Controls whether a markdown table may be + used. + :type allow_markdown_table: bool + :param force_row_lengths: Controls whether an exception is raised if + a row does not have the correct length. + :type force_row_lengths: bool + :param one_line_values: Controls whether all newlines in all values + are replaced with spaces. + :type one_line_values: bool + :param missing_value: The string that is used if a value is missing + (only if ``force_row_lengths`` is ``False``). + :type missing_value: str + """ + + if not header or not body: + return '' + + header_len = len(header) + + if allow_markdown_table: + table = ( + '| ' + ' | '.join(header) + ' |\n|' + ' --- |' * len(header) + '\n' + ) + + for row in body: + if len(row) != header_len: + if force_row_lengths: + raise ValueError( + f'expected {header_len} value' + f'{"s" if header_len > 1 else ""}, got {len(row)}' + ) + else: + row = fit_row_length(header_len, row) + + table += ( + '| ' + + ' | '.join( + [ + str(col if col is not None else missing_value) + .replace('\n', ' ') + .replace('|', '\\|') + if one_line_values + else str( + col if col is not None else missing_value + ).replace('|', '\\|') + for col in row + ] + ) + + ' |\n' + ) + else: + table = '' + + for row in body: + if len(row) != header_len: + if force_row_lengths: + raise ValueError( + f'expected {header_len} value' + f'{"s" if header_len > 1 else ""}, got {len(row)}' + ) + else: + row = fit_row_length(header_len, row) + + sep = '- ' + for i, col in enumerate(row): + table += ( + sep + + header[i] + + ': ' + + ( + str(col if col is not None else missing_value).replace( + '\n', ' ' + ) + if one_line_values + else str(col if col is not None else missing_value) + ) + + '\n\n' + ) + sep = ' ' + + return table.strip() diff --git a/tests/unit/core/python/_core/test__markdown_table.py b/tests/unit/utils/test_markdown_table.py similarity index 98% rename from tests/unit/core/python/_core/test__markdown_table.py rename to tests/unit/utils/test_markdown_table.py index 4ec8e68..08dddce 100644 --- a/tests/unit/core/python/_core/test__markdown_table.py +++ b/tests/unit/utils/test_markdown_table.py @@ -14,7 +14,7 @@ import pytest -from src.jcloud_docsgen.core.python._core import _markdown_table +from src.jcloud_docsgen.utils import markdown_table @pytest.mark.parametrize( @@ -318,7 +318,7 @@ from src.jcloud_docsgen.core.python._core import _markdown_table ), ], ) -def test__markdown_table( +def test_markdown_table( header, body, allow_markdown_table, @@ -328,7 +328,7 @@ def test__markdown_table( expected, ): assert ( - _markdown_table( + markdown_table( header, body, allow_markdown_table=allow_markdown_table, @@ -468,7 +468,7 @@ def test__markdown_table( ), ], ) -def test__markdown_table_exceptions( +def test_markdown_table_exceptions( header, body, allow_markdown_table, @@ -479,7 +479,7 @@ def test__markdown_table_exceptions( expected_exception_msg, ): with pytest.raises(expected_exception) as exc_info: - _markdown_table( + markdown_table( header, body, allow_markdown_table=allow_markdown_table,